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
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App-server protocol type definitions. A minimal subset of the types generated
|
|
3
|
+
* by `codex app-server generate-ts`, covering only what the driver needs for
|
|
4
|
+
* streaming (initialize, thread/start, turn/start, notifications).
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-loop-engine/engine-codex/appserver/types
|
|
7
|
+
*/
|
|
8
|
+
/** A JSON-RPC 2.0 request sent to the app-server. */
|
|
9
|
+
export interface JsonRpcRequest {
|
|
10
|
+
readonly jsonrpc: '2.0';
|
|
11
|
+
readonly id: number;
|
|
12
|
+
readonly method: string;
|
|
13
|
+
readonly params?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** A JSON-RPC 2.0 response (success or error). */
|
|
16
|
+
export interface JsonRpcResponse {
|
|
17
|
+
readonly id: number;
|
|
18
|
+
readonly result?: unknown;
|
|
19
|
+
readonly error?: {
|
|
20
|
+
readonly code: number;
|
|
21
|
+
readonly message: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** A JSON-RPC 2.0 notification (no id). */
|
|
25
|
+
export interface JsonRpcNotification {
|
|
26
|
+
readonly method: string;
|
|
27
|
+
readonly params: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface InitializeParams {
|
|
30
|
+
readonly clientInfo: {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly title: string | null;
|
|
33
|
+
readonly version: string;
|
|
34
|
+
};
|
|
35
|
+
readonly capabilities: {
|
|
36
|
+
readonly experimentalApi: boolean;
|
|
37
|
+
readonly requestAttestation: boolean;
|
|
38
|
+
} | null;
|
|
39
|
+
}
|
|
40
|
+
export interface InitializeResult {
|
|
41
|
+
readonly userAgent: string;
|
|
42
|
+
readonly codexHome: string;
|
|
43
|
+
readonly platformFamily: string;
|
|
44
|
+
readonly platformOs: string;
|
|
45
|
+
}
|
|
46
|
+
/** Sandbox mode accepted by `thread/start`; unlike turn policies, this is a string enum. */
|
|
47
|
+
export type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
|
|
48
|
+
/** Internally tagged sandbox override accepted by `turn/start`. */
|
|
49
|
+
export type SandboxPolicy = {
|
|
50
|
+
readonly type: 'dangerFullAccess';
|
|
51
|
+
} | {
|
|
52
|
+
readonly type: 'readOnly';
|
|
53
|
+
readonly networkAccess: boolean;
|
|
54
|
+
} | {
|
|
55
|
+
readonly type: 'externalSandbox';
|
|
56
|
+
readonly networkAccess: 'restricted' | 'enabled';
|
|
57
|
+
} | {
|
|
58
|
+
readonly type: 'workspaceWrite';
|
|
59
|
+
readonly writableRoots: readonly string[];
|
|
60
|
+
readonly networkAccess: boolean;
|
|
61
|
+
readonly excludeTmpdirEnvVar: boolean;
|
|
62
|
+
readonly excludeSlashTmp: boolean;
|
|
63
|
+
};
|
|
64
|
+
export interface ThreadStartParams {
|
|
65
|
+
readonly model?: string | null;
|
|
66
|
+
readonly modelProvider?: string | null;
|
|
67
|
+
readonly cwd?: string | null;
|
|
68
|
+
readonly approvalPolicy?: string | null;
|
|
69
|
+
readonly sandbox?: SandboxMode | null;
|
|
70
|
+
readonly ephemeral?: boolean | null;
|
|
71
|
+
readonly [key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
export interface ThreadInfo {
|
|
74
|
+
readonly id: string;
|
|
75
|
+
readonly sessionId: string;
|
|
76
|
+
readonly modelProvider: string;
|
|
77
|
+
readonly [key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface ThreadStartResult {
|
|
80
|
+
readonly thread: ThreadInfo;
|
|
81
|
+
}
|
|
82
|
+
export interface ThreadResumeParams {
|
|
83
|
+
readonly threadId: string;
|
|
84
|
+
readonly cwd?: string | null;
|
|
85
|
+
readonly approvalPolicy?: string | null;
|
|
86
|
+
readonly sandbox?: string | null;
|
|
87
|
+
readonly [key: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
export interface TurnStartParams {
|
|
90
|
+
readonly threadId: string;
|
|
91
|
+
readonly input: readonly TurnInput[];
|
|
92
|
+
readonly cwd?: string | null;
|
|
93
|
+
readonly approvalPolicy?: string | null;
|
|
94
|
+
readonly sandboxPolicy?: SandboxPolicy | null;
|
|
95
|
+
readonly model?: string | null;
|
|
96
|
+
readonly [key: string]: unknown;
|
|
97
|
+
}
|
|
98
|
+
export interface TurnInput {
|
|
99
|
+
readonly type: 'text';
|
|
100
|
+
readonly text: string;
|
|
101
|
+
}
|
|
102
|
+
export interface TurnInfo {
|
|
103
|
+
readonly id: string;
|
|
104
|
+
readonly status: string;
|
|
105
|
+
readonly error: {
|
|
106
|
+
readonly message: string;
|
|
107
|
+
} | null;
|
|
108
|
+
readonly items: readonly unknown[];
|
|
109
|
+
readonly [key: string]: unknown;
|
|
110
|
+
}
|
|
111
|
+
export interface TurnStartResult {
|
|
112
|
+
readonly turn: TurnInfo;
|
|
113
|
+
}
|
|
114
|
+
export interface TurnInterruptParams {
|
|
115
|
+
readonly threadId: string;
|
|
116
|
+
readonly turnId: string;
|
|
117
|
+
}
|
|
118
|
+
/** item/agentMessage/delta — agent message token delta. */
|
|
119
|
+
export interface AgentMessageDeltaNotification {
|
|
120
|
+
readonly threadId: string;
|
|
121
|
+
readonly turnId: string;
|
|
122
|
+
readonly itemId: string;
|
|
123
|
+
readonly delta: string;
|
|
124
|
+
}
|
|
125
|
+
/** item/reasoning/summaryTextDelta — reasoning summary token delta. */
|
|
126
|
+
export interface ReasoningSummaryTextDeltaNotification {
|
|
127
|
+
readonly threadId: string;
|
|
128
|
+
readonly turnId: string;
|
|
129
|
+
readonly itemId: string;
|
|
130
|
+
readonly delta: string;
|
|
131
|
+
readonly summaryIndex: number;
|
|
132
|
+
}
|
|
133
|
+
/** item/reasoning/textDelta — reasoning content token delta. */
|
|
134
|
+
export interface ReasoningTextDeltaNotification {
|
|
135
|
+
readonly threadId: string;
|
|
136
|
+
readonly turnId: string;
|
|
137
|
+
readonly itemId: string;
|
|
138
|
+
readonly delta: string;
|
|
139
|
+
readonly contentIndex: number;
|
|
140
|
+
}
|
|
141
|
+
/** item/plan/delta — plan delta. */
|
|
142
|
+
export interface PlanDeltaNotification {
|
|
143
|
+
readonly threadId: string;
|
|
144
|
+
readonly turnId: string;
|
|
145
|
+
readonly itemId: string;
|
|
146
|
+
readonly delta: string;
|
|
147
|
+
}
|
|
148
|
+
/** item/started — item lifecycle start. */
|
|
149
|
+
export interface ItemStartedNotification {
|
|
150
|
+
readonly threadId: string;
|
|
151
|
+
readonly turnId: string;
|
|
152
|
+
readonly item: {
|
|
153
|
+
readonly type: string;
|
|
154
|
+
readonly id: string;
|
|
155
|
+
readonly [key: string]: unknown;
|
|
156
|
+
};
|
|
157
|
+
readonly startedAtMs: number;
|
|
158
|
+
}
|
|
159
|
+
/** item/completed — item lifecycle end. */
|
|
160
|
+
export interface ItemCompletedNotification {
|
|
161
|
+
readonly threadId: string;
|
|
162
|
+
readonly turnId: string;
|
|
163
|
+
readonly item: {
|
|
164
|
+
readonly type: string;
|
|
165
|
+
readonly id: string;
|
|
166
|
+
readonly text?: string;
|
|
167
|
+
readonly [key: string]: unknown;
|
|
168
|
+
};
|
|
169
|
+
readonly completedAtMs: number;
|
|
170
|
+
}
|
|
171
|
+
/** turn/completed — turn end with usage. */
|
|
172
|
+
export interface TurnCompletedNotification {
|
|
173
|
+
readonly threadId: string;
|
|
174
|
+
readonly turn: TurnInfo & {
|
|
175
|
+
readonly usage?: {
|
|
176
|
+
readonly inputTokens: number;
|
|
177
|
+
readonly cachedInputTokens?: number;
|
|
178
|
+
readonly outputTokens: number;
|
|
179
|
+
readonly reasoningOutputTokens?: number;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** thread/tokenUsage/updated — token usage update. */
|
|
184
|
+
export interface ThreadTokenUsageUpdatedNotification {
|
|
185
|
+
readonly threadId: string;
|
|
186
|
+
readonly turnId: string;
|
|
187
|
+
readonly tokenUsage: {
|
|
188
|
+
readonly total: {
|
|
189
|
+
readonly totalTokens: number;
|
|
190
|
+
readonly inputTokens: number;
|
|
191
|
+
readonly cachedInputTokens: number;
|
|
192
|
+
readonly outputTokens: number;
|
|
193
|
+
readonly reasoningOutputTokens: number;
|
|
194
|
+
};
|
|
195
|
+
readonly last: {
|
|
196
|
+
readonly totalTokens: number;
|
|
197
|
+
readonly inputTokens: number;
|
|
198
|
+
readonly cachedInputTokens: number;
|
|
199
|
+
readonly outputTokens: number;
|
|
200
|
+
readonly reasoningOutputTokens: number;
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/** error notification. */
|
|
205
|
+
export interface ErrorNotification {
|
|
206
|
+
readonly threadId: string;
|
|
207
|
+
readonly turnId: string;
|
|
208
|
+
readonly error: {
|
|
209
|
+
readonly message: string;
|
|
210
|
+
readonly codexErrorInfo?: string | null;
|
|
211
|
+
readonly additionalDetails?: string | null;
|
|
212
|
+
};
|
|
213
|
+
readonly willRetry: boolean;
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex loop engine module: hosts the AgentFactory that drives every session
|
|
3
|
+
* through the OpenAI Codex SDK, one stateless thread per dsh step, with the
|
|
4
|
+
* durable session log as the sole source of model context. dsh-loop-engine
|
|
5
|
+
* constructs this factory when the Codex engine is selected; this module is a
|
|
6
|
+
* library, not a Cordis plugin entry. The Codex SDK spawns its own CLI binary
|
|
7
|
+
* (no spawn injection seam), so this loop deliberately does not inject the dsh
|
|
8
|
+
* subprocess service.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-loop-engine/engine-codex
|
|
11
|
+
*/
|
|
12
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import z from '@deepseek-ai/schemastery';
|
|
15
|
+
import type { AgentFactory, AgentHandle, CreateAgentOptions, ResumeAgentOptions } from '@deepseek-ai/dsh-agent';
|
|
16
|
+
import type { CodexApprovalPolicy, CodexSandboxMode, ResolvedConfig } from './types.ts';
|
|
17
|
+
/** Codex CLI sandbox modes a deployment may pin. */
|
|
18
|
+
export declare const CODEX_SANDBOX_MODES: readonly CodexSandboxMode[];
|
|
19
|
+
/** Codex CLI approval policies a deployment may pin. */
|
|
20
|
+
export declare const CODEX_APPROVAL_POLICIES: readonly CodexApprovalPolicy[];
|
|
21
|
+
/** Deployment-owned configuration for the Codex loop plugin. */
|
|
22
|
+
export interface Config {
|
|
23
|
+
/**
|
|
24
|
+
* Pinned sandbox mode for every thread. When omitted, each query follows the
|
|
25
|
+
* session's dsh permission knobs (`sandbox/mode` and `approval/policy`):
|
|
26
|
+
* full access maps to `danger-full-access`, an `ask` policy maps to
|
|
27
|
+
* `workspace-write`, and anything else fails closed with `read-only`.
|
|
28
|
+
*/
|
|
29
|
+
sandboxMode?: CodexSandboxMode;
|
|
30
|
+
/**
|
|
31
|
+
* Pinned approval policy for every thread. When omitted, each query follows
|
|
32
|
+
* the session's dsh permission knobs: an `ask` policy maps to `on-request`
|
|
33
|
+
* (the CLI's own interactive prompt degrades to a denial in the unattended
|
|
34
|
+
* dsh runtime) and anything else maps to `never`.
|
|
35
|
+
*/
|
|
36
|
+
approvalPolicy?: CodexApprovalPolicy;
|
|
37
|
+
/** Explicit environment entries layered over the credential-scrubbed parent environment. */
|
|
38
|
+
env?: Record<string, string>;
|
|
39
|
+
/** Model override for the SDK; Codex native settings own the model when omitted. */
|
|
40
|
+
model?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Schema of the Codex loop plugin configuration. */
|
|
43
|
+
export declare const Config: z<Config>;
|
|
44
|
+
/** Host-face ctx key for the Codex loop service. */
|
|
45
|
+
declare module '@deepseek-ai/cordis' {
|
|
46
|
+
interface Context {
|
|
47
|
+
agentLoopCodex: CodexLoop;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Concrete AgentFactory and driver service of the Codex loop. Creation and
|
|
52
|
+
* resume follow the registry factory contract and the shared publication
|
|
53
|
+
* transaction: prepare, run setup, then publish through both registries,
|
|
54
|
+
* announce, and emit `agent/session-start`.
|
|
55
|
+
*/
|
|
56
|
+
export declare class CodexLoop extends Service implements AgentFactory {
|
|
57
|
+
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
58
|
+
static inject: string[];
|
|
59
|
+
/** Validated configuration owned by the loop plugin. */
|
|
60
|
+
readonly config: ResolvedConfig;
|
|
61
|
+
private readonly ownership;
|
|
62
|
+
/** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */
|
|
63
|
+
private readonly runtime;
|
|
64
|
+
constructor(ctx: Context, config: Config);
|
|
65
|
+
/**
|
|
66
|
+
* Construct the driver, scope, and one memoized reverse teardown for a new
|
|
67
|
+
* agent. The teardown is registered with the factory and the owner fiber
|
|
68
|
+
* BEFORE publication, so a mid-setup unload rolls everything back; `signal`
|
|
69
|
+
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
70
|
+
*/
|
|
71
|
+
private prepare;
|
|
72
|
+
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
73
|
+
private setupAndPublish;
|
|
74
|
+
/**
|
|
75
|
+
* Create an agent and session under one caller-supplied identity, owned by
|
|
76
|
+
* the accessing fiber.
|
|
77
|
+
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
78
|
+
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
79
|
+
* @returns the published handle.
|
|
80
|
+
*/
|
|
81
|
+
createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
|
|
82
|
+
/**
|
|
83
|
+
* Resume an owned agent from the configured persistence service.
|
|
84
|
+
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
85
|
+
* @param options - persisted identity, loop options, setup, and cancellation.
|
|
86
|
+
* @returns the published handle.
|
|
87
|
+
*/
|
|
88
|
+
resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
|
|
89
|
+
/** Resume through an explicit persistence handle. */
|
|
90
|
+
private resumeWith;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping from the dsh session's durable permission knobs to one Codex query's
|
|
3
|
+
* declarative permission stance. Codex has no interactive approval callback:
|
|
4
|
+
* permissions are the `sandboxMode` + `approvalPolicy` pair chosen when the
|
|
5
|
+
* thread starts, so the fold maps the session's `sandbox/mode` and
|
|
6
|
+
* `approval/policy` events directly, mirroring the web surface's presets:
|
|
7
|
+
* - full access → `danger-full-access` + `never` (no native checks at all),
|
|
8
|
+
* - an `ask` policy → `workspace-write` + `on-request` (the CLI's own
|
|
9
|
+
* interactive prompt degrades to a denial in the unattended dsh runtime),
|
|
10
|
+
* - anything else fails closed → `read-only` + `never`.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-loop-engine/engine-codex/permission
|
|
13
|
+
*/
|
|
14
|
+
import type { PermissionEvent } from '../driver-core/permission-knobs.ts';
|
|
15
|
+
import type { CodexApprovalPolicy, CodexSandboxMode } from './types.ts';
|
|
16
|
+
/** The declarative permission stance one Codex thread runs under. */
|
|
17
|
+
export interface CodexPermission {
|
|
18
|
+
readonly sandboxMode: CodexSandboxMode;
|
|
19
|
+
readonly approvalPolicy: CodexApprovalPolicy;
|
|
20
|
+
}
|
|
21
|
+
/** Conservative unattended default: read-only sandbox, never ask. */
|
|
22
|
+
export declare const DEFAULT_CODEX_PERMISSION: CodexPermission;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the session's effective Codex permission stance. Full access wins
|
|
25
|
+
* outright; otherwise an `ask` policy maps to the CLI's on-request approval
|
|
26
|
+
* inside a workspace-write sandbox; anything else — including a session with
|
|
27
|
+
* no recorded knobs — fails closed.
|
|
28
|
+
* @param events - the durable session log.
|
|
29
|
+
* @returns the stance one query should run under.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveSessionPermission(events: readonly PermissionEvent[]): CodexPermission;
|
|
32
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex skill provider: exposes the codex CLI's instruction files as DSH
|
|
3
|
+
* skills. Codex has no per-skill catalog like Claude Code's `.claude/skills/`;
|
|
4
|
+
* it reads project and user instruction files named `AGENTS.md`. Each found
|
|
5
|
+
* `AGENTS.md` is surfaced as a single user-invocable skill whose content is
|
|
6
|
+
* the file body, so the dsh skill-injection seam (`/name` gestures) can carry
|
|
7
|
+
* it into the prompt.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-loop-engine/engine-codex/skills
|
|
10
|
+
*/
|
|
11
|
+
import { type SkillCandidate, type SkillDefinition, type SkillLookupOptions, type SkillProvider, type SkillProviderControl } from '../skills.ts';
|
|
12
|
+
/**
|
|
13
|
+
* Skill provider that discovers `AGENTS.md` from the project root (git root
|
|
14
|
+
* when one exists) and the user home `~/.codex/AGENTS.md`.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CodexSkillProvider implements SkillProvider {
|
|
17
|
+
private readonly control;
|
|
18
|
+
readonly name = "codex";
|
|
19
|
+
constructor(control: SkillProviderControl);
|
|
20
|
+
list(options: SkillLookupOptions): Promise<readonly SkillCandidate[]>;
|
|
21
|
+
get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
|
|
22
|
+
/** Read one AGENTS.md file and push a candidate when it exists. */
|
|
23
|
+
private collectAgentsMd;
|
|
24
|
+
}
|
|
25
|
+
export default CodexSkillProvider;
|
|
26
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types of the Codex loop driver. Types only — no runtime code.
|
|
3
|
+
*
|
|
4
|
+
* @module dsh-loop-engine/engine-codex/types
|
|
5
|
+
*/
|
|
6
|
+
/** Codex CLI sandbox modes, as spoken by the app-server `sandbox` field. */
|
|
7
|
+
export type CodexSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
|
|
8
|
+
/** Codex CLI approval policies, as spoken by the app-server `approvalPolicy` field. */
|
|
9
|
+
export type CodexApprovalPolicy = 'never' | 'on-request' | 'on-failure' | 'untrusted';
|
|
10
|
+
/** Driver configuration after defaults and load-time validation. */
|
|
11
|
+
export interface ResolvedConfig {
|
|
12
|
+
/** Pinned sandbox mode; `undefined` follows the session's dsh permission knobs per query. */
|
|
13
|
+
readonly sandboxMode: CodexSandboxMode | undefined;
|
|
14
|
+
/** Pinned approval policy; `undefined` follows the session's dsh permission knobs per query. */
|
|
15
|
+
readonly approvalPolicy: CodexApprovalPolicy | undefined;
|
|
16
|
+
readonly env: Record<string, string>;
|
|
17
|
+
readonly model: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi loop Agent: drives one session through turn and step boundaries by
|
|
3
|
+
* spawning a `pi --mode rpc` child process and speaking strict-LF JSONL over
|
|
4
|
+
* stdio. The dsh session log is the sole source of truth and each step runs one
|
|
5
|
+
* stateless Pi session (a fresh `new_session` + a single `prompt`), so the
|
|
6
|
+
* prompt is a pure serialization of the durable history plus the assembled dsh
|
|
7
|
+
* system prompt. Pi owns its tools natively but has no permission system, so
|
|
8
|
+
* the whole child is sandboxed by the dsh subprocess seam and its `--tools`
|
|
9
|
+
* are pruned to the resolved stance.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-loop-engine/engine-pi/agent
|
|
12
|
+
*/
|
|
13
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
|
|
14
|
+
import { Inbox } from '@deepseek-ai/dsh-agent';
|
|
15
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
16
|
+
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { ResolvedConfig } from './types.ts';
|
|
19
|
+
import { type PiSpawnCapability } from './rpc/client.ts';
|
|
20
|
+
/** Drives one session through turn and step boundaries on Pi. */
|
|
21
|
+
export declare class PiAgent implements Agent {
|
|
22
|
+
private loopCtx;
|
|
23
|
+
readonly id: SessionId;
|
|
24
|
+
readonly options: AgentOptions;
|
|
25
|
+
readonly session: Session;
|
|
26
|
+
private readonly config;
|
|
27
|
+
private readonly spawn;
|
|
28
|
+
private readonly bin;
|
|
29
|
+
readonly inbox: Inbox;
|
|
30
|
+
private phase;
|
|
31
|
+
private activityDone;
|
|
32
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
33
|
+
readonly scope: Scope;
|
|
34
|
+
readonly ctx: Context;
|
|
35
|
+
/** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
|
|
36
|
+
private readonly dispatch;
|
|
37
|
+
/** Whether this loop instance has appended its initial/resume request anchor. */
|
|
38
|
+
private requestHeaderLogged;
|
|
39
|
+
/** Lazily created RPC client, reused across steps and released on scope teardown. */
|
|
40
|
+
private rpc;
|
|
41
|
+
/** The spawn spec the cached client was built from; a change forces a respawn. */
|
|
42
|
+
private lastSpec;
|
|
43
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: PiSpawnCapability, bin: string);
|
|
44
|
+
/** Return the cached RPC client, respawning when the spec or process changed. */
|
|
45
|
+
private rpcClient;
|
|
46
|
+
get status(): AgentStatus;
|
|
47
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
48
|
+
private setPhase;
|
|
49
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
50
|
+
/**
|
|
51
|
+
* Queue a message for the next turn and wake the driver.
|
|
52
|
+
* @param input - the user message to deliver.
|
|
53
|
+
*/
|
|
54
|
+
followup(input: UserMessage): void;
|
|
55
|
+
/**
|
|
56
|
+
* Queue a message for the running step and wake the driver.
|
|
57
|
+
* @param input - the user message to deliver.
|
|
58
|
+
*/
|
|
59
|
+
steer(input: UserMessage): void;
|
|
60
|
+
/**
|
|
61
|
+
* Queue a message for the running step without waking the driver.
|
|
62
|
+
* @param input - the user message to deliver.
|
|
63
|
+
*/
|
|
64
|
+
inject(input: UserMessage): void;
|
|
65
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
66
|
+
/**
|
|
67
|
+
* Run a maintenance job while the agent is idle.
|
|
68
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
69
|
+
* @returns the maintenance result.
|
|
70
|
+
*/
|
|
71
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
72
|
+
/**
|
|
73
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
74
|
+
* activity. A wake sent while idle always opens its turn boundary, even
|
|
75
|
+
* when its message was cleared; only a latched replay is suppressed when
|
|
76
|
+
* the queue no longer holds the wake.
|
|
77
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before
|
|
78
|
+
* the inbox insertion so a reentrant cancel cannot reclassify it.
|
|
79
|
+
*/
|
|
80
|
+
private wakeDriver;
|
|
81
|
+
whenIdle(): Promise<void>;
|
|
82
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
83
|
+
private throwError;
|
|
84
|
+
private kick;
|
|
85
|
+
private preStep;
|
|
86
|
+
/**
|
|
87
|
+
* Scan the step's user messages for `/name` skill gestures, load each
|
|
88
|
+
* matching skill, and inject the rendered skill content into the message
|
|
89
|
+
* batch. This mirrors what dsh-tool-skill does for the in-process engine.
|
|
90
|
+
* @param messages - the current step's message batch.
|
|
91
|
+
* @param signal - cancellation signal (aborted loads are silently dropped).
|
|
92
|
+
* @returns the original batch when no skill was invoked, or an extended
|
|
93
|
+
* batch with injected skill-content messages appended.
|
|
94
|
+
*/
|
|
95
|
+
private injectSkills;
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the runtime permission stance for one query. Deployment-pinned
|
|
98
|
+
* fields win; anything unpinned follows the session's durable dsh permission
|
|
99
|
+
* knobs, re-folded per query so mid-session preset switches take effect on the
|
|
100
|
+
* next step.
|
|
101
|
+
* @returns the permission fields of the query spec.
|
|
102
|
+
*/
|
|
103
|
+
private queryPermission;
|
|
104
|
+
/** Open one turn before claiming its first proposed step. */
|
|
105
|
+
private turn;
|
|
106
|
+
/** Model label recorded in the request header for one lifecycle. */
|
|
107
|
+
private modelLabel;
|
|
108
|
+
/** Append the request header snapshot once per loop instance. */
|
|
109
|
+
private assertRequestHeader;
|
|
110
|
+
/** Build the `pi --mode rpc` argv/cwd/env for one step's child process. */
|
|
111
|
+
private spawnSpec;
|
|
112
|
+
/**
|
|
113
|
+
* Run one Pi RPC query for the current step and map its event stream into the
|
|
114
|
+
* session log. The step opens a fresh Pi session (`new_session`) and sends the
|
|
115
|
+
* serialized session history as one prompt, then consumes events until the
|
|
116
|
+
* agent settles. Like the Codex/Claude drivers, Pi owns its own system prompt
|
|
117
|
+
* natively, so the dsh system-prompt assembly (which pulls dsh tool schemas
|
|
118
|
+
* and `agent.ctx.tools`) is deliberately not run — the durable session log is
|
|
119
|
+
* the sole source of model context.
|
|
120
|
+
*/
|
|
121
|
+
private step;
|
|
122
|
+
/** Append one Pi tool result to the durable log as a `tool/result` message. */
|
|
123
|
+
private appendToolResult;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi loop engine module: hosts the AgentFactory that drives every session
|
|
3
|
+
* through the Pi CLI (`@earendil-works/pi-coding-agent`) over its JSONL RPC
|
|
4
|
+
* mode, one stateless session per dsh step, with the durable session log as the
|
|
5
|
+
* sole source of model context. dsh-loop-engine constructs this factory when
|
|
6
|
+
* the Pi engine is selected; this module is a library, not a Cordis plugin
|
|
7
|
+
* entry. Pi has no permission system, so the entire `pi --mode rpc` child is
|
|
8
|
+
* spawned through the dsh subprocess seam — the only available privilege
|
|
9
|
+
* boundary — and its `--tools` are pruned to the resolved sandbox stance.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-loop-engine/engine-pi
|
|
12
|
+
*/
|
|
13
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
+
import z from '@deepseek-ai/schemastery';
|
|
16
|
+
import type { AgentFactory, AgentHandle, CreateAgentOptions, ResumeAgentOptions } from '@deepseek-ai/dsh-agent';
|
|
17
|
+
import type { PiProcess, PiSpawnSpec } from './rpc/client.ts';
|
|
18
|
+
import type { PiSandboxMode, ResolvedConfig } from './types.ts';
|
|
19
|
+
/** Pi CLI sandbox modes a deployment may pin. */
|
|
20
|
+
export declare const PI_SANDBOX_MODES: readonly PiSandboxMode[];
|
|
21
|
+
/** Grace in milliseconds for Pi process-tree termination. */
|
|
22
|
+
export declare const PI_DISPOSE_GRACE_MS = 3000;
|
|
23
|
+
/** Deployment-owned configuration for the Pi loop plugin. */
|
|
24
|
+
export interface Config {
|
|
25
|
+
/**
|
|
26
|
+
* Pinned sandbox stance for every RPC child. When omitted, each query follows
|
|
27
|
+
* the session's dsh permission knobs (`sandbox/mode` and `approval/policy`):
|
|
28
|
+
* full access runs native, `workspace-write` wraps the child in the dsh
|
|
29
|
+
* sandbox with a write-capable tool set, an `ask` policy degrades to a
|
|
30
|
+
* read-only denial, and anything else fails closed with `read-only`.
|
|
31
|
+
*/
|
|
32
|
+
sandboxMode?: PiSandboxMode;
|
|
33
|
+
/** LLM provider for the `pi` child (`--provider`), when the deployment pins one. */
|
|
34
|
+
provider?: string;
|
|
35
|
+
/** Model pattern for the `pi` child (`--model`); Pi native settings own the model when omitted. */
|
|
36
|
+
model?: string;
|
|
37
|
+
/** Thinking/reasoning level, appended to the `--model` pattern when pinned. */
|
|
38
|
+
thinkingLevel?: string;
|
|
39
|
+
/** Explicit environment entries passed to the `pi` child. */
|
|
40
|
+
env?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
/** Schema of the Pi loop plugin configuration. */
|
|
43
|
+
export declare const Config: z<Config>;
|
|
44
|
+
/** Host-face ctx key for the Pi loop service. */
|
|
45
|
+
declare module '@deepseek-ai/cordis' {
|
|
46
|
+
interface Context {
|
|
47
|
+
agentLoopPi: PiLoop;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Concrete AgentFactory and driver service of the Pi loop. Creation and resume
|
|
52
|
+
* follow the registry factory contract and the shared publication transaction:
|
|
53
|
+
* prepare, run setup, then publish through both registries, announce, and emit
|
|
54
|
+
* `agent/session-start`.
|
|
55
|
+
*/
|
|
56
|
+
export declare class PiLoop extends Service implements AgentFactory {
|
|
57
|
+
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
58
|
+
static inject: string[];
|
|
59
|
+
/** Validated configuration owned by the loop plugin. */
|
|
60
|
+
readonly config: ResolvedConfig;
|
|
61
|
+
private readonly ownership;
|
|
62
|
+
/** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */
|
|
63
|
+
private readonly runtime;
|
|
64
|
+
/** Process-tree spawn capability handed to every agent, sandboxed by the subprocess seam. */
|
|
65
|
+
readonly spawn: (spec: PiSpawnSpec) => PiProcess;
|
|
66
|
+
/** Resolved Pi CLI entrypoint; `argv[0]` of every Pi RPC child. */
|
|
67
|
+
readonly bin: string;
|
|
68
|
+
constructor(ctx: Context, config: Config);
|
|
69
|
+
/**
|
|
70
|
+
* Construct the driver, scope, and one memoized reverse teardown for a new
|
|
71
|
+
* agent. The teardown is registered with the factory and the owner fiber
|
|
72
|
+
* BEFORE publication, so a mid-setup unload rolls everything back; `signal`
|
|
73
|
+
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
74
|
+
*/
|
|
75
|
+
private prepare;
|
|
76
|
+
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
77
|
+
private setupAndPublish;
|
|
78
|
+
/**
|
|
79
|
+
* Create an agent and session under one caller-supplied identity, owned by
|
|
80
|
+
* the accessing fiber.
|
|
81
|
+
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
82
|
+
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
83
|
+
* @returns the published handle.
|
|
84
|
+
*/
|
|
85
|
+
createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
|
|
86
|
+
/**
|
|
87
|
+
* Resume an owned agent from the configured persistence service.
|
|
88
|
+
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
89
|
+
* @param options - persisted identity, loop options, setup, and cancellation.
|
|
90
|
+
* @returns the published handle.
|
|
91
|
+
*/
|
|
92
|
+
resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
|
|
93
|
+
/** Resume through an explicit persistence handle. */
|
|
94
|
+
private resumeWith;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping from the dsh session's durable permission knobs to one Pi RPC
|
|
3
|
+
* process's runtime stance. Pi carries no native permission system — "runs
|
|
4
|
+
* with the permissions of the user" — so the driver cannot ask it to sandbox or
|
|
5
|
+
* approve. The only available boundary is the process environment: the driver
|
|
6
|
+
* either wraps the whole `pi --mode rpc` child in the dsh subprocess sandbox
|
|
7
|
+
* and prunes its `--tools`, or (full access) lets it run under the dsh user.
|
|
8
|
+
* The fold mirrors the codex bridge, mapping the session's `sandbox/mode` and
|
|
9
|
+
* `approval/policy` events directly:
|
|
10
|
+
* - full access → `danger-full-access`, no tool pruning (native tools);
|
|
11
|
+
* - `workspace-write` → sandbox wrap with a write-capable tool set;
|
|
12
|
+
* - an `ask` policy → degraded to a read-only denial (Pi has no request
|
|
13
|
+
* callback, so interactive approval can only become a rejection);
|
|
14
|
+
* - anything else fails closed → `read-only`.
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-loop-engine/engine-pi/permission
|
|
17
|
+
*/
|
|
18
|
+
import type { PermissionEvent } from '../driver-core/permission-knobs.ts';
|
|
19
|
+
import type { PiSandboxMode } from './types.ts';
|
|
20
|
+
/** The runtime stance one Pi RPC process should run under. */
|
|
21
|
+
export interface PiPermission {
|
|
22
|
+
/** Sandbox mode driving whether the child is wrapped in the dsh sandbox. */
|
|
23
|
+
readonly sandboxMode: PiSandboxMode;
|
|
24
|
+
/** The `--tools` allowlist; empty means "use Pi's native tools" (no pruning). */
|
|
25
|
+
readonly tools: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/** Conservative unattended default: read-only sandbox, no write/exec tools. */
|
|
28
|
+
export declare const DEFAULT_PI_PERMISSION: PiPermission;
|
|
29
|
+
/**
|
|
30
|
+
* Derive the `--tools` allowlist for a given sandbox stance. Full access prunes
|
|
31
|
+
* nothing; `workspace-write` allows a write-capable set; `read-only` allows read
|
|
32
|
+
* and search only.
|
|
33
|
+
* @param mode - the resolved sandbox stance.
|
|
34
|
+
* @returns the tool set to pass as `--tools`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toolsForSandbox(mode: PiSandboxMode): readonly string[];
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the session's effective Pi runtime stance.
|
|
39
|
+
* @param events - the durable session log.
|
|
40
|
+
* @returns the stance one Pi RPC process should run under.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveSessionPermission(events: readonly PermissionEvent[]): PiPermission;
|
|
43
|
+
//# sourceMappingURL=permission.d.ts.map
|