dsh-loop-engine 0.1.5-rc1 → 0.1.5-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 +88 -5
- package/README.zh.md +74 -0
- package/lib/client.js +158 -0
- package/lib/index.js +1020 -1486
- package/lib/invariant.js +6 -3
- package/lib/types/client/turn-status.d.ts +43 -0
- package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
- package/lib/types/driver-core/hosted-loop-factory.d.ts +119 -0
- package/lib/types/driver-core/ownership.d.ts +6 -6
- package/lib/types/driver-core/permission-knobs.d.ts +1 -1
- package/lib/types/driver-core/prompt.d.ts +1 -1
- package/lib/types/driver-core/skill-inject.d.ts +2 -2
- package/lib/types/engine-claude/agent.d.ts +22 -0
- package/lib/types/engine-claude/loop.d.ts +7 -56
- package/lib/types/engine-claude/mapping.d.ts +28 -3
- package/lib/types/engine-codex/agent.d.ts +46 -3
- package/lib/types/engine-codex/appserver/client.d.ts +16 -0
- package/lib/types/engine-codex/loop.d.ts +7 -56
- package/lib/types/engine-codex/permission.d.ts +100 -6
- package/lib/types/engine-codex/skills.d.ts +7 -8
- package/lib/types/engine-kimi/acp/client.d.ts +33 -3
- package/lib/types/engine-kimi/acp/mapping.d.ts +36 -7
- package/lib/types/engine-kimi/acp/types.d.ts +41 -2
- package/lib/types/engine-kimi/agent.d.ts +65 -8
- package/lib/types/engine-kimi/loop.d.ts +7 -56
- package/lib/types/engine-kimi/skills.d.ts +9 -14
- package/lib/types/engine-pi/agent.d.ts +23 -4
- package/lib/types/engine-pi/loop.d.ts +7 -56
- package/lib/types/engine-pi/permission.d.ts +16 -12
- package/lib/types/engine-pi/skills.d.ts +6 -14
- package/lib/types/patch-manager.d.ts +7 -0
- package/lib/types/provider-route.d.ts +16 -7
- package/lib/types/settings.d.ts +4 -1
- package/lib/types/skills.d.ts +6 -14
- package/package.json +1 -1
package/lib/invariant.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
var LOOP_ENGINE_IDS = ["in-process", "claude-code", "codex", "pi", "kimi"];
|
|
4
4
|
var LOOP_ENGINE_SETTINGS_SCHEMA = z.object({
|
|
5
|
-
engine: z.union(
|
|
5
|
+
engine: z.union(LOOP_ENGINE_IDS.map((id) => z.const(id))).default("in-process"),
|
|
6
6
|
showInComposer: z.boolean().default(true)
|
|
7
7
|
});
|
|
8
8
|
|
|
@@ -23,9 +23,12 @@ function renderManagedBlock(engine) {
|
|
|
23
23
|
].join("\n");
|
|
24
24
|
}
|
|
25
25
|
var BEGIN_MARKER_RE = /^# -- dsh-loop-engine managed block: (\S+) --$/m;
|
|
26
|
-
function
|
|
26
|
+
function managedBlockEngineOf(text) {
|
|
27
27
|
const engine = BEGIN_MARKER_RE.exec(text)?.[1];
|
|
28
|
-
return LOOP_ENGINE_IDS.includes(engine ?? "") ? engine :
|
|
28
|
+
return LOOP_ENGINE_IDS.includes(engine ?? "") ? engine : void 0;
|
|
29
|
+
}
|
|
30
|
+
function currentEngineOf(text) {
|
|
31
|
+
return managedBlockEngineOf(text) ?? "in-process";
|
|
29
32
|
}
|
|
30
33
|
function managedSpan(text) {
|
|
31
34
|
const begin = text.indexOf(MANAGED_BLOCK_BEGIN);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-engine styling for the chat turn-status line (the "深度求索中..." row).
|
|
3
|
+
*
|
|
4
|
+
* That row is rendered from inside the harness's ChatView and its words belong
|
|
5
|
+
* to ui-chat: the row is not a slot, and ui-chat owns the `chat` locale
|
|
6
|
+
* namespace (a second `locale.register` for the same namespace throws), so a
|
|
7
|
+
* plugin cannot change the text. What it CAN do is restyle the element, and
|
|
8
|
+
* that is all this module does — it paints an engine-specific glyph and color
|
|
9
|
+
* onto the row while a hosted engine is selected, and leaves the stock look
|
|
10
|
+
* alone otherwise.
|
|
11
|
+
*
|
|
12
|
+
* Three facts about the harness markup make that safe and specific:
|
|
13
|
+
* - the row carries exactly one class whose `[hash]_turnStatus` suffix is
|
|
14
|
+
* stable across rebuilds (the hash in front is not), so
|
|
15
|
+
* `[class$="_turnStatus"]` matches it and not the sibling clock;
|
|
16
|
+
* - its gradient paints through the `--dsw-static-deepseek-*` custom
|
|
17
|
+
* properties, so recoloring is a variable override rather than a fight
|
|
18
|
+
* over `background` and `background-clip`;
|
|
19
|
+
* - the glyph rides a `::before` pseudo-element, so the row's real text node
|
|
20
|
+
* — and the status it announces — is untouched.
|
|
21
|
+
*
|
|
22
|
+
* This sheet deliberately keeps the row animating even when the OS reports
|
|
23
|
+
* `prefers-reduced-motion: reduce` (see the re-asserted sweep below): the
|
|
24
|
+
* deployment's Windows images ship with client-area animation off, which
|
|
25
|
+
* otherwise freezes every indicator here — the sweep and the glyph alike.
|
|
26
|
+
* In-process sessions keep the stock reduced-motion behaviour.
|
|
27
|
+
*
|
|
28
|
+
* @module dsh-loop-engine/client/turn-status
|
|
29
|
+
*/
|
|
30
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
31
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
32
|
+
import type { LoopEngineState } from './store.ts';
|
|
33
|
+
/**
|
|
34
|
+
* Install the per-engine turn-status styling for the lifetime of `ctx`.
|
|
35
|
+
*
|
|
36
|
+
* Keyed off the shared controller store, so it follows a live engine switch
|
|
37
|
+
* exactly as the settings section and header chip do; the store is read rather
|
|
38
|
+
* than the settings scope so all three surfaces cannot disagree.
|
|
39
|
+
* @param ctx - the client root context.
|
|
40
|
+
* @param store - the loop-engine controller's snapshot source.
|
|
41
|
+
*/
|
|
42
|
+
export declare function installTurnStatusStyles(ctx: ClientContext, store: SnapshotStore<LoopEngineState>): void;
|
|
43
|
+
//# sourceMappingURL=turn-status.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-driven skill provider for the hosted engines whose discovery surface is
|
|
3
|
+
* "per-directory instruction files plus a skills catalog".
|
|
4
|
+
*
|
|
5
|
+
* Codex, Pi, and Kimi Code each expose the same two things through the dsh
|
|
6
|
+
* skill-injection seam: an `agents-md` candidate merging the per-directory
|
|
7
|
+
* instruction files found between the session cwd and the git root, and one
|
|
8
|
+
* candidate per `SKILL.md` catalog entry. Only the *locations*, *names*, and
|
|
9
|
+
* *precedence ranks* differ — never the algorithm — so those are the entire
|
|
10
|
+
* configuration surface here, and each engine module supplies one
|
|
11
|
+
* {@link AgentsMdProviderSpec} and a thin subclass.
|
|
12
|
+
*
|
|
13
|
+
* `.agents/skills` roots are deliberately absent from every engine's spec:
|
|
14
|
+
* dsh's own `skill-filesystem` provider already serves them through the same
|
|
15
|
+
* registry in the web profile.
|
|
16
|
+
*
|
|
17
|
+
* @module dsh-loop-engine/driver-core/agents-md-skill-provider
|
|
18
|
+
*/
|
|
19
|
+
import { type ContextFilePolicy } from './context-files.ts';
|
|
20
|
+
import type { SkillCandidate, SkillDefinition, SkillLookupOptions, SkillProvider, SkillProviderControl } from '../skills.ts';
|
|
21
|
+
/** A skills catalog: a per-ancestor project directory plus one user-level directory. */
|
|
22
|
+
export interface SkillCatalogSpec {
|
|
23
|
+
/** Directory path, relative to each ancestor of the session cwd. */
|
|
24
|
+
readonly project: readonly string[];
|
|
25
|
+
/** Rank of project catalog entries — project instructions beat project skills. */
|
|
26
|
+
readonly projectRank: number;
|
|
27
|
+
/** Directory name under the user install root. */
|
|
28
|
+
readonly userDir: string;
|
|
29
|
+
/** Rank of user catalog entries — project files win duplicate names. */
|
|
30
|
+
readonly userRank: number;
|
|
31
|
+
}
|
|
32
|
+
/** Everything that varies between the codex, pi, and kimi discovery surfaces. */
|
|
33
|
+
export interface AgentsMdProviderSpec {
|
|
34
|
+
/** Provider identity registered against the host skills service. */
|
|
35
|
+
readonly name: string;
|
|
36
|
+
/** Description of the merged `agents-md` candidate. */
|
|
37
|
+
readonly agentsMdDescription: string;
|
|
38
|
+
/** Per-directory instruction files this engine reads, and its override. */
|
|
39
|
+
readonly contextPolicy: ContextFilePolicy;
|
|
40
|
+
/** The engine's user-level install root. */
|
|
41
|
+
readonly userDir: () => string;
|
|
42
|
+
/** Rank of the project `agents-md` candidate. */
|
|
43
|
+
readonly projectRank: number;
|
|
44
|
+
/** User-level instruction file inside {@link userDir}, when the engine reads one. */
|
|
45
|
+
readonly userContext?: {
|
|
46
|
+
readonly file: string;
|
|
47
|
+
readonly rank: number;
|
|
48
|
+
};
|
|
49
|
+
/** Skill catalogs, when the engine has any. */
|
|
50
|
+
readonly skills?: SkillCatalogSpec;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Skill provider that discovers an engine's per-directory instruction files and
|
|
54
|
+
* `SKILL.md` catalogs from the locations its spec names.
|
|
55
|
+
*/
|
|
56
|
+
export declare class AgentsMdSkillProvider implements SkillProvider {
|
|
57
|
+
private readonly spec;
|
|
58
|
+
private readonly control;
|
|
59
|
+
readonly name: string;
|
|
60
|
+
constructor(spec: AgentsMdProviderSpec, control: SkillProviderControl);
|
|
61
|
+
list(options: SkillLookupOptions): Promise<readonly SkillCandidate[]>;
|
|
62
|
+
get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
|
|
63
|
+
/** One merged `agents-md` candidate for a ranked file set. */
|
|
64
|
+
private agentsCandidate;
|
|
65
|
+
/** Collect every skill in one skills directory, both nesting layouts. */
|
|
66
|
+
private collectSkillsDir;
|
|
67
|
+
/** One parsed skill as a ranked candidate. */
|
|
68
|
+
private skillCandidate;
|
|
69
|
+
/** Parse one SKILL.md file, or `undefined` when it is unreadable or invalid. */
|
|
70
|
+
private tryParse;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=agents-md-skill-provider.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared AgentFactory transaction machinery for the hosted loop engines.
|
|
3
|
+
*
|
|
4
|
+
* All four engines (Claude Code, Codex, Pi, Kimi Code) implement the harness's
|
|
5
|
+
* single AgentFactory slot the same way: prepare a driver, scope, and one
|
|
6
|
+
* memoized reverse teardown for a session; run the caller's setup under a fused
|
|
7
|
+
* abort signal; publish through both registries and announce; and on resume,
|
|
8
|
+
* own a session's write handle across the cold read, crash repair, and
|
|
9
|
+
* re-publication. None of that touches an engine protocol — the whole
|
|
10
|
+
* engine-specific surface is one call, {@link HostedLoopFactory.buildAgent}.
|
|
11
|
+
*
|
|
12
|
+
* This body mirrors the default in-process `agent-loop` factory; depending on
|
|
13
|
+
* the `dsh-agent-loop` package is forbidden (it would claim the slot this
|
|
14
|
+
* plugin exists to hand over), so the machinery is replicated here once instead
|
|
15
|
+
* of four times.
|
|
16
|
+
*
|
|
17
|
+
* Subclasses supply:
|
|
18
|
+
* - the cordis service label, which is also the prefix of every effect label
|
|
19
|
+
* (`<label>.transactions()`, `<label>.setFactory()`, `<label>.lifecycle(id)`,
|
|
20
|
+
* `<label>.resume-load(id)`) — the lifecycle label is asserted by tests, so
|
|
21
|
+
* it must stay `<label>.lifecycle(...)`;
|
|
22
|
+
* - their own `static inject` (Codex needs no `subprocess`);
|
|
23
|
+
* - {@link HostedLoopFactory.buildAgent}.
|
|
24
|
+
*
|
|
25
|
+
* @module dsh-loop-engine/driver-core/hosted-loop-factory
|
|
26
|
+
*/
|
|
27
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
28
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
29
|
+
import type { Agent, AgentFactory, AgentHandle, AgentOptions, CreateAgentOptions, ResumeAgentOptions } from '@deepseek-ai/dsh-agent';
|
|
30
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
31
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
32
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
33
|
+
/**
|
|
34
|
+
* What the transaction machinery needs of a driver beyond the harness `Agent`
|
|
35
|
+
* contract: a teardown entry point that also unwinds the driver's own scope.
|
|
36
|
+
* The concrete engines declare these; the base only spells them out so the
|
|
37
|
+
* shared body can call them generically.
|
|
38
|
+
*/
|
|
39
|
+
export interface HostedAgent extends Agent {
|
|
40
|
+
/** Stop the machine with the given cause, without waiting for it to settle. */
|
|
41
|
+
cancel(cause: Parameters<Agent['cancel']>[0]): void;
|
|
42
|
+
/** Resolves when the machine has no work in flight. */
|
|
43
|
+
whenIdle(): Promise<void>;
|
|
44
|
+
/** The driver's own resource scope, unwound after the machine settles. */
|
|
45
|
+
readonly scope: Scope;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Concrete AgentFactory base for one hosted engine. Creation and resume follow
|
|
49
|
+
* the registry factory contract and the shared publication transaction:
|
|
50
|
+
* prepare, run setup, then publish through both registries, announce, and emit
|
|
51
|
+
* `agent/session-start`.
|
|
52
|
+
*/
|
|
53
|
+
export declare abstract class HostedLoopFactory<TConfig, TAgent extends HostedAgent> extends Service implements AgentFactory {
|
|
54
|
+
/** Validated configuration owned by the loop plugin. */
|
|
55
|
+
readonly config: TConfig;
|
|
56
|
+
private readonly ownership;
|
|
57
|
+
/** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */
|
|
58
|
+
protected readonly runtime: {
|
|
59
|
+
ctx: Context;
|
|
60
|
+
};
|
|
61
|
+
/** Cordis service name, also the prefix of every effect label. */
|
|
62
|
+
private readonly label;
|
|
63
|
+
constructor(ctx: Context, label: string, config: TConfig);
|
|
64
|
+
/**
|
|
65
|
+
* Construct this engine's driver for one prepared session. Called once per
|
|
66
|
+
* create or resume, after the session exists and before setup runs; the
|
|
67
|
+
* hook is the engine's entire protocol surface.
|
|
68
|
+
*/
|
|
69
|
+
protected abstract buildAgent(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session): TAgent;
|
|
70
|
+
/**
|
|
71
|
+
* Construct the driver, scope, and one memoized reverse teardown for a new
|
|
72
|
+
* agent. The teardown is registered with the factory and the owner fiber
|
|
73
|
+
* BEFORE publication, so a mid-setup unload rolls everything back; `signal`
|
|
74
|
+
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
75
|
+
*/
|
|
76
|
+
private prepare;
|
|
77
|
+
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
78
|
+
private setupAndPublish;
|
|
79
|
+
/**
|
|
80
|
+
* Create an agent and session under one caller-supplied identity, owned by
|
|
81
|
+
* the accessing fiber. When a persistence backend is mounted, the session's
|
|
82
|
+
* durable identity is stored before publication.
|
|
83
|
+
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
84
|
+
* @param options - identities, optional live parent, session seed/metadata, loop options, setup, and cancellation.
|
|
85
|
+
* @returns the published handle.
|
|
86
|
+
*/
|
|
87
|
+
createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
|
|
88
|
+
/**
|
|
89
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
90
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
91
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
92
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
93
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
94
|
+
* created again.
|
|
95
|
+
* @param session - the unpublished session to store.
|
|
96
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
97
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
98
|
+
*/
|
|
99
|
+
private createStoredSession;
|
|
100
|
+
/**
|
|
101
|
+
* Durably store the session events appended since the last stored cursor.
|
|
102
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
103
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
104
|
+
* through the handle before live events start routing into it.
|
|
105
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
106
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
107
|
+
*/
|
|
108
|
+
private appendUnstoredSuffix;
|
|
109
|
+
/**
|
|
110
|
+
* Resume an owned agent from the configured persistence service.
|
|
111
|
+
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
112
|
+
* @param options - persisted identity, optional live parent, loop options, setup, and cancellation.
|
|
113
|
+
* @returns the published handle.
|
|
114
|
+
*/
|
|
115
|
+
resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
|
|
116
|
+
/** Resume through an explicit persistence service. */
|
|
117
|
+
private resumeWith;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=hosted-loop-factory.d.ts.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared factory ownership and abort-race machinery for the hosted engines.
|
|
3
|
-
*
|
|
4
|
-
* one factory owns the AgentFactory slot, every live
|
|
5
|
-
* tracked until it settles, and setup awaits are raced
|
|
6
|
-
* signal. These helpers are engine-free — they only
|
|
7
|
-
* the session id type, and an AbortController — so the
|
|
8
|
-
* them verbatim.
|
|
3
|
+
* All four loop drivers (Claude Code, Codex, Pi, Kimi Code) run the same
|
|
4
|
+
* lifecycle: exactly one factory owns the AgentFactory slot, every live
|
|
5
|
+
* agent's teardown is tracked until it settles, and setup awaits are raced
|
|
6
|
+
* against a fused abort signal. These helpers are engine-free — they only
|
|
7
|
+
* touch the fiber state, the session id type, and an AbortController — so the
|
|
8
|
+
* loop modules share them verbatim.
|
|
9
9
|
*
|
|
10
10
|
* @module dsh-loop-engine/driver-core/ownership
|
|
11
11
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reading the dsh session's durable permission knobs from the session log.
|
|
3
|
-
*
|
|
3
|
+
* Every hosted driver folds the same `sandbox/mode` and
|
|
4
4
|
* `approval/policy` events (pinned at creation, re-recorded on every switch)
|
|
5
5
|
* into per-query permission decisions; the knob readers are engine-free.
|
|
6
6
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serialization of the durable session history into the prompt text of one
|
|
3
|
-
* hosted-engine query.
|
|
3
|
+
* hosted-engine query. Every hosted driver builds its
|
|
4
4
|
* per-step input from the durable session log: the transcript is the log's
|
|
5
5
|
* exact projection, so a later replay of the same log derives the identical
|
|
6
6
|
* prompt (Model-visible ⟺ logged bridge).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Skill-injection helpers shared by the hosted engine drivers.
|
|
3
|
-
*
|
|
2
|
+
* Skill-injection helpers shared by the hosted engine drivers. Each hosted
|
|
3
|
+
* engine's agent replicates the dsh `/name` skill gesture scan and the
|
|
4
4
|
* XML `<skill_content>` rendering that the in-process engine's dsh-tool-skill
|
|
5
5
|
* handler would otherwise provide — their agent contexts do not descend from
|
|
6
6
|
* the agent-preset chain. These helpers are pure: they take user messages or a
|
|
@@ -33,6 +33,17 @@ export declare class ClaudeCodeAgent implements Agent {
|
|
|
33
33
|
private requestHeaderLogged;
|
|
34
34
|
/** Agent-lifecycle-local counter naming each streamed attempt. */
|
|
35
35
|
private streamAttempts;
|
|
36
|
+
/**
|
|
37
|
+
* Tool results logged into the currently open step. A result means the
|
|
38
|
+
* segment that requested the call is finished, so the next assistant content
|
|
39
|
+
* opens the next step (see {@link beginSegment}).
|
|
40
|
+
*/
|
|
41
|
+
private stepSettledTools;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the current query has rotated into a second step. The query-total
|
|
44
|
+
* usage record is only meaningful while one step holds the whole query.
|
|
45
|
+
*/
|
|
46
|
+
private rotated;
|
|
36
47
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
37
48
|
get status(): AgentStatus;
|
|
38
49
|
/** Commit a phase and publish its externally visible status transition. */
|
|
@@ -96,6 +107,17 @@ export declare class ClaudeCodeAgent implements Agent {
|
|
|
96
107
|
private queryPermission;
|
|
97
108
|
/** Open one turn before claiming its first proposed step. */
|
|
98
109
|
private turn;
|
|
110
|
+
/**
|
|
111
|
+
* Rotate to the next step when the segment that ran a tool has finished, so
|
|
112
|
+
* each assistant segment lands in its own step.
|
|
113
|
+
*
|
|
114
|
+
* Called as new assistant content begins. A step holding a settled tool
|
|
115
|
+
* result means the previous segment is complete, and the content about to be
|
|
116
|
+
* written belongs to the next one. Rotating here (rather than when a call is
|
|
117
|
+
* announced) keeps calls announced together — one model turn — in one step.
|
|
118
|
+
* @param phase - the running phase carrying the open step's position.
|
|
119
|
+
*/
|
|
120
|
+
private beginSegment;
|
|
99
121
|
/** Model label recorded in the request header for one lifecycle. */
|
|
100
122
|
private modelLabel;
|
|
101
123
|
/** Append the request header snapshot once per loop instance. */
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-loop-engine/engine-claude
|
|
9
9
|
*/
|
|
10
|
-
import { Service } from '@deepseek-ai/cordis';
|
|
11
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
11
|
import z from '@deepseek-ai/schemastery';
|
|
13
|
-
import type {
|
|
12
|
+
import type { AgentOptions } from '@deepseek-ai/dsh-agent';
|
|
13
|
+
import type { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
14
|
+
import { ClaudeCodeAgent } from './agent.ts';
|
|
14
15
|
import type { ClaudeCodePermissionMode, ResolvedConfig } from './types.ts';
|
|
16
|
+
import { HostedLoopFactory } from '../driver-core/hosted-loop-factory.ts';
|
|
15
17
|
/** Deployment-selectable non-interactive Claude Code permission modes. */
|
|
16
18
|
export declare const CLAUDE_CODE_PERMISSION_MODES: readonly ClaudeCodePermissionMode[];
|
|
17
19
|
/** Deployment-owned configuration for the Claude Code loop plugin. */
|
|
@@ -50,62 +52,11 @@ declare module '@deepseek-ai/cordis' {
|
|
|
50
52
|
* transaction: prepare, run setup, then publish through both registries,
|
|
51
53
|
* announce, and emit `agent/session-start`.
|
|
52
54
|
*/
|
|
53
|
-
export declare class ClaudeCodeLoop extends
|
|
55
|
+
export declare class ClaudeCodeLoop extends HostedLoopFactory<ResolvedConfig, ClaudeCodeAgent> {
|
|
54
56
|
/** Services the loop resolves through its own fiber; blessed identically to the package-level entry inject. */
|
|
55
57
|
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
58
|
constructor(ctx: Context, config: Config);
|
|
62
|
-
/**
|
|
63
|
-
|
|
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;
|
|
59
|
+
/** Construct the Claude Code driver for one prepared session. */
|
|
60
|
+
protected buildAgent(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session): ClaudeCodeAgent;
|
|
110
61
|
}
|
|
111
62
|
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-loop-engine/engine-claude/mapping
|
|
8
8
|
*/
|
|
9
|
-
import type { BetaMessage, BetaRawMessageStreamEvent
|
|
9
|
+
import type { BetaMessage, BetaRawMessageStreamEvent } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs';
|
|
10
10
|
import type { MessageParam } from '@anthropic-ai/sdk/resources';
|
|
11
11
|
import { ToolCallId, type ContentBlock, type StreamChunk, type TokenUsage, type ToolResultMessage } from '@deepseek-ai/dsh-llm';
|
|
12
12
|
/** One tool invocation surfaced from a Claude Code assistant message. */
|
|
@@ -54,13 +54,38 @@ export declare function mapAssistantMessage(message: BetaMessage): MappedAssista
|
|
|
54
54
|
* @returns the mapped tool-result messages, in block order.
|
|
55
55
|
*/
|
|
56
56
|
export declare function mapToolResults(message: MessageParam): ToolResultMessage[];
|
|
57
|
+
/**
|
|
58
|
+
* The counters {@link mapUsage} reads. The SDK reports these on an assistant
|
|
59
|
+
* message's `usage`, on a stream `message_delta`'s narrower usage, and on the
|
|
60
|
+
* query `result`'s totals, so the parameter is the common shape rather than any
|
|
61
|
+
* one of those types.
|
|
62
|
+
*/
|
|
63
|
+
export interface ReportedUsage {
|
|
64
|
+
readonly input_tokens: number | null;
|
|
65
|
+
readonly output_tokens: number | null;
|
|
66
|
+
readonly cache_read_input_tokens?: number | null;
|
|
67
|
+
readonly cache_creation_input_tokens?: number | null;
|
|
68
|
+
}
|
|
57
69
|
/**
|
|
58
70
|
* Translate SDK token accounting into the dsh token-usage shape. Cache
|
|
59
71
|
* breakpoints are optional; absent or null SDK counters stay absent.
|
|
60
|
-
* @param usage - SDK-reported usage for one assistant message.
|
|
72
|
+
* @param usage - SDK-reported usage for one assistant message, stream delta, or query result.
|
|
61
73
|
* @returns dsh token accounting, omitting absent optional counters.
|
|
62
74
|
*/
|
|
63
|
-
export declare function mapUsage(usage:
|
|
75
|
+
export declare function mapUsage(usage: ReportedUsage): TokenUsage;
|
|
76
|
+
/**
|
|
77
|
+
* Keep a usage sample only when it accounts for something.
|
|
78
|
+
*
|
|
79
|
+
* The SDK zero-fills `usage` on streamed assistant messages it cannot attribute
|
|
80
|
+
* (gateway-fronted models report the real counters on the stream's
|
|
81
|
+
* `message_delta` and the query's `result` instead). A sample with every bucket
|
|
82
|
+
* at zero is that placeholder, not a measurement: keeping it would both show an
|
|
83
|
+
* empty usage row and — because the token meter trusts a present sample over
|
|
84
|
+
* the stream — hide the real one.
|
|
85
|
+
* @param usage - a mapped usage sample, or undefined.
|
|
86
|
+
* @returns the sample when it is non-empty, otherwise undefined.
|
|
87
|
+
*/
|
|
88
|
+
export declare function meaningfulUsage(usage: TokenUsage | undefined): TokenUsage | undefined;
|
|
64
89
|
/** Per-block-index tool-call identity captured at `content_block_start`, reused by `input_json_delta`. */
|
|
65
90
|
export interface StreamToolCall {
|
|
66
91
|
readonly callId: ToolCallId;
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* the source of truth and the thread input is a pure serialization of it.
|
|
6
6
|
* The app-server streams token-level deltas via `item/agentMessage/delta` and
|
|
7
7
|
* `item/reasoning/summaryTextDelta`, so the visible partial paints
|
|
8
|
-
* progressively as the model generates — not all at once at the end.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* progressively as the model generates — not all at once at the end. Native
|
|
9
|
+
* approval requests (the `item/…/requestApproval` family) are server-initiated
|
|
10
|
+
* requests, not notifications: the client answers them through the dsh approval
|
|
11
|
+
* seam (`agent/requestPermission` → `ctx.approval`), while the thread still
|
|
12
|
+
* starts with a declarative `sandboxMode`/`approvalPolicy` stance.
|
|
11
13
|
*
|
|
12
14
|
* @module dsh-loop-engine/engine-codex/agent
|
|
13
15
|
*/
|
|
@@ -38,11 +40,52 @@ export declare class CodexAgent implements Agent {
|
|
|
38
40
|
private requestHeaderLogged;
|
|
39
41
|
/** Agent-lifecycle-local counter naming each streamed attempt. */
|
|
40
42
|
private streamAttempts;
|
|
43
|
+
/**
|
|
44
|
+
* Tool results logged into the currently open step. A result means the
|
|
45
|
+
* segment that requested the call is finished, so the next assistant content
|
|
46
|
+
* opens the next step (see {@link beginSegment}).
|
|
47
|
+
*/
|
|
48
|
+
private stepSettledTools;
|
|
49
|
+
/**
|
|
50
|
+
* Rotate to the next step when the segment that ran a tool has finished, so
|
|
51
|
+
* each assistant segment lands in its own step.
|
|
52
|
+
*
|
|
53
|
+
* Called as new assistant content begins. A step holding a settled tool
|
|
54
|
+
* result means the previous segment is complete, and the content about to be
|
|
55
|
+
* written belongs to the next one. Rotating here (rather than when a call is
|
|
56
|
+
* announced) keeps calls announced together — one model turn — in one step.
|
|
57
|
+
* @param phase - the running phase carrying the open step's position.
|
|
58
|
+
*/
|
|
59
|
+
private beginSegment;
|
|
41
60
|
/** Lazily created app-server client, reused across steps and released on scope teardown. */
|
|
42
61
|
private appServer;
|
|
43
62
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
44
63
|
/** Return the cached app-server client, spawning one on first use or after a dead process. */
|
|
45
64
|
private appServerClient;
|
|
65
|
+
/**
|
|
66
|
+
* Answer one server-initiated interaction. Approvals go through the dsh
|
|
67
|
+
* approval seam; a `request_user_input` question goes to the user-questions
|
|
68
|
+
* seam (both fail closed when their seam is absent), and an MCP elicitation is
|
|
69
|
+
* declined outright. Anything else is a protocol error.
|
|
70
|
+
* @param method - the server request method.
|
|
71
|
+
* @param params - the server request params.
|
|
72
|
+
* @returns the JSON-RPC outcome to send back.
|
|
73
|
+
*/
|
|
74
|
+
private answerRequest;
|
|
75
|
+
/** Resolve one native Codex approval request through the dsh approval seam. */
|
|
76
|
+
private answerApproval;
|
|
77
|
+
/**
|
|
78
|
+
* Put one `request_user_input` question to the human through the dsh
|
|
79
|
+
* user-questions seam. The seam itself fails closed (`NO_PROVIDER`) when no
|
|
80
|
+
* answerer is composed, so a refusal degrades to "no answers given" and the
|
|
81
|
+
* turn continues; asking is never silently skipped, and the degradation is
|
|
82
|
+
* logged rather than swallowed.
|
|
83
|
+
* @param params - the request params carrying the questions.
|
|
84
|
+
* @returns the response payload (or the empty answer when nobody answered).
|
|
85
|
+
*/
|
|
86
|
+
private answerUserInput;
|
|
87
|
+
/** Ask the dsh approval seam; fail closed to a denial when it is absent. */
|
|
88
|
+
private requestApproval;
|
|
46
89
|
get status(): AgentStatus;
|
|
47
90
|
/** Commit a phase and publish its externally visible status transition. */
|
|
48
91
|
private setPhase;
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
import type { InitializeResult, ThreadResumeParams, ThreadStartParams, ThreadStartResult, TurnInterruptParams, TurnStartParams, TurnStartResult } from './types.ts';
|
|
9
9
|
/** Callback for receiving server notifications. */
|
|
10
10
|
export type NotificationHandler = (method: string, params: unknown) => void;
|
|
11
|
+
/** A JSON-RPC reply the client writes back for one inbound server request. */
|
|
12
|
+
export type RequestOutcome = {
|
|
13
|
+
readonly result: unknown;
|
|
14
|
+
} | {
|
|
15
|
+
readonly error: {
|
|
16
|
+
readonly code: number;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/** Callback answering one server-initiated JSON-RPC request. */
|
|
21
|
+
export type RequestHandler = (method: string, params: unknown, id: number | string) => RequestOutcome | Promise<RequestOutcome>;
|
|
11
22
|
/** Callback for receiving raw stderr lines from the server process. */
|
|
12
23
|
export type StderrHandler = (line: string) => void;
|
|
13
24
|
/** JSON-RPC client for the codex app-server. */
|
|
@@ -17,6 +28,7 @@ export declare class AppServerClient {
|
|
|
17
28
|
private reqId;
|
|
18
29
|
private pending;
|
|
19
30
|
private notificationHandler;
|
|
31
|
+
private requestHandler;
|
|
20
32
|
private stderrHandler;
|
|
21
33
|
private disposed;
|
|
22
34
|
/** Whether this client was disposed or its server process exited. */
|
|
@@ -27,6 +39,8 @@ export declare class AppServerClient {
|
|
|
27
39
|
static create(): Promise<AppServerClient>;
|
|
28
40
|
/** Set the notification handler for streaming events. */
|
|
29
41
|
onNotification(handler: NotificationHandler): void;
|
|
42
|
+
/** Set the handler for server-initiated requests (e.g. approvals). */
|
|
43
|
+
onRequest(handler: RequestHandler): void;
|
|
30
44
|
/** Set the stderr handler for server log lines. */
|
|
31
45
|
onStderr(handler: StderrHandler): void;
|
|
32
46
|
/** Send the initialize handshake. */
|
|
@@ -45,5 +59,7 @@ export declare class AppServerClient {
|
|
|
45
59
|
private request;
|
|
46
60
|
/** Handle one line of stdout from the server. */
|
|
47
61
|
private handleLine;
|
|
62
|
+
/** Resolve one inbound server request and write the JSON-RPC reply to stdin. */
|
|
63
|
+
private answerRequest;
|
|
48
64
|
}
|
|
49
65
|
//# sourceMappingURL=client.d.ts.map
|