dsh-loop-engine 0.1.5-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/README.zh.md +53 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +675 -0
- package/lib/index.js +6821 -0
- package/lib/invariant.js +108 -0
- package/lib/types/client/LoopEngineBadge.d.ts +34 -0
- package/lib/types/client/LoopEngineComposerSelect.d.ts +40 -0
- package/lib/types/client/LoopEngineSection.d.ts +34 -0
- package/lib/types/client/index.d.ts +29 -0
- package/lib/types/client/locales.d.ts +46 -0
- package/lib/types/client/store.d.ts +58 -0
- package/lib/types/commands.d.ts +69 -0
- package/lib/types/driver-core/assistant-stream.d.ts +68 -0
- package/lib/types/driver-core/context-files.d.ts +62 -0
- package/lib/types/driver-core/inbox.d.ts +104 -0
- package/lib/types/driver-core/ownership.d.ts +40 -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 +106 -0
- package/lib/types/engine-claude/loop.d.ts +111 -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 +113 -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 +114 -0
- package/lib/types/engine-codex/permission.d.ts +32 -0
- package/lib/types/engine-codex/skills.d.ts +29 -0
- package/lib/types/engine-codex/types.d.ts +19 -0
- package/lib/types/engine-kimi/acp/client.d.ts +76 -0
- package/lib/types/engine-kimi/acp/mapping.d.ts +44 -0
- package/lib/types/engine-kimi/acp/types.d.ts +95 -0
- package/lib/types/engine-kimi/agent.d.ts +130 -0
- package/lib/types/engine-kimi/commands.d.ts +40 -0
- package/lib/types/engine-kimi/loop.d.ts +108 -0
- package/lib/types/engine-kimi/mapping.d.ts +71 -0
- package/lib/types/engine-kimi/permission.d.ts +28 -0
- package/lib/types/engine-kimi/process.d.ts +61 -0
- package/lib/types/engine-kimi/skills.d.ts +57 -0
- package/lib/types/engine-kimi/types.d.ts +23 -0
- package/lib/types/engine-pi/agent.d.ts +150 -0
- package/lib/types/engine-pi/loop.d.ts +125 -0
- package/lib/types/engine-pi/permission.d.ts +43 -0
- package/lib/types/engine-pi/probe.d.ts +23 -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 +55 -0
- package/lib/types/engine-pi/types.d.ts +27 -0
- package/lib/types/index.d.ts +114 -0
- package/lib/types/invariant.d.ts +23 -0
- package/lib/types/namespace.d.ts +9 -0
- package/lib/types/patch-manager.d.ts +59 -0
- package/lib/types/preset.d.ts +73 -0
- package/lib/types/provider-route.d.ts +49 -0
- package/lib/types/settings.d.ts +31 -0
- package/lib/types/skills.d.ts +93 -0
- package/package.json +104 -0
|
@@ -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
|
|
@@ -0,0 +1,106 @@
|
|
|
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 type { Scope } from '@deepseek-ai/dsh-scope';
|
|
11
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { ResolvedConfig } from './types.ts';
|
|
14
|
+
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
15
|
+
/** Provider route label used for logged header snapshots and message provenance. */
|
|
16
|
+
export declare const PROVIDER = "claude-code";
|
|
17
|
+
/** Drives one session through turn and step boundaries on Claude Code. */
|
|
18
|
+
export declare class ClaudeCodeAgent implements Agent {
|
|
19
|
+
private loopCtx;
|
|
20
|
+
readonly id: SessionId;
|
|
21
|
+
readonly options: AgentOptions;
|
|
22
|
+
readonly session: Session;
|
|
23
|
+
private readonly config;
|
|
24
|
+
readonly inbox: DriverInbox;
|
|
25
|
+
private phase;
|
|
26
|
+
private activityDone;
|
|
27
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
28
|
+
readonly scope: Scope;
|
|
29
|
+
readonly ctx: Context;
|
|
30
|
+
/** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
|
|
31
|
+
private readonly dispatch;
|
|
32
|
+
/** Whether this loop instance has appended its initial/resume request anchor. */
|
|
33
|
+
private requestHeaderLogged;
|
|
34
|
+
/** Agent-lifecycle-local counter naming each streamed attempt. */
|
|
35
|
+
private streamAttempts;
|
|
36
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
37
|
+
get status(): AgentStatus;
|
|
38
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
39
|
+
private setPhase;
|
|
40
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
41
|
+
/**
|
|
42
|
+
* Queue a message for the next turn and wake the driver.
|
|
43
|
+
* @param input - the user message to deliver.
|
|
44
|
+
*/
|
|
45
|
+
followup(input: UserMessage): void;
|
|
46
|
+
/**
|
|
47
|
+
* Queue a message for the running step and wake the driver.
|
|
48
|
+
* @param input - the user message to deliver.
|
|
49
|
+
*/
|
|
50
|
+
steer(input: UserMessage): void;
|
|
51
|
+
/**
|
|
52
|
+
* Queue a message for the running step without waking the driver.
|
|
53
|
+
* @param input - the user message to deliver.
|
|
54
|
+
*/
|
|
55
|
+
inject(input: UserMessage): void;
|
|
56
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
57
|
+
/**
|
|
58
|
+
* Run a maintenance job while the agent is idle.
|
|
59
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
60
|
+
* @returns the maintenance result.
|
|
61
|
+
*/
|
|
62
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
65
|
+
* activity. A wake sent while idle always opens its turn boundary, even
|
|
66
|
+
* when its message was cleared; only a latched replay is suppressed when
|
|
67
|
+
* the queue no longer holds the wake.
|
|
68
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before
|
|
69
|
+
* the inbox insertion so a reentrant cancel cannot reclassify it.
|
|
70
|
+
*/
|
|
71
|
+
private wakeDriver;
|
|
72
|
+
whenIdle(): Promise<void>;
|
|
73
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
74
|
+
private throwError;
|
|
75
|
+
private kick;
|
|
76
|
+
private preStep;
|
|
77
|
+
/**
|
|
78
|
+
* Scan the step's user messages for `/name` skill gestures, load each
|
|
79
|
+
* matching skill, and inject the rendered skill content into the message
|
|
80
|
+
* batch. This mirrors what dsh-tool-skill does for the in-process engine.
|
|
81
|
+
* @param messages - the current step's message batch.
|
|
82
|
+
* @param signal - cancellation signal (aborted loads are silently dropped).
|
|
83
|
+
* @returns the original batch when no skill was invoked, or an extended
|
|
84
|
+
* batch with injected skill-content messages appended.
|
|
85
|
+
*/
|
|
86
|
+
private injectSkills;
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the native permission handling for one query. A deployment-pinned
|
|
89
|
+
* mode wins outright; otherwise the session's durable dsh permission knobs
|
|
90
|
+
* decide per query (mid-session preset switches included): full access
|
|
91
|
+
* bypasses native checks, an `ask` policy forwards each native permission
|
|
92
|
+
* request to the dsh approval seam, and anything else fails closed with the
|
|
93
|
+
* unattended deny-all stance.
|
|
94
|
+
* @returns the permission fields of the query spec.
|
|
95
|
+
*/
|
|
96
|
+
private queryPermission;
|
|
97
|
+
/** Open one turn before claiming its first proposed step. */
|
|
98
|
+
private turn;
|
|
99
|
+
/** Model label recorded in the request header for one lifecycle. */
|
|
100
|
+
private modelLabel;
|
|
101
|
+
/** Append the request header snapshot once per loop instance. */
|
|
102
|
+
private assertRequestHeader;
|
|
103
|
+
/** Run one Claude Code query for the current step and map its transcript into the session log. */
|
|
104
|
+
private step;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
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. When a persistence backend is mounted, the session's
|
|
74
|
+
* durable identity is stored before publication.
|
|
75
|
+
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
76
|
+
* @param options - identities, optional live parent, session seed/metadata, loop options, setup, and cancellation.
|
|
77
|
+
* @returns the published handle.
|
|
78
|
+
*/
|
|
79
|
+
createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
|
|
80
|
+
/**
|
|
81
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
82
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
83
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
84
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
85
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
86
|
+
* created again.
|
|
87
|
+
* @param session - the unpublished session to store.
|
|
88
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
89
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
90
|
+
*/
|
|
91
|
+
private createStoredSession;
|
|
92
|
+
/**
|
|
93
|
+
* Durably store the session events appended since the last stored cursor.
|
|
94
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
95
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
96
|
+
* through the handle before live events start routing into it.
|
|
97
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
98
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
99
|
+
*/
|
|
100
|
+
private appendUnstoredSuffix;
|
|
101
|
+
/**
|
|
102
|
+
* Resume an owned agent from the configured persistence service.
|
|
103
|
+
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
104
|
+
* @param options - persisted identity, optional live parent, loop options, setup, and cancellation.
|
|
105
|
+
* @returns the published handle.
|
|
106
|
+
*/
|
|
107
|
+
resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
|
|
108
|
+
/** Resume through an explicit persistence service. */
|
|
109
|
+
private resumeWith;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure translation from the Claude Agent SDK's message vocabulary to the dsh
|
|
3
|
+
* session-log vocabulary. Each function maps one SDK message to the durable
|
|
4
|
+
* event payloads the driver appends inside its current step, so the mapping
|
|
5
|
+
* stays unit-testable without any SDK process.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-loop-engine/engine-claude/mapping
|
|
8
|
+
*/
|
|
9
|
+
import type { BetaMessage, BetaRawMessageStreamEvent, BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs';
|
|
10
|
+
import type { MessageParam } from '@anthropic-ai/sdk/resources';
|
|
11
|
+
import { ToolCallId, type ContentBlock, type StreamChunk, type TokenUsage, type ToolResultMessage } from '@deepseek-ai/dsh-llm';
|
|
12
|
+
/** One tool invocation surfaced from a Claude Code assistant message. */
|
|
13
|
+
export interface MappedToolCall {
|
|
14
|
+
/** SDK tool_use id, reused as the dsh call-id so results pair. */
|
|
15
|
+
readonly callId: ToolCallId;
|
|
16
|
+
/** Tool name exactly as the SDK reported it. */
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/** Raw JSON arguments string as the SDK produced them. */
|
|
19
|
+
readonly arguments: string;
|
|
20
|
+
}
|
|
21
|
+
/** Result of translating one SDK assistant message. */
|
|
22
|
+
export interface MappedAssistantMessage {
|
|
23
|
+
/** dsh content blocks: text verbatim, tool calls as tool-call blocks. */
|
|
24
|
+
readonly content: ContentBlock[];
|
|
25
|
+
/** Tool invocations surfaced as dsh tool/call events. */
|
|
26
|
+
readonly toolCalls: readonly MappedToolCall[];
|
|
27
|
+
/** Provider-reported token accounting, when present. */
|
|
28
|
+
readonly usage: TokenUsage | undefined;
|
|
29
|
+
/** Model id reported by the SDK message. */
|
|
30
|
+
readonly model: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Render an SDK tool input as the raw JSON string carried by a dsh tool-call
|
|
34
|
+
* block. Values that cannot be stringified (undefined, functions, cyclic
|
|
35
|
+
* graphs) fall back to a stable placeholder instead of failing the mapping.
|
|
36
|
+
* @param input - the SDK tool input value.
|
|
37
|
+
* @returns the JSON string, or a placeholder when the input is not JSON-serializable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function stringifyToolInput(input: unknown): string;
|
|
40
|
+
/**
|
|
41
|
+
* Translate one SDK assistant message into dsh content blocks and tool calls.
|
|
42
|
+
* Text blocks map verbatim; tool_use blocks map to tool-call blocks and
|
|
43
|
+
* surfaced calls; thinking blocks map to reasoning blocks; redacted-thinking
|
|
44
|
+
* and unknown blocks are dropped.
|
|
45
|
+
* @param message - the SDK assistant message.
|
|
46
|
+
* @returns the mapped content, calls, usage, and model.
|
|
47
|
+
*/
|
|
48
|
+
export declare function mapAssistantMessage(message: BetaMessage): MappedAssistantMessage;
|
|
49
|
+
/**
|
|
50
|
+
* Translate the tool_result blocks of one SDK user message into dsh
|
|
51
|
+
* tool-result messages. Non-tool_result blocks are ignored: Claude Code user
|
|
52
|
+
* messages inside a query carry only tool outcomes.
|
|
53
|
+
* @param message - the SDK user message.
|
|
54
|
+
* @returns the mapped tool-result messages, in block order.
|
|
55
|
+
*/
|
|
56
|
+
export declare function mapToolResults(message: MessageParam): ToolResultMessage[];
|
|
57
|
+
/**
|
|
58
|
+
* Translate SDK token accounting into the dsh token-usage shape. Cache
|
|
59
|
+
* breakpoints are optional; absent or null SDK counters stay absent.
|
|
60
|
+
* @param usage - SDK-reported usage for one assistant message.
|
|
61
|
+
* @returns dsh token accounting, omitting absent optional counters.
|
|
62
|
+
*/
|
|
63
|
+
export declare function mapUsage(usage: BetaUsage): TokenUsage;
|
|
64
|
+
/** Per-block-index tool-call identity captured at `content_block_start`, reused by `input_json_delta`. */
|
|
65
|
+
export interface StreamToolCall {
|
|
66
|
+
readonly callId: ToolCallId;
|
|
67
|
+
readonly name: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Translate one SDK raw stream event into the dsh assistant chunks that drive
|
|
71
|
+
* the live partial projection. Text blocks yield `block-start`/`text-delta`;
|
|
72
|
+
* thinking blocks yield `block-start`/`reasoning-delta`; tool_use blocks yield
|
|
73
|
+
* `block-start`/`tool-call-delta`. Redacted thinking, signature deltas,
|
|
74
|
+
* `content_block_stop`, and transport events yield nothing — the durable
|
|
75
|
+
* `assistant/message` is appended separately from the SDK's complete message,
|
|
76
|
+
* so the streamed chunks never have to carry the whole block.
|
|
77
|
+
* @param event - one raw stream event from an `includePartialMessages` query.
|
|
78
|
+
* @param toolCalls - per-block-index tool identity, mutated here at a tool
|
|
79
|
+
* `content_block_start` so later `input_json_delta` can name the call.
|
|
80
|
+
* @returns the chunks that change the visible partial (empty for non-visual events).
|
|
81
|
+
*/
|
|
82
|
+
export declare function mapStreamEvent(event: BetaRawMessageStreamEvent, toolCalls: Map<number, StreamToolCall>): StreamChunk[];
|
|
83
|
+
//# sourceMappingURL=mapping.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping from the dsh session's durable permission knobs to one Claude Code
|
|
3
|
+
* query's native permission handling. The session log pins `sandbox/mode`
|
|
4
|
+
* and `approval/policy` events at creation and records every later switch;
|
|
5
|
+
* folding them per query keeps the Claude Code driver consistent with the
|
|
6
|
+
* permission preset the web surface shows, including mid-session switches.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-loop-engine/engine-claude/permission
|
|
9
|
+
*/
|
|
10
|
+
import type { PermissionEvent } from '../driver-core/permission-knobs.ts';
|
|
11
|
+
/**
|
|
12
|
+
* The effective native permission stance for one query:
|
|
13
|
+
* - `bypass` — full access: skip every native permission check.
|
|
14
|
+
* - `ask` — forward each native permission request to the dsh approval seam.
|
|
15
|
+
* - `deny` — auto-deny every native permission request (unattended default).
|
|
16
|
+
*/
|
|
17
|
+
export type SessionPermission = {
|
|
18
|
+
readonly kind: 'bypass';
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: 'ask';
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'deny';
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the session's effective native permission stance. Full access wins
|
|
26
|
+
* outright (the web "full" preset pins it together with `never`); otherwise
|
|
27
|
+
* an `ask` policy forwards permission requests to the dsh approval seam and
|
|
28
|
+
* anything else — including a session with no recorded knobs — fails closed.
|
|
29
|
+
* @param events - the durable session log.
|
|
30
|
+
* @returns the stance one query should run under.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveSessionPermission(events: readonly PermissionEvent[]): SessionPermission;
|
|
33
|
+
/**
|
|
34
|
+
* Human-readable explanation of WHY a native permission request is asked,
|
|
35
|
+
* carrying a bounded excerpt of the exact tool input.
|
|
36
|
+
* @param toolName - the native tool being decided.
|
|
37
|
+
* @param input - the exact tool input.
|
|
38
|
+
* @returns the approval request's reason text.
|
|
39
|
+
*/
|
|
40
|
+
export declare function approvalReason(toolName: string, input: Record<string, unknown>): string;
|
|
41
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection from the shared managed-process handle to the official Claude
|
|
3
|
+
* Agent SDK's custom-spawn process interface.
|
|
4
|
+
*
|
|
5
|
+
* @module dsh-loop-engine/engine-claude/process
|
|
6
|
+
*/
|
|
7
|
+
import type { SpawnedProcess, SpawnOptions } from '@anthropic-ai/claude-agent-sdk';
|
|
8
|
+
import { type SubprocessHandle, type SubprocessOutcome, type SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
|
|
9
|
+
/**
|
|
10
|
+
* Encode the SDK's complete child environment as a subprocess overlay.
|
|
11
|
+
* @param env - SDK-composed child environment after its removals and replacements.
|
|
12
|
+
* @returns explicit values plus tombstones for surviving ambient names the SDK removed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sdkEnvironmentOverlay(env: SpawnOptions['env']): NodeJS.ProcessEnv;
|
|
15
|
+
/**
|
|
16
|
+
* Translate one official SDK spawn request to the shared process owner.
|
|
17
|
+
* @param options - command, arguments, workspace, environment, and forwarded signal from the SDK.
|
|
18
|
+
* @param graceMs - process-tree termination grace.
|
|
19
|
+
* @returns the fully explicit shared subprocess request.
|
|
20
|
+
*/
|
|
21
|
+
export declare function claudeSpawnSpec(options: SpawnOptions, graceMs: number): SubprocessSpawnSpec;
|
|
22
|
+
/**
|
|
23
|
+
* SDK-facing view of one shared managed process. Protocol transport remains
|
|
24
|
+
* in the official SDK; this adapter only projects streams and exit events.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ManagedClaudeCodeProcess implements SpawnedProcess {
|
|
27
|
+
private readonly child;
|
|
28
|
+
readonly stdin: import("stream").Writable;
|
|
29
|
+
readonly stdout: import("stream").Readable;
|
|
30
|
+
private readonly events;
|
|
31
|
+
private outcomeValue;
|
|
32
|
+
private killRequested;
|
|
33
|
+
/**
|
|
34
|
+
* Project a managed process with piped stdin and stdout.
|
|
35
|
+
* @param child - shared handle that remains the process-tree authority.
|
|
36
|
+
*/
|
|
37
|
+
constructor(child: SubprocessHandle);
|
|
38
|
+
/** Whether the SDK has requested managed tree termination. */
|
|
39
|
+
get killed(): boolean;
|
|
40
|
+
/** Direct-child exit code, or null while running or after signal exit. */
|
|
41
|
+
get exitCode(): number | null;
|
|
42
|
+
/** Direct-child terminating signal, if any. */
|
|
43
|
+
get signalCode(): NodeJS.Signals | null;
|
|
44
|
+
/** Exact managed-process outcome after exit, or undefined while running. */
|
|
45
|
+
get outcome(): SubprocessOutcome | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Route the SDK's termination request to the tree-scoped process owner.
|
|
48
|
+
* @param _signal - SDK-selected signal; the shared seam owns its escalation ladder.
|
|
49
|
+
* @returns false only after exit or a previous termination request.
|
|
50
|
+
*/
|
|
51
|
+
kill(_signal: NodeJS.Signals): boolean;
|
|
52
|
+
/** Register a persistent process lifecycle listener. */
|
|
53
|
+
on(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
|
|
54
|
+
/** Register a one-shot process lifecycle listener. */
|
|
55
|
+
once(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
|
|
56
|
+
/** Remove a process lifecycle listener. */
|
|
57
|
+
off(event: 'exit' | 'error', listener: ((code: number | null, signal: NodeJS.Signals | null) => void) | ((error: Error) => void)): void;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One Claude Agent SDK query: options assembly, process seam projection, and
|
|
3
|
+
* the headless interaction policy. The driver runs exactly one query per dsh
|
|
4
|
+
* step; this module owns no session state.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-loop-engine/engine-claude/sdk
|
|
7
|
+
*/
|
|
8
|
+
import type { Options, PermissionMode } from '@anthropic-ai/claude-agent-sdk';
|
|
9
|
+
import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
|
|
10
|
+
/** Native lock-down mode fixed for every query unless deployment overrides it. */
|
|
11
|
+
export declare const DEFAULT_PERMISSION_MODE: "dontAsk";
|
|
12
|
+
/** Grace in milliseconds for Claude Code process-tree termination. */
|
|
13
|
+
export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
|
|
14
|
+
export type { PermissionMode };
|
|
15
|
+
/** Deployment-owned process-spawn capability handed over from the plugin. */
|
|
16
|
+
export type SpawnCapability = (spec: SubprocessSpawnSpec) => SubprocessHandle;
|
|
17
|
+
/** Everything one SDK query needs, resolved at step time. */
|
|
18
|
+
export interface ClaudeCodeQuerySpec {
|
|
19
|
+
/** Absolute workspace the Claude Code process runs in. */
|
|
20
|
+
readonly cwd: string;
|
|
21
|
+
/** Native permission handling for this query. */
|
|
22
|
+
readonly permissionMode: PermissionMode;
|
|
23
|
+
/** Explicit environment entries layered over the scrubbed parent environment. */
|
|
24
|
+
readonly env?: Record<string, string>;
|
|
25
|
+
/** Grace in milliseconds for process-tree termination. */
|
|
26
|
+
readonly disposeGraceMs: number;
|
|
27
|
+
/** Model override for the SDK, when the deployment pins one. */
|
|
28
|
+
readonly model?: string;
|
|
29
|
+
/** Cap on the number of conversation turns before the query stops. */
|
|
30
|
+
readonly maxTurns?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Decide one native permission request through the dsh approval seam.
|
|
33
|
+
* When present, `canUseTool` forwards to it instead of auto-denying.
|
|
34
|
+
*/
|
|
35
|
+
readonly onToolPermission?: (toolName: string, input: Record<string, unknown>, signal: AbortSignal) => Promise<'allow' | 'deny'>;
|
|
36
|
+
/** Spawn the Claude Code child under the shared process owner. */
|
|
37
|
+
readonly spawn: SpawnCapability;
|
|
38
|
+
/** Receive a human-readable denial or decline for one unattended interaction. */
|
|
39
|
+
readonly onUnattended?: (description: string) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Diagnose one auto-answered interaction in headless mode.
|
|
43
|
+
* @param mode - permission mode in force.
|
|
44
|
+
* @param kind - what the interaction was.
|
|
45
|
+
* @param answer - what the driver did.
|
|
46
|
+
* @param why - reason the driver cannot forward the interaction.
|
|
47
|
+
* @returns a stable one-line diagnostic.
|
|
48
|
+
*/
|
|
49
|
+
export declare function unattendedDiagnostic(mode: PermissionMode, kind: string, answer: string, why: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Build the fixed official SDK options for one step's query.
|
|
52
|
+
* @param spec - workspace, environment, process seam, and disposal policy.
|
|
53
|
+
* @param controller - per-query cancellation owner.
|
|
54
|
+
* @returns the options for one stateless query.
|
|
55
|
+
*/
|
|
56
|
+
export declare function claudeQueryOptions(spec: ClaudeCodeQuerySpec, controller: AbortController): Options;
|
|
57
|
+
//# sourceMappingURL=sdk.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types of the Claude Code loop driver. Types only — no runtime code.
|
|
3
|
+
*
|
|
4
|
+
* @module dsh-loop-engine/engine-claude/types
|
|
5
|
+
*/
|
|
6
|
+
import type { PermissionMode } from '@anthropic-ai/claude-agent-sdk';
|
|
7
|
+
/** Claude Code permission modes that never wait for a human response. */
|
|
8
|
+
export type ClaudeCodePermissionMode = Extract<PermissionMode, 'dontAsk' | 'acceptEdits' | 'auto' | 'plan' | 'bypassPermissions'>;
|
|
9
|
+
/** Driver configuration after defaults and load-time validation. */
|
|
10
|
+
export interface ResolvedConfig {
|
|
11
|
+
/** Pinned native mode; `undefined` follows the session's dsh permission knobs per query. */
|
|
12
|
+
readonly permissionMode: ClaudeCodePermissionMode | undefined;
|
|
13
|
+
readonly env: Record<string, string>;
|
|
14
|
+
readonly model: string | undefined;
|
|
15
|
+
readonly disposeGraceMs: number;
|
|
16
|
+
readonly maxTurns: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|