dsh-loop-engine 1.0.0-rc2
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 +151 -0
- package/README.zh.md +93 -0
- package/lib/client.js +403 -0
- package/lib/index.js +4310 -0
- package/lib/invariant.js +83 -0
- package/lib/types/client/LoopEngineBadge.d.ts +34 -0
- package/lib/types/client/LoopEngineSection.d.ts +34 -0
- package/lib/types/client/index.d.ts +28 -0
- package/lib/types/client/locales.d.ts +40 -0
- package/lib/types/client/store.d.ts +45 -0
- package/lib/types/commands.d.ts +32 -0
- package/lib/types/driver-core/ownership.d.ts +41 -0
- package/lib/types/driver-core/permission-knobs.d.ts +26 -0
- package/lib/types/driver-core/prompt.d.ts +23 -0
- package/lib/types/driver-core/skill-inject.d.ts +59 -0
- package/lib/types/engine-claude/agent.d.ts +102 -0
- package/lib/types/engine-claude/loop.d.ts +89 -0
- package/lib/types/engine-claude/mapping.d.ts +83 -0
- package/lib/types/engine-claude/permission.d.ts +41 -0
- package/lib/types/engine-claude/process.d.ts +59 -0
- package/lib/types/engine-claude/sdk.d.ts +57 -0
- package/lib/types/engine-claude/types.d.ts +18 -0
- package/lib/types/engine-codex/agent.d.ts +109 -0
- package/lib/types/engine-codex/appserver/client.d.ts +49 -0
- package/lib/types/engine-codex/appserver/mapping.d.ts +67 -0
- package/lib/types/engine-codex/appserver/thread.d.ts +66 -0
- package/lib/types/engine-codex/appserver/types.d.ts +215 -0
- package/lib/types/engine-codex/loop.d.ts +92 -0
- package/lib/types/engine-codex/permission.d.ts +32 -0
- package/lib/types/engine-codex/skills.d.ts +26 -0
- package/lib/types/engine-codex/types.d.ts +19 -0
- package/lib/types/engine-pi/agent.d.ts +125 -0
- package/lib/types/engine-pi/loop.d.ts +96 -0
- package/lib/types/engine-pi/permission.d.ts +43 -0
- package/lib/types/engine-pi/rpc/client.d.ts +105 -0
- package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
- package/lib/types/engine-pi/rpc/types.d.ts +235 -0
- package/lib/types/engine-pi/skills.d.ts +26 -0
- package/lib/types/engine-pi/types.d.ts +27 -0
- package/lib/types/index.d.ts +96 -0
- package/lib/types/invariant.d.ts +23 -0
- package/lib/types/namespace.d.ts +9 -0
- package/lib/types/patch-manager.d.ts +47 -0
- package/lib/types/settings.d.ts +29 -0
- package/lib/types/skills.d.ts +77 -0
- package/package.json +103 -0
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/settings.ts
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
|
+
var LOOP_ENGINE_IDS = ["in-process", "claude-code", "codex", "pi"];
|
|
5
|
+
var LOOP_ENGINE_SETTINGS_SCHEMA = z.object({
|
|
6
|
+
engine: z.union([z.const("in-process"), z.const("claude-code"), z.const("codex"), z.const("pi")]).default("in-process")
|
|
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
|
+
END_MARKER_LINE
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
23
|
+
var BEGIN_MARKER_RE = /^# -- dsh-loop-engine managed block: (\S+) --$/m;
|
|
24
|
+
function currentEngineOf(text) {
|
|
25
|
+
const engine = BEGIN_MARKER_RE.exec(text)?.[1];
|
|
26
|
+
return LOOP_ENGINE_IDS.includes(engine ?? "") ? engine : "in-process";
|
|
27
|
+
}
|
|
28
|
+
function managedSpan(text) {
|
|
29
|
+
const begin = text.indexOf(MANAGED_BLOCK_BEGIN);
|
|
30
|
+
if (begin === -1) return { head: text, tail: "", present: false, blankBefore: false };
|
|
31
|
+
const afterBegin = begin + MANAGED_BLOCK_BEGIN.length;
|
|
32
|
+
const endAt = text.indexOf(MANAGED_BLOCK_END, afterBegin);
|
|
33
|
+
const spanEnd = endAt === -1 ? text.length : endAt + END_MARKER_LINE.length;
|
|
34
|
+
const before = text.slice(0, begin);
|
|
35
|
+
const blankBefore = before.endsWith("\n\n");
|
|
36
|
+
return {
|
|
37
|
+
head: blankBefore ? before.slice(0, -1) : before,
|
|
38
|
+
tail: text.slice(spanEnd),
|
|
39
|
+
present: true,
|
|
40
|
+
blankBefore
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function ensureTrailingNewline(text) {
|
|
44
|
+
return text.endsWith("\n") ? text : `${text}
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
function applyManagedBlock(text, engine) {
|
|
48
|
+
const block = renderManagedBlock(engine);
|
|
49
|
+
const span = managedSpan(text);
|
|
50
|
+
if (!span.present) {
|
|
51
|
+
if (block === "") return text;
|
|
52
|
+
const base = ensureTrailingNewline(text);
|
|
53
|
+
return `${base}
|
|
54
|
+
${block}`;
|
|
55
|
+
}
|
|
56
|
+
if (block === "") {
|
|
57
|
+
return span.tail.startsWith("\n") ? `${span.head}${span.tail.slice(1)}` : `${span.head}${span.tail}`;
|
|
58
|
+
}
|
|
59
|
+
return `${span.head}${span.blankBefore ? "\n" : ""}${block}${span.tail}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/invariant.ts
|
|
63
|
+
var PACKAGE_NAME = "dsh-loop-engine";
|
|
64
|
+
var name = "loop-engine-invariant";
|
|
65
|
+
var inject = ["invariants"];
|
|
66
|
+
var install = (ctx, fail) => {
|
|
67
|
+
void ctx;
|
|
68
|
+
const seed = "# dsh profile patch layer\n";
|
|
69
|
+
for (const engine of LOOP_ENGINE_IDS) {
|
|
70
|
+
const applied = applyManagedBlock(seed, engine);
|
|
71
|
+
const reborn = applyManagedBlock(applied, currentEngineOf(applied));
|
|
72
|
+
if (reborn !== applied) fail(`managed-block round trip for ${engine} is not a fixed point`);
|
|
73
|
+
if (engine === "in-process" && applied !== seed) fail("in-process engine must leave the file text unchanged");
|
|
74
|
+
if (engine !== "in-process" && currentEngineOf(renderManagedBlock(engine)) !== engine) fail(`${engine} block must read back as the ${engine} engine`);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
78
|
+
export {
|
|
79
|
+
apply,
|
|
80
|
+
inject,
|
|
81
|
+
name
|
|
82
|
+
};
|
|
83
|
+
//# 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-runtime/client';
|
|
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,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-runtime/client';
|
|
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,28 @@
|
|
|
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 { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
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 { LoopEngineState } from './store.ts';
|
|
13
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
14
|
+
interface LocaleNamespaceMap {
|
|
15
|
+
/** The Loop engine settings page copy. */
|
|
16
|
+
'settings.loop-engine': LoopEngineKey;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Required services (cordis fiber inject). The target slot is declared by
|
|
20
|
+
* ui-settings' apply; registration depends on it through `slots.inject()`. */
|
|
21
|
+
export declare const inject: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Register the Loop engine section once the `settings.section` declaration is
|
|
24
|
+
* on the ledger and bind its store to the duplicated settings scope.
|
|
25
|
+
* @param ctx - client root context.
|
|
26
|
+
*/
|
|
27
|
+
export declare function apply(ctx: ClientContext): void;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
/** Unavailable-state message. */
|
|
20
|
+
unavailable: string;
|
|
21
|
+
/** Notice shown when the selection would interrupt running agents. */
|
|
22
|
+
switchNotice: string;
|
|
23
|
+
/** Saving state label. */
|
|
24
|
+
saving: string;
|
|
25
|
+
/** Confirmation dialog title. */
|
|
26
|
+
confirmTitle: string;
|
|
27
|
+
/** Confirmation dialog body. */
|
|
28
|
+
confirmBody: string;
|
|
29
|
+
/** Confirmation action label. */
|
|
30
|
+
confirmAction: string;
|
|
31
|
+
/** Cancel action label. */
|
|
32
|
+
cancelAction: string;
|
|
33
|
+
/** Notice shown while the Claude Code engine owns the slot: model selection is native. */
|
|
34
|
+
claudeModelNotice: string;
|
|
35
|
+
}
|
|
36
|
+
/** Simplified Chinese copy. */
|
|
37
|
+
export declare const zh: Record<keyof LoopEngineKey, string>;
|
|
38
|
+
/** English copy. */
|
|
39
|
+
export declare const en: Record<keyof LoopEngineKey, string>;
|
|
40
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
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, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
7
|
+
import type { LoopEngineId } from '../settings.ts';
|
|
8
|
+
/** State rendered by the loop engine section. */
|
|
9
|
+
export interface LoopEngineState {
|
|
10
|
+
status: 'loading' | 'ready' | 'unavailable' | 'saving';
|
|
11
|
+
engine: LoopEngineId;
|
|
12
|
+
writable: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** Narrow a wire section to the stored engine id; an invalid one reads default. */
|
|
16
|
+
export declare function decodeLoopEngine(section: unknown): {
|
|
17
|
+
engine: LoopEngineId;
|
|
18
|
+
} | undefined;
|
|
19
|
+
/** Coordinates the settings-backed loop engine selection. */
|
|
20
|
+
export declare class LoopEngineStore {
|
|
21
|
+
private readonly scope;
|
|
22
|
+
/** uSES-safe state source shared by the registered settings section. */
|
|
23
|
+
readonly store: SnapshotStore<LoopEngineState>;
|
|
24
|
+
private following;
|
|
25
|
+
private saving;
|
|
26
|
+
/**
|
|
27
|
+
* @param scope - the loop engine settings namespace scope.
|
|
28
|
+
*/
|
|
29
|
+
constructor(scope: SettingsScope<{
|
|
30
|
+
engine: LoopEngineId;
|
|
31
|
+
}>);
|
|
32
|
+
/** Begin following the bound scope and publish its current answer. */
|
|
33
|
+
load(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Persist the selected engine. Success is judged against the snapshot the
|
|
36
|
+
* write left behind, so a refused write reports error after its recovery.
|
|
37
|
+
* @param engine - the engine to select for future Agent turns.
|
|
38
|
+
* @returns whether the write landed.
|
|
39
|
+
*/
|
|
40
|
+
setEngine(engine: LoopEngineId): Promise<boolean>;
|
|
41
|
+
/** Stop following the scope. */
|
|
42
|
+
dispose(): void;
|
|
43
|
+
private derive;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code built-in slash command definitions.
|
|
3
|
+
*
|
|
4
|
+
* These commands are registered into the DSH CommandRuntime when the
|
|
5
|
+
* claude-code engine is active. The handlers are stubs — the real command
|
|
6
|
+
* processing happens inside the Claude Agent SDK — so the UI shows the
|
|
7
|
+
* commands in the slash menu and the handler returns success immediately.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-loop-engine/commands
|
|
10
|
+
*/
|
|
11
|
+
/** Minimal shape of a DSH command definition (avoiding a direct peer dep on @deepseek-ai/dsh-commands). */
|
|
12
|
+
export interface CommandDefinition {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly description: string;
|
|
15
|
+
readonly input?: {
|
|
16
|
+
readonly hint: string;
|
|
17
|
+
readonly images?: boolean;
|
|
18
|
+
};
|
|
19
|
+
readonly handler: (invocation: CommandInvocation) => CommandResult | Promise<CommandResult>;
|
|
20
|
+
}
|
|
21
|
+
export interface CommandInvocation {
|
|
22
|
+
readonly commandId: string;
|
|
23
|
+
readonly rawInput: string;
|
|
24
|
+
readonly signal: AbortSignal;
|
|
25
|
+
}
|
|
26
|
+
export interface CommandResult {
|
|
27
|
+
readonly kind: 'success' | 'error';
|
|
28
|
+
readonly text?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Claude Code's built-in slash commands. */
|
|
31
|
+
export declare const CLAUDE_CODE_COMMANDS: CommandDefinition[];
|
|
32
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
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 { FiberState } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
15
|
+
/** Fiber states that cannot own or serve a new lifecycle. */
|
|
16
|
+
export declare const INACTIVE_STATES: ReadonlySet<FiberState>;
|
|
17
|
+
/** Factory-level ownership: live agent teardowns plus load-time tracking. */
|
|
18
|
+
export declare class FactoryOwnership {
|
|
19
|
+
private readonly fiber;
|
|
20
|
+
private accepting;
|
|
21
|
+
private readonly teardown;
|
|
22
|
+
private readonly inactive;
|
|
23
|
+
private readonly liveAgents;
|
|
24
|
+
private startupTasks;
|
|
25
|
+
constructor(fiber: Context['fiber']);
|
|
26
|
+
/** Aborts (reason: `agent loop is not active` error) when factory teardown begins. */
|
|
27
|
+
get signal(): AbortSignal;
|
|
28
|
+
isActive(): boolean;
|
|
29
|
+
/** Track one live agent's shared teardown until it has run. */
|
|
30
|
+
track(dispose: () => Promise<void>): () => void;
|
|
31
|
+
/** Join config startup work that begins before an agent exists. */
|
|
32
|
+
trackStartup(job: Promise<void>): void;
|
|
33
|
+
/** Join one public create/resume continuation; factory dispose awaits its settlement. */
|
|
34
|
+
trackWrapper(job: Promise<unknown>): void;
|
|
35
|
+
dispose(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/** Await `operation`, or throw the signal's reason as soon as it aborts. */
|
|
38
|
+
export declare function raceAbort<T>(operation: PromiseLike<T> | T, signal: AbortSignal, id: SessionId): Promise<T>;
|
|
39
|
+
/** Start an abortable operation and release a value that arrives after cancellation. */
|
|
40
|
+
export declare function raceAbortCall<T>(operation: () => PromiseLike<T> | T, signal: AbortSignal, id: SessionId, releaseAbandoned?: (value: T) => void): Promise<T>;
|
|
41
|
+
//# 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
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code loop Agent: drives one session through turn and step boundaries
|
|
3
|
+
* with one Claude Agent SDK query per step. Claude Code owns its prompt,
|
|
4
|
+
* tools, and permissions; the durable session log remains the source of truth
|
|
5
|
+
* and the query prompt is a pure serialization of it.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-loop-engine/engine-claude/agent
|
|
8
|
+
*/
|
|
9
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
|
|
10
|
+
import { Inbox } from '@deepseek-ai/dsh-agent';
|
|
11
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
12
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { ResolvedConfig } from './types.ts';
|
|
15
|
+
/** Drives one session through turn and step boundaries on Claude Code. */
|
|
16
|
+
export declare class ClaudeCodeAgent implements Agent {
|
|
17
|
+
private loopCtx;
|
|
18
|
+
readonly id: SessionId;
|
|
19
|
+
readonly options: AgentOptions;
|
|
20
|
+
readonly session: Session;
|
|
21
|
+
private readonly config;
|
|
22
|
+
readonly inbox: Inbox;
|
|
23
|
+
private phase;
|
|
24
|
+
private activityDone;
|
|
25
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
26
|
+
readonly scope: Scope;
|
|
27
|
+
readonly ctx: Context;
|
|
28
|
+
/** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
|
|
29
|
+
private readonly dispatch;
|
|
30
|
+
/** Whether this loop instance has appended its initial/resume request anchor. */
|
|
31
|
+
private requestHeaderLogged;
|
|
32
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
33
|
+
get status(): AgentStatus;
|
|
34
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
35
|
+
private setPhase;
|
|
36
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* Queue a message for the next turn and wake the driver.
|
|
39
|
+
* @param input - the user message to deliver.
|
|
40
|
+
*/
|
|
41
|
+
followup(input: UserMessage): void;
|
|
42
|
+
/**
|
|
43
|
+
* Queue a message for the running step and wake the driver.
|
|
44
|
+
* @param input - the user message to deliver.
|
|
45
|
+
*/
|
|
46
|
+
steer(input: UserMessage): void;
|
|
47
|
+
/**
|
|
48
|
+
* Queue a message for the running step without waking the driver.
|
|
49
|
+
* @param input - the user message to deliver.
|
|
50
|
+
*/
|
|
51
|
+
inject(input: UserMessage): void;
|
|
52
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
53
|
+
/**
|
|
54
|
+
* Run a maintenance job while the agent is idle.
|
|
55
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
56
|
+
* @returns the maintenance result.
|
|
57
|
+
*/
|
|
58
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
59
|
+
/**
|
|
60
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
61
|
+
* activity. A wake sent while idle always opens its turn boundary, even
|
|
62
|
+
* when its message was cleared; only a latched replay is suppressed when
|
|
63
|
+
* the queue no longer holds the wake.
|
|
64
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before
|
|
65
|
+
* the inbox insertion so a reentrant cancel cannot reclassify it.
|
|
66
|
+
*/
|
|
67
|
+
private wakeDriver;
|
|
68
|
+
whenIdle(): Promise<void>;
|
|
69
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
70
|
+
private throwError;
|
|
71
|
+
private kick;
|
|
72
|
+
private preStep;
|
|
73
|
+
/**
|
|
74
|
+
* Scan the step's user messages for `/name` skill gestures, load each
|
|
75
|
+
* matching skill, and inject the rendered skill content into the message
|
|
76
|
+
* batch. This mirrors what dsh-tool-skill does for the in-process engine.
|
|
77
|
+
* @param messages - the current step's message batch.
|
|
78
|
+
* @param signal - cancellation signal (aborted loads are silently dropped).
|
|
79
|
+
* @returns the original batch when no skill was invoked, or an extended
|
|
80
|
+
* batch with injected skill-content messages appended.
|
|
81
|
+
*/
|
|
82
|
+
private injectSkills;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the native permission handling for one query. A deployment-pinned
|
|
85
|
+
* mode wins outright; otherwise the session's durable dsh permission knobs
|
|
86
|
+
* decide per query (mid-session preset switches included): full access
|
|
87
|
+
* bypasses native checks, an `ask` policy forwards each native permission
|
|
88
|
+
* request to the dsh approval seam, and anything else fails closed with the
|
|
89
|
+
* unattended deny-all stance.
|
|
90
|
+
* @returns the permission fields of the query spec.
|
|
91
|
+
*/
|
|
92
|
+
private queryPermission;
|
|
93
|
+
/** Open one turn before claiming its first proposed step. */
|
|
94
|
+
private turn;
|
|
95
|
+
/** Model label recorded in the request header for one lifecycle. */
|
|
96
|
+
private modelLabel;
|
|
97
|
+
/** Append the request header snapshot once per loop instance. */
|
|
98
|
+
private assertRequestHeader;
|
|
99
|
+
/** Run one Claude Code query for the current step and map its transcript into the session log. */
|
|
100
|
+
private step;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code loop engine module: hosts the AgentFactory that drives every
|
|
3
|
+
* session through the official Claude Agent SDK, one stateless query per dsh
|
|
4
|
+
* step, with the durable session log as the sole source of model context.
|
|
5
|
+
* dsh-loop-engine constructs this factory when the Claude Code engine is
|
|
6
|
+
* selected; this module is a library, not a Cordis plugin entry.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-loop-engine/engine-claude
|
|
9
|
+
*/
|
|
10
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import z from '@deepseek-ai/schemastery';
|
|
13
|
+
import type { AgentFactory, AgentHandle, CreateAgentOptions, ResumeAgentOptions } from '@deepseek-ai/dsh-agent';
|
|
14
|
+
import type { ClaudeCodePermissionMode, ResolvedConfig } from './types.ts';
|
|
15
|
+
/** Deployment-selectable non-interactive Claude Code permission modes. */
|
|
16
|
+
export declare const CLAUDE_CODE_PERMISSION_MODES: readonly ClaudeCodePermissionMode[];
|
|
17
|
+
/** Deployment-owned configuration for the Claude Code loop plugin. */
|
|
18
|
+
export interface Config {
|
|
19
|
+
/**
|
|
20
|
+
* Native non-interactive permission handling for every query. When omitted,
|
|
21
|
+
* each query follows the session's dsh permission knobs (`sandbox/mode` and
|
|
22
|
+
* `approval/policy`): full access bypasses native checks, an `ask` policy
|
|
23
|
+
* forwards requests to the dsh approval seam, and anything else auto-denies.
|
|
24
|
+
* A pinned mode overrides the session for every query: `dontAsk` auto-denies,
|
|
25
|
+
* `acceptEdits` accepts edits, `auto` uses the native classifier, `plan`
|
|
26
|
+
* returns a plan without approving execution, and `bypassPermissions`
|
|
27
|
+
* explicitly skips permission checks.
|
|
28
|
+
*/
|
|
29
|
+
permissionMode?: ClaudeCodePermissionMode;
|
|
30
|
+
/** Explicit environment entries layered over the credential-scrubbed parent environment. */
|
|
31
|
+
env?: Record<string, string>;
|
|
32
|
+
/** Model label for the logged request header; Claude Code native settings own the actual model. */
|
|
33
|
+
model?: string;
|
|
34
|
+
/** Grace in milliseconds for Claude Code process-tree termination. */
|
|
35
|
+
disposeGraceMs?: number;
|
|
36
|
+
/** Cap on the number of conversation turns before each query stops. */
|
|
37
|
+
maxTurns?: number;
|
|
38
|
+
}
|
|
39
|
+
/** Schema of the Claude Code loop plugin configuration. */
|
|
40
|
+
export declare const Config: z<Config>;
|
|
41
|
+
/** Host-face ctx key for the Claude Code loop service. */
|
|
42
|
+
declare module '@deepseek-ai/cordis' {
|
|
43
|
+
interface Context {
|
|
44
|
+
agentLoopClaudeCode: ClaudeCodeLoop;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Concrete AgentFactory and driver service of the Claude Code loop. Creation
|
|
49
|
+
* and resume follow the registry factory contract and the shared publication
|
|
50
|
+
* transaction: prepare, run setup, then publish through both registries,
|
|
51
|
+
* announce, and emit `agent/session-start`.
|
|
52
|
+
*/
|
|
53
|
+
export declare class ClaudeCodeLoop extends Service implements AgentFactory {
|
|
54
|
+
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
55
|
+
static inject: string[];
|
|
56
|
+
/** Validated configuration owned by the loop plugin. */
|
|
57
|
+
readonly config: ResolvedConfig;
|
|
58
|
+
private readonly ownership;
|
|
59
|
+
/** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */
|
|
60
|
+
private readonly runtime;
|
|
61
|
+
constructor(ctx: Context, config: Config);
|
|
62
|
+
/**
|
|
63
|
+
* Construct the driver, scope, and one memoized reverse teardown for a new
|
|
64
|
+
* agent. The teardown is registered with the factory and the owner fiber
|
|
65
|
+
* BEFORE publication, so a mid-setup unload rolls everything back; `signal`
|
|
66
|
+
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
67
|
+
*/
|
|
68
|
+
private prepare;
|
|
69
|
+
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
70
|
+
private setupAndPublish;
|
|
71
|
+
/**
|
|
72
|
+
* Create an agent and session under one caller-supplied identity, owned by
|
|
73
|
+
* the accessing fiber.
|
|
74
|
+
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
75
|
+
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
76
|
+
* @returns the published handle.
|
|
77
|
+
*/
|
|
78
|
+
createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
|
|
79
|
+
/**
|
|
80
|
+
* Resume an owned agent from the configured persistence service.
|
|
81
|
+
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
82
|
+
* @param options - persisted identity, loop options, setup, and cancellation.
|
|
83
|
+
* @returns the published handle.
|
|
84
|
+
*/
|
|
85
|
+
resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
|
|
86
|
+
/** Resume through an explicit persistence handle. */
|
|
87
|
+
private resumeWith;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=loop.d.ts.map
|