dsh-loop-engine 0.1.5-rc3 → 0.1.5-rc5
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 +44 -187
- package/README.zh.md +45 -103
- package/lib/client.js +887 -266
- package/lib/index.js +2216 -915
- package/lib/invariant.js +43 -45
- package/lib/types/agent-preset-ids.d.ts +303 -0
- package/lib/types/client/LoopEngineBadge.d.ts +44 -17
- package/lib/types/client/LoopEngineComposerSelect.d.ts +79 -13
- package/lib/types/client/LoopEngineSection.d.ts +5 -4
- package/lib/types/client/locales.d.ts +133 -7
- package/lib/types/client/reload.d.ts +135 -0
- package/lib/types/client/session-engine.d.ts +474 -0
- package/lib/types/client/store.d.ts +1 -1
- package/lib/types/client/turn-status.d.ts +112 -10
- package/lib/types/client/use-session-engine.d.ts +66 -0
- package/lib/types/commands.d.ts +11 -3
- package/lib/types/driver-core/host-servers.d.ts +106 -0
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
- package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +72 -0
- package/lib/types/driver-core/model-handover.d.ts +134 -0
- package/lib/types/driver-core/ownership.d.ts +6 -5
- package/lib/types/driver-core/prompt.d.ts +32 -0
- package/lib/types/driver-core/session-lifetime.d.ts +62 -0
- package/lib/types/driver-core/session-model.d.ts +82 -0
- package/lib/types/engine-claude/agent.d.ts +23 -3
- package/lib/types/engine-claude/loop.d.ts +16 -15
- package/lib/types/engine-codex/agent.d.ts +22 -3
- package/lib/types/engine-codex/appserver/client.d.ts +15 -2
- package/lib/types/engine-codex/loop.d.ts +13 -15
- package/lib/types/engine-codex/model-handover.d.ts +56 -0
- package/lib/types/engine-kimi/acp/client.d.ts +10 -0
- package/lib/types/engine-kimi/agent.d.ts +19 -2
- package/lib/types/engine-kimi/commands.d.ts +18 -14
- package/lib/types/engine-kimi/loop.d.ts +14 -16
- package/lib/types/engine-kimi/model-handover.d.ts +32 -0
- package/lib/types/engine-kimi/process.d.ts +2 -2
- package/lib/types/engine-kimi/types.d.ts +1 -1
- package/lib/types/engine-of-session.d.ts +97 -0
- package/lib/types/engine-pi/agent.d.ts +25 -23
- package/lib/types/engine-pi/loop.d.ts +13 -23
- package/lib/types/engine-pi/model-handover.d.ts +39 -0
- package/lib/types/engine-pi/types.d.ts +2 -2
- package/lib/types/engine-remote.d.ts +192 -0
- package/lib/types/engine-surface.d.ts +36 -0
- package/lib/types/index.d.ts +51 -50
- package/lib/types/invariant.d.ts +8 -5
- package/lib/types/model-selection-reset.d.ts +271 -0
- package/lib/types/patch-manager.d.ts +57 -39
- package/lib/types/preset.d.ts +39 -26
- package/lib/types/provider-route.d.ts +83 -36
- package/lib/types/router-loop.d.ts +406 -0
- package/lib/types/session-engine-store.d.ts +138 -0
- package/lib/types/settings.d.ts +12 -11
- package/package.json +109 -104
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session-scoped half of one published hosted transaction: the session's
|
|
3
|
+
* entry in the store, and the write handle that stores its events.
|
|
4
|
+
*
|
|
5
|
+
* Ownership is ONE object rather than two closure variables because an engine
|
|
6
|
+
* swap MOVES it. A hot swap replaces a session's agent while the session itself
|
|
7
|
+
* stays live: the retiring machine hands its lifetime to the successor instead
|
|
8
|
+
* of releasing it, and the successor releases it later exactly as the agent that
|
|
9
|
+
* entered the session would have. Releasing it at the handover point would emit
|
|
10
|
+
* `session/disposed`, which the browser half reads as the session being gone —
|
|
11
|
+
* its session list drops the row, its composer renders "session unavailable",
|
|
12
|
+
* and the conversation view falls back to the workspace picker — so a live
|
|
13
|
+
* session would be torn out from under the page attached to it.
|
|
14
|
+
*
|
|
15
|
+
* The two release steps are separate operations because their ORDER around the
|
|
16
|
+
* agent registry's own release is load-bearing: `agent/disposed` is published
|
|
17
|
+
* after driver quiescence and BEFORE the session is detached (the event's
|
|
18
|
+
* contract in `@deepseek-ai/dsh-agent`), and the write handle closes before the
|
|
19
|
+
* store attachment is released so its buffered closing events are drained
|
|
20
|
+
* durably. Users of this type therefore close the handle, detach the agent, then
|
|
21
|
+
* leave the store.
|
|
22
|
+
*
|
|
23
|
+
* Neither step needs a memo of its own: `SessionHandle.close()` is idempotent by
|
|
24
|
+
* contract, and the detacher `SessionsService.enter` returns is single-shot.
|
|
25
|
+
*
|
|
26
|
+
* @module dsh-loop-engine/driver-core/session-lifetime
|
|
27
|
+
*/
|
|
28
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
29
|
+
import type { SessionHandle } from '@deepseek-ai/dsh-session-persistence';
|
|
30
|
+
/**
|
|
31
|
+
* One live session's lifetime resources, owned by whichever agent drives it.
|
|
32
|
+
*/
|
|
33
|
+
export declare class SessionLifetime {
|
|
34
|
+
/** The live session these resources belong to. */
|
|
35
|
+
readonly session: Session;
|
|
36
|
+
/** The write handle this lifetime owns, or `undefined` when no persistence backend is mounted. */
|
|
37
|
+
private readonly handle;
|
|
38
|
+
/** The store's detacher; `undefined` until the entry is bound by the transaction that entered the session. */
|
|
39
|
+
private detach;
|
|
40
|
+
/**
|
|
41
|
+
* @param session - the live session whose entry and write handle these are.
|
|
42
|
+
* @param handle - the write handle the transaction acquired, if a backend is mounted.
|
|
43
|
+
*/
|
|
44
|
+
constructor(session: Session, handle: SessionHandle | undefined);
|
|
45
|
+
/**
|
|
46
|
+
* Whether the session's store entry is already held: true for a lifetime a
|
|
47
|
+
* retiring machine handed over, false for one whose transaction still has to
|
|
48
|
+
* enter the session it prepared.
|
|
49
|
+
*/
|
|
50
|
+
get entered(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Bind the store entry this lifetime owns. Called once, by the transaction
|
|
53
|
+
* that enters the session; a joined session's entry is already bound.
|
|
54
|
+
* @param detach - the detacher the session's own `enter` returned.
|
|
55
|
+
*/
|
|
56
|
+
bind(detach: () => void): void;
|
|
57
|
+
/** Drain and close the write handle — the durability barrier that precedes leaving the store. */
|
|
58
|
+
closeHandle(): Promise<void>;
|
|
59
|
+
/** Remove the session from the store, publishing its paired disposal exactly once. */
|
|
60
|
+
leaveStore(): void;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=session-lifetime.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dsh model selection a hosted engine is handed, and the ONE judgement that
|
|
3
|
+
* decides it.
|
|
4
|
+
*
|
|
5
|
+
* A session's model seat is the harness's own per-session fact: the host writes
|
|
6
|
+
* a `model/selection` event when the user picks a model, and derives the
|
|
7
|
+
* session's selection from the newest pending selection, else the logged
|
|
8
|
+
* `request/header` (`packages/api/session-controller/src/agent.ts`,
|
|
9
|
+
* `selectionFor`). A hosted engine owns its model natively, but it can still be
|
|
10
|
+
* handed a real dsh model when the session selected one — that is what this
|
|
11
|
+
* module answers.
|
|
12
|
+
*
|
|
13
|
+
* Three cases, decided once here and consumed by all four drivers:
|
|
14
|
+
*
|
|
15
|
+
* - NO selection, or one whose provider is a hosted engine route label
|
|
16
|
+
* ({@link isHostedProviderRoute}: today's shared `external`, or a label an
|
|
17
|
+
* earlier build logged) → `undefined`. The engine keeps its own native
|
|
18
|
+
* default or the model the deployment pinned in its composition; nothing is
|
|
19
|
+
* sent, so the engine is not asked a question it never had an opinion on.
|
|
20
|
+
* - a REAL dsh model (any other provider) → `{ provider, model }`. Each engine
|
|
21
|
+
* renders that into its own interface: Pi's `--model <provider>/<model>`,
|
|
22
|
+
* Claude Code's `Options.model`, Codex's `thread/start` `model`, and Kimi's
|
|
23
|
+
* ACP `session/set_model`. Whether the engine can actually serve the model is
|
|
24
|
+
* the engine's own business: a refusal is reported, never swallowed.
|
|
25
|
+
*
|
|
26
|
+
* The read is the host's own ({@link currentSelection}), so the value an engine
|
|
27
|
+
* gets is the value the host would install for the session — and it is taken
|
|
28
|
+
* fresh on every step, because a session's model can change mid-conversation.
|
|
29
|
+
*
|
|
30
|
+
* @module dsh-loop-engine/driver-core/session-model
|
|
31
|
+
*/
|
|
32
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
33
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
34
|
+
import type { SessionModelSelection, SessionProjectionsService } from './host-servers.ts';
|
|
35
|
+
/** The model a session's own selection asks a hosted engine to run. */
|
|
36
|
+
export interface SessionModelOverride {
|
|
37
|
+
/** Provider route the selection names, in the engine's own provider vocabulary. */
|
|
38
|
+
readonly provider: string;
|
|
39
|
+
/** Provider-owned model id the selection names. */
|
|
40
|
+
readonly model: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The selection the host will use for this session, by the host's own read
|
|
44
|
+
* (`ApiSessionAgentController.selectionFor`): a pending selection — one
|
|
45
|
+
* appended, not yet consumed by a matching recorded request — outranks the
|
|
46
|
+
* logged header, and the header answers when nothing is pending.
|
|
47
|
+
*
|
|
48
|
+
* A deployment with no session-projection registry is not an error: the read
|
|
49
|
+
* simply skips the pending half and answers from the logged header, exactly as
|
|
50
|
+
* a session whose log carries only a header would.
|
|
51
|
+
* @param session - the session whose selection is being read.
|
|
52
|
+
* @param projections - the host session-projection registry, when composed.
|
|
53
|
+
* @returns the current selection, or undefined when the log records none.
|
|
54
|
+
*/
|
|
55
|
+
export declare function currentSelection(session: Session, projections: SessionProjectionsService | undefined): SessionModelSelection | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* The model one hosted engine should be handed for this selection, or
|
|
58
|
+
* `undefined` to leave the engine to its own default (or the deployment's pin).
|
|
59
|
+
*
|
|
60
|
+
* The provider is the whole test: a hosted engine route label — the shared
|
|
61
|
+
* `external` this plugin serves as a placeholder, or a per-engine label an
|
|
62
|
+
* earlier build logged — means "the engine decides", not a model to send. Any
|
|
63
|
+
* other provider is a real dsh model, and the plugin passes it through rather
|
|
64
|
+
* than second-guessing whether the engine can serve it: a rejection is the
|
|
65
|
+
* engine's to report, and this plugin surfaces it.
|
|
66
|
+
* @param selection - the session's current selection, or undefined when none.
|
|
67
|
+
* @returns the model to hand over, or undefined to send nothing.
|
|
68
|
+
*/
|
|
69
|
+
export declare function sessionModelOverride(selection: SessionModelSelection | undefined): SessionModelOverride | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* The model override for one session, read live from its log and judged by
|
|
72
|
+
* {@link sessionModelOverride}.
|
|
73
|
+
*
|
|
74
|
+
* The four drivers' single entry: each calls this at the top of every step, so a
|
|
75
|
+
* model picked mid-conversation lands on the engine's next request rather than
|
|
76
|
+
* being frozen when the agent was built.
|
|
77
|
+
* @param ctx - the driver's context, carrying the session-projection registry.
|
|
78
|
+
* @param session - the session whose selection is being resolved.
|
|
79
|
+
* @returns the model to hand the engine, or undefined to send nothing.
|
|
80
|
+
*/
|
|
81
|
+
export declare function sessionModelOverrideOf(ctx: Context, session: Session): SessionModelOverride | undefined;
|
|
82
|
+
//# sourceMappingURL=session-model.d.ts.map
|
|
@@ -12,8 +12,14 @@ import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
13
|
import type { ResolvedConfig } from './types.ts';
|
|
14
14
|
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import { type SpawnCapability } from './sdk.ts';
|
|
16
|
+
/**
|
|
17
|
+
* Provider route label this driver logs into request/header snapshots and
|
|
18
|
+
* message provenance — the ONE route every hosted engine shares
|
|
19
|
+
* ({@link HOSTED_ROUTE_LABEL}), so all four engines select `external/default`
|
|
20
|
+
* and the model menu carries a single group instead of one per engine.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PROVIDER = "external";
|
|
17
23
|
/** Drives one session through turn and step boundaries on Claude Code. */
|
|
18
24
|
export declare class ClaudeCodeAgent implements Agent {
|
|
19
25
|
private loopCtx;
|
|
@@ -21,6 +27,13 @@ export declare class ClaudeCodeAgent implements Agent {
|
|
|
21
27
|
readonly options: AgentOptions;
|
|
22
28
|
readonly session: Session;
|
|
23
29
|
private readonly config;
|
|
30
|
+
/**
|
|
31
|
+
* Process-spawn capability handed down from the engine, which resolves it
|
|
32
|
+
* from the host's subprocess service. Held as a capability rather than read
|
|
33
|
+
* off the context because a service PROPERTY read requires the reading
|
|
34
|
+
* fiber to have injected it, and this agent's context is the engine's.
|
|
35
|
+
*/
|
|
36
|
+
private readonly spawn;
|
|
24
37
|
readonly inbox: DriverInbox;
|
|
25
38
|
private phase;
|
|
26
39
|
private activityDone;
|
|
@@ -44,7 +57,14 @@ export declare class ClaudeCodeAgent implements Agent {
|
|
|
44
57
|
* usage record is only meaningful while one step holds the whole query.
|
|
45
58
|
*/
|
|
46
59
|
private rotated;
|
|
47
|
-
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig
|
|
60
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig,
|
|
61
|
+
/**
|
|
62
|
+
* Process-spawn capability handed down from the engine, which resolves it
|
|
63
|
+
* from the host's subprocess service. Held as a capability rather than read
|
|
64
|
+
* off the context because a service PROPERTY read requires the reading
|
|
65
|
+
* fiber to have injected it, and this agent's context is the engine's.
|
|
66
|
+
*/
|
|
67
|
+
spawn: SpawnCapability);
|
|
48
68
|
get status(): AgentStatus;
|
|
49
69
|
/** Commit a phase and publish its externally visible status transition. */
|
|
50
70
|
private setPhase;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code loop engine module:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Claude Code loop engine module: drives every session it is handed through
|
|
3
|
+
* the official Claude Agent SDK, one stateless query per dsh step, with the
|
|
4
|
+
* durable session log as the sole source of model context. The router routes a
|
|
5
|
+
* session here on the plugin's own engine record, else its recorded agent
|
|
6
|
+
* preset; this module is a library, not a Cordis plugin entry.
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-loop-engine/engine-claude
|
|
9
9
|
*/
|
|
@@ -12,8 +12,9 @@ import z from '@deepseek-ai/schemastery';
|
|
|
12
12
|
import type { AgentOptions } from '@deepseek-ai/dsh-agent';
|
|
13
13
|
import type { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
14
14
|
import { ClaudeCodeAgent } from './agent.ts';
|
|
15
|
+
import { type SpawnCapability } from './sdk.ts';
|
|
15
16
|
import type { ClaudeCodePermissionMode, ResolvedConfig } from './types.ts';
|
|
16
|
-
import {
|
|
17
|
+
import { HostedEngineRuntime } from '../driver-core/hosted-engine-runtime.ts';
|
|
17
18
|
/** Deployment-selectable non-interactive Claude Code permission modes. */
|
|
18
19
|
export declare const CLAUDE_CODE_PERMISSION_MODES: readonly ClaudeCodePermissionMode[];
|
|
19
20
|
/** Deployment-owned configuration for the Claude Code loop plugin. */
|
|
@@ -31,7 +32,7 @@ export interface Config {
|
|
|
31
32
|
permissionMode?: ClaudeCodePermissionMode;
|
|
32
33
|
/** Explicit environment entries layered over the credential-scrubbed parent environment. */
|
|
33
34
|
env?: Record<string, string>;
|
|
34
|
-
/** Model
|
|
35
|
+
/** Model passed to each query (`Options.model`, overridden by the session's own pick); also the request-header label. Claude Code native settings own the model when omitted. */
|
|
35
36
|
model?: string;
|
|
36
37
|
/** Grace in milliseconds for Claude Code process-tree termination. */
|
|
37
38
|
disposeGraceMs?: number;
|
|
@@ -40,21 +41,21 @@ export interface Config {
|
|
|
40
41
|
}
|
|
41
42
|
/** Schema of the Claude Code loop plugin configuration. */
|
|
42
43
|
export declare const Config: z<Config>;
|
|
43
|
-
/** Host-face ctx key
|
|
44
|
+
/** Host-face ctx key this engine's runtime is registered under. */
|
|
45
|
+
export declare const CLAUDE_CODE_ENGINE_LABEL = "agentLoopClaudeCode";
|
|
44
46
|
declare module '@deepseek-ai/cordis' {
|
|
45
47
|
interface Context {
|
|
46
48
|
agentLoopClaudeCode: ClaudeCodeLoop;
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* announce, and emit `agent/session-start`.
|
|
52
|
+
* Creation/resume machinery for the Claude Code engine. The process-wide
|
|
53
|
+
* AgentFactory slot belongs to the router, which delegates each session to the
|
|
54
|
+
* engine its preset names; this class is that engine's driver, not a plugin.
|
|
54
55
|
*/
|
|
55
|
-
export declare class ClaudeCodeLoop extends
|
|
56
|
-
/**
|
|
57
|
-
|
|
56
|
+
export declare class ClaudeCodeLoop extends HostedEngineRuntime<ResolvedConfig, ClaudeCodeAgent> {
|
|
57
|
+
/** Process-spawn capability handed to every agent, sandboxed by the subprocess seam. */
|
|
58
|
+
readonly spawn: SpawnCapability;
|
|
58
59
|
constructor(ctx: Context, config: Config);
|
|
59
60
|
/** Construct the Claude Code driver for one prepared session. */
|
|
60
61
|
protected buildAgent(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session): ClaudeCodeAgent;
|
|
@@ -19,8 +19,13 @@ import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
|
19
19
|
import type { Context } from '@deepseek-ai/cordis';
|
|
20
20
|
import type { ResolvedConfig } from './types.ts';
|
|
21
21
|
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Provider route label this driver logs into request/header snapshots and
|
|
24
|
+
* message provenance — the ONE route every hosted engine shares
|
|
25
|
+
* ({@link HOSTED_ROUTE_LABEL}), so all four engines select `external/default`
|
|
26
|
+
* and the model menu carries a single group instead of one per engine.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PROVIDER = "external";
|
|
24
29
|
/** Drives one session through turn and step boundaries on Codex. */
|
|
25
30
|
export declare class CodexAgent implements Agent {
|
|
26
31
|
private loopCtx;
|
|
@@ -59,8 +64,22 @@ export declare class CodexAgent implements Agent {
|
|
|
59
64
|
private beginSegment;
|
|
60
65
|
/** Lazily created app-server client, reused across steps and released on scope teardown. */
|
|
61
66
|
private appServer;
|
|
67
|
+
/**
|
|
68
|
+
* The spawn configuration (`argv` + `env`) the cached client was built from.
|
|
69
|
+
* Codex's endpoint lives in the child's own command line, so a handover change
|
|
70
|
+
* must respawn the child rather than reuse one configured for another endpoint.
|
|
71
|
+
*/
|
|
72
|
+
private appServerConfig;
|
|
62
73
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
63
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Return the cached app-server client, spawning one on first use, after a dead
|
|
76
|
+
* process, or when the resolved dsh endpoint changed since the last spawn.
|
|
77
|
+
*
|
|
78
|
+
* A handover is codex's own `-c` configuration for a dsh provider, so a
|
|
79
|
+
* different endpoint (different provider, base URL, protocol, or credential)
|
|
80
|
+
* needs a different child — the config is fixed when the process starts.
|
|
81
|
+
* @param handover - the session's resolved dsh endpoint, or undefined to leave codex to its own configuration.
|
|
82
|
+
*/
|
|
64
83
|
private appServerClient;
|
|
65
84
|
/**
|
|
66
85
|
* Answer one server-initiated interaction. Approvals go through the dsh
|
|
@@ -35,8 +35,21 @@ export declare class AppServerClient {
|
|
|
35
35
|
get closed(): boolean;
|
|
36
36
|
/** Create a client by spawning `codex app-server`. */
|
|
37
37
|
private constructor();
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Spawn the pinned app-server dependency and initialize the client.
|
|
40
|
+
*
|
|
41
|
+
* `argv` appends the driver's own overrides to the bare `app-server`
|
|
42
|
+
* subcommand (codex's `-c key=value` configuration), and `env` layers the
|
|
43
|
+
* driver's explicit entries over the ambient environment — the child still
|
|
44
|
+
* needs `PATH` and, under a user's own codex setup, the ambient auth facts its
|
|
45
|
+
* configuration reads, so this is an overlay, not a replacement. Both default
|
|
46
|
+
* to nothing, which is the bare `codex app-server` the driver used before a
|
|
47
|
+
* dsh endpoint was ever handed over.
|
|
48
|
+
* @param argv - extra arguments after the `app-server` subcommand.
|
|
49
|
+
* @param env - explicit environment entries layered over the ambient one.
|
|
50
|
+
* @returns the initialized client.
|
|
51
|
+
*/
|
|
52
|
+
static create(argv?: readonly string[], env?: NodeJS.ProcessEnv): Promise<AppServerClient>;
|
|
40
53
|
/** Set the notification handler for streaming events. */
|
|
41
54
|
onNotification(handler: NotificationHandler): void;
|
|
42
55
|
/** Set the handler for server-initiated requests (e.g. approvals). */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Codex loop engine module:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* library, not a Cordis plugin entry. The Codex SDK spawns its own CLI binary
|
|
2
|
+
* Codex loop engine module: drives every session it is handed through the
|
|
3
|
+
* OpenAI Codex SDK, one stateless thread per dsh step, with the durable
|
|
4
|
+
* session log as the sole source of model context. The router routes a session
|
|
5
|
+
* here on the plugin's own engine record, else its agent preset; this module is
|
|
6
|
+
* a library, not a Cordis plugin entry. The Codex SDK spawns its own CLI binary
|
|
7
7
|
* (no spawn injection seam), so this loop deliberately does not inject the dsh
|
|
8
8
|
* subprocess service.
|
|
9
9
|
*
|
|
@@ -15,7 +15,7 @@ import type { AgentOptions } from '@deepseek-ai/dsh-agent';
|
|
|
15
15
|
import type { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
16
16
|
import { CodexAgent } from './agent.ts';
|
|
17
17
|
import type { CodexApprovalPolicy, CodexSandboxMode, ResolvedConfig } from './types.ts';
|
|
18
|
-
import {
|
|
18
|
+
import { HostedEngineRuntime } from '../driver-core/hosted-engine-runtime.ts';
|
|
19
19
|
/** Codex CLI sandbox modes a deployment may pin. */
|
|
20
20
|
export declare const CODEX_SANDBOX_MODES: readonly CodexSandboxMode[];
|
|
21
21
|
/** Codex CLI approval policies a deployment may pin. */
|
|
@@ -38,26 +38,24 @@ export interface Config {
|
|
|
38
38
|
approvalPolicy?: CodexApprovalPolicy;
|
|
39
39
|
/** Explicit environment entries layered over the credential-scrubbed parent environment. */
|
|
40
40
|
env?: Record<string, string>;
|
|
41
|
-
/**
|
|
41
|
+
/** Fallback model for the app-server thread, used when the session selects none; Codex native settings own the model when omitted. */
|
|
42
42
|
model?: string;
|
|
43
43
|
}
|
|
44
44
|
/** Schema of the Codex loop plugin configuration. */
|
|
45
45
|
export declare const Config: z<Config>;
|
|
46
|
-
/** Host-face ctx key
|
|
46
|
+
/** Host-face ctx key this engine's runtime is registered under. */
|
|
47
|
+
export declare const CODEX_ENGINE_LABEL = "agentLoopCodex";
|
|
47
48
|
declare module '@deepseek-ai/cordis' {
|
|
48
49
|
interface Context {
|
|
49
50
|
agentLoopCodex: CodexLoop;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* announce, and emit `agent/session-start`.
|
|
54
|
+
* Creation/resume machinery for the Codex engine. The process-wide
|
|
55
|
+
* AgentFactory slot belongs to the router, which delegates each session to the
|
|
56
|
+
* engine its preset names; this class is that engine's driver, not a plugin.
|
|
57
57
|
*/
|
|
58
|
-
export declare class CodexLoop extends
|
|
59
|
-
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
60
|
-
static inject: string[];
|
|
58
|
+
export declare class CodexLoop extends HostedEngineRuntime<ResolvedConfig, CodexAgent> {
|
|
61
59
|
constructor(ctx: Context, config: Config);
|
|
62
60
|
/** Construct the Codex driver for one prepared session. */
|
|
63
61
|
protected buildAgent(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session): CodexAgent;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex's dialect for a dsh endpoint: the `codex app-server` configuration
|
|
3
|
+
* overrides and environment that point it at dsh's model.
|
|
4
|
+
*
|
|
5
|
+
* Codex resolves a custom endpoint through `model_providers.<id>` in its own
|
|
6
|
+
* `config.toml` (`name`, `base_url`, `wire_api`, and an `env_key` naming the
|
|
7
|
+
* variable the credential is read from), and `codex app-server` accepts those as
|
|
8
|
+
* `-c key=value` overrides — so the handover needs no edit of `~/.codex`. The
|
|
9
|
+
* credential travels in the child's environment under {@link CODEX_DSH_API_KEY_ENV},
|
|
10
|
+
* which the `env_key` names.
|
|
11
|
+
*
|
|
12
|
+
* `name` is not optional in practice even though the CLI's own docs describe it
|
|
13
|
+
* as display metadata: codex 0.149.1 (the version this plugin pins) refuses to
|
|
14
|
+
* load a provider whose `name` is empty — "provider name must not be empty in
|
|
15
|
+
* `model_providers`" — and that config error kills `app-server` on startup, which
|
|
16
|
+
* the driver can only report as "process exited unexpectedly". Omitting it looked
|
|
17
|
+
* correct and failed before any request was ever sent, so every override this
|
|
18
|
+
* module builds carries it (verified against the pinned binary).
|
|
19
|
+
*
|
|
20
|
+
* Codex speaks only OpenAI's `responses` wire as of the version this plugin pins
|
|
21
|
+
* (0.149.1 dropped `chat`). A dsh protocol with no codex equivalent (Anthropic
|
|
22
|
+
* Messages, and now OpenAI Chat Completions) is NOT guessed: `wire_api` is
|
|
23
|
+
* omitted, so codex falls back to its own default wire and fails loud on the
|
|
24
|
+
* request — the honest outcome, since the plugin cannot make codex speak a
|
|
25
|
+
* protocol it does not implement. That default is `responses` as of 0.149.1: the
|
|
26
|
+
* request lands on `<baseURL>/responses`, which an Anthropic Messages or Chat
|
|
27
|
+
* Completions endpoint does not serve.
|
|
28
|
+
*
|
|
29
|
+
* @module dsh-loop-engine/engine-codex/model-handover
|
|
30
|
+
*/
|
|
31
|
+
import type { DshModelHandover } from '../driver-core/model-handover.ts';
|
|
32
|
+
/** Provider id codex is configured with when dsh's endpoint is handed over. */
|
|
33
|
+
export declare const CODEX_DSH_PROVIDER = "dsh";
|
|
34
|
+
/** Environment variable codex reads the handed-over credential from. */
|
|
35
|
+
export declare const CODEX_DSH_API_KEY_ENV = "DSH_LOOP_ENGINE_API_KEY";
|
|
36
|
+
/** The `codex app-server` argv/env that hand one dsh endpoint over. */
|
|
37
|
+
export interface CodexModelConfig {
|
|
38
|
+
/** `-c` overrides to append to the `app-server` subcommand. */
|
|
39
|
+
readonly argv: string[];
|
|
40
|
+
/** Environment the child needs to read the endpoint's credential. */
|
|
41
|
+
readonly env: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Translate one dsh endpoint handover into codex's `-c` overrides and the
|
|
45
|
+
* environment carrying the credential.
|
|
46
|
+
*
|
|
47
|
+
* The provider id ({@link CODEX_DSH_PROVIDER}) is a plugin-owned name: it exists
|
|
48
|
+
* only in this child's command line, so it cannot collide with a provider the
|
|
49
|
+
* user configured, and the model itself is still handed over as the thread's own
|
|
50
|
+
* `model` param. The provider's `name` repeats that id because codex rejects an
|
|
51
|
+
* empty one at config load, before any request is made.
|
|
52
|
+
* @param handover - the resolved dsh endpoint for the session's selection.
|
|
53
|
+
* @returns the argv entries and environment to spawn the app-server with.
|
|
54
|
+
*/
|
|
55
|
+
export declare function codexModelConfig(handover: DshModelHandover): CodexModelConfig;
|
|
56
|
+
//# sourceMappingURL=model-handover.d.ts.map
|
|
@@ -83,6 +83,16 @@ export declare class AcpClient {
|
|
|
83
83
|
initialize(): Promise<unknown>;
|
|
84
84
|
/** Start a fresh ACP session and resolve to its session id. */
|
|
85
85
|
newSession(cwd: string): Promise<string>;
|
|
86
|
+
/**
|
|
87
|
+
* Select the model for one ACP session. The agent answers with an error frame
|
|
88
|
+
* when it cannot serve the id, and {@link request} rejects on that frame — so
|
|
89
|
+
* an engine that refuses the model fails the step loud rather than silently
|
|
90
|
+
* running its own default.
|
|
91
|
+
* @param sessionId - the ACP session the model applies to.
|
|
92
|
+
* @param modelId - the model id to select.
|
|
93
|
+
* @returns the agent's response to `session/set_model`.
|
|
94
|
+
*/
|
|
95
|
+
setModel(sessionId: string, modelId: string): Promise<unknown>;
|
|
86
96
|
/** Prompt the agent in a session and resolve when the turn completes. */
|
|
87
97
|
prompt(sessionId: string, text: string): Promise<unknown>;
|
|
88
98
|
/** Cancel the active turn in a session (fire-and-forget). */
|
|
@@ -17,8 +17,13 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
17
17
|
import type { ResolvedConfig } from './types.ts';
|
|
18
18
|
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
19
19
|
import type { KimiSpawnCapability } from './process.ts';
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Provider route label this driver logs into request/header snapshots and
|
|
22
|
+
* message provenance — the ONE route every hosted engine shares
|
|
23
|
+
* ({@link HOSTED_ROUTE_LABEL}), so all four engines select `external/default`
|
|
24
|
+
* and the model menu carries a single group instead of one per engine.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PROVIDER = "external";
|
|
22
27
|
/** Drives one session through turn and step boundaries on Kimi Code. */
|
|
23
28
|
export declare class KimiAgent implements Agent {
|
|
24
29
|
private loopCtx;
|
|
@@ -44,6 +49,13 @@ export declare class KimiAgent implements Agent {
|
|
|
44
49
|
private acp;
|
|
45
50
|
/** The spawn spec the cached client was built from; a change forces a respawn. */
|
|
46
51
|
private lastSpec;
|
|
52
|
+
/**
|
|
53
|
+
* The environment the last handover produced, memoized by its own content so
|
|
54
|
+
* an unchanged endpoint reuses ONE object: `specsEqual` compares environments
|
|
55
|
+
* by reference, and a fresh object per step would otherwise respawn the child
|
|
56
|
+
* every step.
|
|
57
|
+
*/
|
|
58
|
+
private handoverEnvCache;
|
|
47
59
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: KimiSpawnCapability, bin: string);
|
|
48
60
|
get status(): AgentStatus;
|
|
49
61
|
/** Commit a phase and publish its externally visible status transition. */
|
|
@@ -107,6 +119,11 @@ export declare class KimiAgent implements Agent {
|
|
|
107
119
|
private acpClient;
|
|
108
120
|
/** Build the `kimi acp` argv/cwd/env for the persistent child. */
|
|
109
121
|
private spawnSpec;
|
|
122
|
+
/**
|
|
123
|
+
* The child environment for one handover, memoized by content so an unchanged
|
|
124
|
+
* endpoint yields the SAME object across steps (see {@link handoverEnvCache}).
|
|
125
|
+
*/
|
|
126
|
+
private handoverEnv;
|
|
110
127
|
/** Run one `kimi acp` step for the current session history and map the streamed updates. */
|
|
111
128
|
private step;
|
|
112
129
|
/** Per-step accumulation state for streamed assistant blocks and tool calls. */
|
|
@@ -4,24 +4,28 @@
|
|
|
4
4
|
* The dsh `commands` runtime executes a registered command locally — the line is
|
|
5
5
|
* consumed and never reaches the model — so a command whose real processing
|
|
6
6
|
* lives inside the Kimi engine must forward the raw line back to the agent, which
|
|
7
|
-
* Kimi then expands
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* surface.
|
|
7
|
+
* Kimi then expands natively. Registering the built-ins keeps them visible in
|
|
8
|
+
* the dsh web slash menu; unregistered `/lines` pass through as user text, but
|
|
9
|
+
* the menu would hide the engine's command surface.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* The list below is exactly what the `kimi acp` command surface implements
|
|
12
|
+
* (verified against kimi 0.28.1, both by driving `session/prompt` directly and
|
|
13
|
+
* by reading the `available_commands_update` the child publishes): `compact`,
|
|
14
|
+
* `status`, `usage`, `mcp`, `tasks`, `help`. Everything else Kimi's TUI offers
|
|
15
|
+
* (`/login`, `/provider`, `/settings`, `/sessions`, `/clear`, `/plan`, …) is a
|
|
16
|
+
* TUI control the ACP surface does not implement — forwarding one answers
|
|
17
|
+
* `Unknown ACP command: /name.` — so none of them is registered. `skill:`
|
|
18
|
+
* commands are already carried by the dsh skill injection seam and Kimi's own
|
|
19
|
+
* shorthand, so they are not duplicated here.
|
|
18
20
|
*
|
|
19
21
|
* `model` is deliberately absent even though the CLI has it: the dsh web
|
|
20
22
|
* client owns a `/model` contribution, and a same-named host command makes
|
|
21
23
|
* `ui-commands` throw the whole command menu source away, leaving only the
|
|
22
|
-
* skill group. `/goal`
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* skill group. `/goal` is absent too: the ACP surface implements no `/goal`, so
|
|
25
|
+
* registering one would only answer with an unknown-command error. Hosted
|
|
26
|
+
* sessions have no dsh `/goal` either — the engine preset they join strips the
|
|
27
|
+
* `command-goal` row (`src/preset.ts`, `STRIPPED_ROWS`) — so the command is
|
|
28
|
+
* simply not part of their menu.
|
|
25
29
|
*
|
|
26
30
|
* @module dsh-loop-engine/engine-kimi/commands
|
|
27
31
|
*/
|
|
@@ -35,6 +39,6 @@ import type { CommandDefinition, CommandInvocation, CommandResult } from '../com
|
|
|
35
39
|
* @returns the command handler.
|
|
36
40
|
*/
|
|
37
41
|
export declare function forwardKimiCommand(name: string): (invocation: CommandInvocation) => CommandResult;
|
|
38
|
-
/** Kimi Code's built-in slash commands that
|
|
42
|
+
/** Kimi Code's built-in slash commands that the ACP surface implements. */
|
|
39
43
|
export declare const KIMI_COMMANDS: readonly CommandDefinition[];
|
|
40
44
|
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Kimi Code loop engine module:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* Kimi Code loop engine module: drives every session it is handed through a
|
|
3
|
+
* persistent `kimi acp` child (Agent Client Protocol over stdio), speaking one
|
|
4
|
+
* stateless `session/new` + `session/prompt` per dsh step, with the durable
|
|
5
|
+
* session log as the sole source of model context. The router routes a session
|
|
6
|
+
* here on the plugin's own engine record, else its agent preset; this module is
|
|
7
|
+
* a library, not a Cordis plugin entry. Kimi has no host approval
|
|
8
8
|
* callback, so tool approvals surfaced by ACP (`session/request_permission`) are
|
|
9
9
|
* answered from the session's dsh approval knobs (an `ask` policy degrades to
|
|
10
10
|
* denial); the whole child is spawned through the dsh subprocess seam — the only
|
|
@@ -21,12 +21,12 @@ import type { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
|
21
21
|
import { KimiAgent } from './agent.ts';
|
|
22
22
|
import type { KimiSpawnCapability } from './process.ts';
|
|
23
23
|
import type { ResolvedConfig } from './types.ts';
|
|
24
|
-
import {
|
|
24
|
+
import { HostedEngineRuntime } from '../driver-core/hosted-engine-runtime.ts';
|
|
25
25
|
/** Grace in milliseconds for Kimi process-tree termination. */
|
|
26
26
|
export declare const KIMI_DISPOSE_GRACE_MS = 3000;
|
|
27
27
|
/** Deployment-owned configuration for the Kimi loop plugin. */
|
|
28
28
|
export interface Config {
|
|
29
|
-
/** Model
|
|
29
|
+
/** Model sent per ACP session (`session/set_model`) when the session selects none; Kimi native config owns the model when omitted. */
|
|
30
30
|
model?: string;
|
|
31
31
|
/** Explicit environment entries passed to the `kimi` child. */
|
|
32
32
|
env?: Record<string, string>;
|
|
@@ -35,21 +35,19 @@ export interface Config {
|
|
|
35
35
|
}
|
|
36
36
|
/** Schema of the Kimi loop plugin configuration. */
|
|
37
37
|
export declare const Config: z<Config>;
|
|
38
|
-
/** Host-face ctx key
|
|
38
|
+
/** Host-face ctx key this engine's runtime is registered under. */
|
|
39
|
+
export declare const KIMI_ENGINE_LABEL = "agentLoopKimi";
|
|
39
40
|
declare module '@deepseek-ai/cordis' {
|
|
40
41
|
interface Context {
|
|
41
42
|
agentLoopKimi: KimiLoop;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* announce, and emit `agent/session-start`.
|
|
46
|
+
* Creation/resume machinery for the Kimi Code engine. The process-wide
|
|
47
|
+
* AgentFactory slot belongs to the router, which delegates each session to the
|
|
48
|
+
* engine its preset names; this class is that engine's driver, not a plugin.
|
|
49
49
|
*/
|
|
50
|
-
export declare class KimiLoop extends
|
|
51
|
-
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
52
|
-
static inject: string[];
|
|
50
|
+
export declare class KimiLoop extends HostedEngineRuntime<ResolvedConfig, KimiAgent> {
|
|
53
51
|
/** One-shot spawn capability handed to every agent, sandboxed by the subprocess seam. */
|
|
54
52
|
readonly spawn: KimiSpawnCapability;
|
|
55
53
|
constructor(ctx: Context, config: Config);
|