dsh-loop-engine-allinone 0.0.0-stage → 0.4.0
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/CHANGELOG.md +100 -0
- package/CONTRIBUTING.md +78 -0
- package/LICENSE +23 -0
- package/NOTICE +45 -0
- package/README.md +221 -2
- package/README.zh.md +89 -0
- package/SECURITY.md +39 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +2166 -0
- package/lib/index.js +11621 -0
- package/lib/invariant.js +453 -0
- package/lib/types/agent-preset-ids.d.ts +276 -0
- package/lib/types/client/LoopEngineBadge.d.ts +71 -0
- package/lib/types/client/LoopEngineComposerSelect.d.ts +126 -0
- package/lib/types/client/LoopEngineSection.d.ts +35 -0
- package/lib/types/client/engine-marks.d.ts +87 -0
- package/lib/types/client/index.d.ts +39 -0
- package/lib/types/client/locales.d.ts +182 -0
- package/lib/types/client/reload.d.ts +135 -0
- package/lib/types/client/session-engine.d.ts +508 -0
- package/lib/types/client/store.d.ts +103 -0
- package/lib/types/client/turn-status.d.ts +234 -0
- package/lib/types/client/use-session-engine.d.ts +74 -0
- package/lib/types/commands.d.ts +77 -0
- package/lib/types/compat.d.ts +25 -0
- package/lib/types/driver-acp/agent.d.ts +289 -0
- package/lib/types/driver-acp/approval.d.ts +27 -0
- package/lib/types/driver-acp/capabilities.d.ts +200 -0
- package/lib/types/driver-acp/client.d.ts +297 -0
- package/lib/types/driver-acp/executable.d.ts +53 -0
- package/lib/types/driver-acp/mapping.d.ts +76 -0
- package/lib/types/driver-acp/profile.d.ts +126 -0
- package/lib/types/driver-acp/runtime.d.ts +45 -0
- package/lib/types/driver-acp/subprocess.d.ts +19 -0
- package/lib/types/driver-acp/terminal.d.ts +98 -0
- package/lib/types/driver-acp/transport.d.ts +43 -0
- package/lib/types/driver-acp/types.d.ts +152 -0
- package/lib/types/driver-cli/capability.d.ts +42 -0
- package/lib/types/driver-cli/cli-agent.d.ts +258 -0
- package/lib/types/driver-cli/cli-loop.d.ts +46 -0
- package/lib/types/driver-cli/flatten.d.ts +35 -0
- package/lib/types/driver-cli/frames.d.ts +128 -0
- package/lib/types/driver-cli/ndjson.d.ts +106 -0
- package/lib/types/driver-cli/session-sink.d.ts +333 -0
- package/lib/types/driver-cli/stream-json.d.ts +61 -0
- package/lib/types/driver-cli/text.d.ts +66 -0
- package/lib/types/driver-cli/transport.d.ts +84 -0
- package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
- package/lib/types/driver-core/assistant-stream.d.ts +68 -0
- package/lib/types/driver-core/context-files.d.ts +62 -0
- package/lib/types/driver-core/host-servers.d.ts +119 -0
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
- package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +78 -0
- package/lib/types/driver-core/idle-child.d.ts +57 -0
- package/lib/types/driver-core/image-access.d.ts +25 -0
- package/lib/types/driver-core/inbox.d.ts +104 -0
- package/lib/types/driver-core/model-handover.d.ts +134 -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 +64 -0
- package/lib/types/driver-core/session-lifetime.d.ts +62 -0
- package/lib/types/driver-core/session-model.d.ts +128 -0
- package/lib/types/driver-core/skill-inject.d.ts +59 -0
- package/lib/types/driver-core/step-close.d.ts +49 -0
- package/lib/types/driver-core/step-images.d.ts +62 -0
- package/lib/types/driver-core/step-tools.d.ts +43 -0
- package/lib/types/driver-core/system-head.d.ts +43 -0
- package/lib/types/engine-agy/agent.d.ts +67 -0
- package/lib/types/engine-agy/frames.d.ts +90 -0
- package/lib/types/engine-agy/loop.d.ts +64 -0
- package/lib/types/engine-agy/process.d.ts +173 -0
- package/lib/types/engine-claude/agent.d.ts +186 -0
- package/lib/types/engine-claude/loop.d.ts +73 -0
- package/lib/types/engine-claude/mapping.d.ts +108 -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 +67 -0
- package/lib/types/engine-claude/types.d.ts +28 -0
- package/lib/types/engine-codex/agent.d.ts +216 -0
- package/lib/types/engine-codex/appserver/client.d.ts +80 -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 +225 -0
- package/lib/types/engine-codex/loop.d.ts +75 -0
- package/lib/types/engine-codex/model-handover.d.ts +56 -0
- package/lib/types/engine-codex/permission.d.ts +126 -0
- package/lib/types/engine-codex/skills.d.ts +28 -0
- package/lib/types/engine-codex/types.d.ts +36 -0
- package/lib/types/engine-drivers.d.ts +132 -0
- package/lib/types/engine-grok/agent.d.ts +24 -0
- package/lib/types/engine-grok/loop.d.ts +61 -0
- package/lib/types/engine-grok/process.d.ts +46 -0
- package/lib/types/engine-grok/profile.d.ts +82 -0
- package/lib/types/engine-hermes/agent.d.ts +47 -0
- package/lib/types/engine-hermes/loop.d.ts +56 -0
- package/lib/types/engine-hermes/process.d.ts +92 -0
- package/lib/types/engine-kimi/acp/client.d.ts +123 -0
- package/lib/types/engine-kimi/acp/mapping.d.ts +73 -0
- package/lib/types/engine-kimi/acp/types.d.ts +143 -0
- package/lib/types/engine-kimi/agent.d.ts +24 -0
- package/lib/types/engine-kimi/commands.d.ts +44 -0
- package/lib/types/engine-kimi/loop.d.ts +62 -0
- package/lib/types/engine-kimi/model-handover.d.ts +32 -0
- package/lib/types/engine-kimi/permission.d.ts +28 -0
- package/lib/types/engine-kimi/process.d.ts +47 -0
- package/lib/types/engine-kimi/profile.d.ts +19 -0
- package/lib/types/engine-kimi/skills.d.ts +52 -0
- package/lib/types/engine-kimi/types.d.ts +25 -0
- package/lib/types/engine-minimaxcode/agent.d.ts +24 -0
- package/lib/types/engine-minimaxcode/loop.d.ts +83 -0
- package/lib/types/engine-minimaxcode/process.d.ts +53 -0
- package/lib/types/engine-minimaxcode/profile.d.ts +72 -0
- package/lib/types/engine-of-session.d.ts +97 -0
- package/lib/types/engine-pi/agent.d.ts +230 -0
- package/lib/types/engine-pi/loop.d.ts +72 -0
- package/lib/types/engine-pi/model-handover.d.ts +84 -0
- package/lib/types/engine-pi/permission.d.ts +47 -0
- package/lib/types/engine-pi/rpc/client.d.ts +107 -0
- package/lib/types/engine-pi/rpc/mapping.d.ts +37 -0
- package/lib/types/engine-pi/rpc/types.d.ts +249 -0
- package/lib/types/engine-pi/skills.d.ts +47 -0
- package/lib/types/engine-pi/types.d.ts +36 -0
- package/lib/types/engine-qoderclicn/agent.d.ts +59 -0
- package/lib/types/engine-qoderclicn/loop.d.ts +51 -0
- package/lib/types/engine-qoderclicn/process.d.ts +113 -0
- package/lib/types/engine-registry.d.ts +584 -0
- package/lib/types/engine-remote.d.ts +192 -0
- package/lib/types/engine-surface.d.ts +36 -0
- package/lib/types/engine-zcode/agent.d.ts +50 -0
- package/lib/types/engine-zcode/loop.d.ts +47 -0
- package/lib/types/engine-zcode/process.d.ts +111 -0
- package/lib/types/index.d.ts +205 -0
- package/lib/types/invariant.d.ts +26 -0
- package/lib/types/model-selection-reset.d.ts +271 -0
- package/lib/types/namespace.d.ts +25 -0
- package/lib/types/patch-manager.d.ts +104 -0
- package/lib/types/preset.d.ts +254 -0
- package/lib/types/provider-route.d.ts +245 -0
- package/lib/types/router-loop.d.ts +458 -0
- package/lib/types/session-engine-store.d.ts +138 -0
- package/lib/types/settings.d.ts +60 -0
- package/lib/types/skills.d.ts +85 -0
- package/package.json +150 -4
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal bridge: an ACP agent's `terminal/*` calls, executed through the
|
|
3
|
+
* harness's own shell service.
|
|
4
|
+
*
|
|
5
|
+
* This is the piece that makes a tool-using ACP engine actually work. Grok Build
|
|
6
|
+
* runs every shell tool by asking the CLIENT to execute the command
|
|
7
|
+
* (`terminal/create`); a client that answers `methodNotFound` gives the model a
|
|
8
|
+
* broken tool, which is what this plugin shipped before. The bridge fixes that
|
|
9
|
+
* without touching the engine and without inventing a second execution path:
|
|
10
|
+
*
|
|
11
|
+
* - the command goes through `ctx.shell` (`ShellExecutor.resolve` +
|
|
12
|
+
* `execute`), which is the SAME service dsh's own `bash` tool uses — so the
|
|
13
|
+
* sandbox mode, the timeout policy and the output caps are dsh's, not a
|
|
14
|
+
* plugin-local imitation of them;
|
|
15
|
+
* - when the composition has no shell service the bridge REFUSES rather than
|
|
16
|
+
* falling back to a bare `child_process.spawn`: an unconfined execution
|
|
17
|
+
* channel is exactly the thing a sandbox exists to prevent, and silently
|
|
18
|
+
* becoming it would be worse than the tool failing;
|
|
19
|
+
* - the reverse-RPC approval gate still runs first, so a session whose approval
|
|
20
|
+
* policy says `ask` denies the command before it is ever spawned.
|
|
21
|
+
*
|
|
22
|
+
* ## Terminal lifecycle
|
|
23
|
+
*
|
|
24
|
+
* ACP models a terminal as a long-lived object: `create` returns an id, the agent
|
|
25
|
+
* polls `terminal/output`, awaits `terminal/wait_for_exit`, and finally calls
|
|
26
|
+
* `terminal/release`. The harness's shell handle already has that shape
|
|
27
|
+
* (`readOutput` is incremental, `done` settles at close, `kill` stops it), so the
|
|
28
|
+
* bridge is a thin adapter over one execution.
|
|
29
|
+
*
|
|
30
|
+
* @module dsh-loop-engine-allinone/driver-acp/terminal
|
|
31
|
+
*/
|
|
32
|
+
import type { AcpTerminal, AcpTerminalSpawnOptions } from './capabilities.ts';
|
|
33
|
+
/**
|
|
34
|
+
* The part of the harness shell service this bridge uses, declared structurally.
|
|
35
|
+
*
|
|
36
|
+
* The real service is `@deepseek-ai/dsh-shell`'s `ShellExecutor`. It is declared
|
|
37
|
+
* here for the same reason every other host service in this plugin is: the plugin
|
|
38
|
+
* depends on the CONTRACT it consumes, and a minimal composition may not carry the
|
|
39
|
+
* package at all.
|
|
40
|
+
*/
|
|
41
|
+
export interface ShellExecutorLike {
|
|
42
|
+
/**
|
|
43
|
+
* Fill in and cap an execution request.
|
|
44
|
+
* @param request - the caller's request.
|
|
45
|
+
* @returns the resolved spec to hand to {@link ShellExecutorLike.execute}.
|
|
46
|
+
*/
|
|
47
|
+
resolve(request: ShellExecRequestLike): ShellExecSpecLike;
|
|
48
|
+
/**
|
|
49
|
+
* Prepare and spawn one command.
|
|
50
|
+
* @param spec - a resolved spec.
|
|
51
|
+
* @returns the prepared handle.
|
|
52
|
+
*/
|
|
53
|
+
execute(spec: ShellExecSpecLike): Promise<ShellExecutionLike>;
|
|
54
|
+
}
|
|
55
|
+
/** The subset of the shell request shape this bridge fills in. */
|
|
56
|
+
export interface ShellExecRequestLike {
|
|
57
|
+
command: string;
|
|
58
|
+
workdir?: string;
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
onExpiry?: 'kill' | 'none';
|
|
61
|
+
env?: Record<string, string>;
|
|
62
|
+
}
|
|
63
|
+
/** The resolved spec, echoed so the handle's result can be read. */
|
|
64
|
+
export interface ShellExecSpecLike extends ShellExecRequestLike {
|
|
65
|
+
workdir: string;
|
|
66
|
+
timeoutMs: number;
|
|
67
|
+
onExpiry: 'kill' | 'none';
|
|
68
|
+
}
|
|
69
|
+
/** The part of one shell execution this bridge reads. */
|
|
70
|
+
export interface ShellExecutionLike {
|
|
71
|
+
readonly done: Promise<void>;
|
|
72
|
+
readonly exitCode: number | null;
|
|
73
|
+
readOutput(): {
|
|
74
|
+
readonly stdout: string;
|
|
75
|
+
readonly stderr: string;
|
|
76
|
+
};
|
|
77
|
+
kill(): boolean;
|
|
78
|
+
result(): Promise<{
|
|
79
|
+
readonly exitCode: number | null;
|
|
80
|
+
readonly stdout: {
|
|
81
|
+
readonly text: string;
|
|
82
|
+
} | string;
|
|
83
|
+
readonly stderr: {
|
|
84
|
+
readonly text: string;
|
|
85
|
+
} | string;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create the bridge an ACP client uses to answer `terminal/*`.
|
|
90
|
+
*
|
|
91
|
+
* @param shell - the harness shell service, or undefined when the composition has
|
|
92
|
+
* none (the bridge then refuses every command, see the module note).
|
|
93
|
+
* @param defaultCwd - the session's working directory, used when the agent names
|
|
94
|
+
* none of its own.
|
|
95
|
+
* @returns a spawn function for {@link AcpReverseHandlers.spawnTerminal}.
|
|
96
|
+
*/
|
|
97
|
+
export declare function createTerminalBridge(shell: ShellExecutorLike | undefined, defaultCwd: string): (command: string, options: AcpTerminalSpawnOptions) => Promise<AcpTerminal>;
|
|
98
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The process transport an ACP driver speaks over, engine-neutral.
|
|
3
|
+
*
|
|
4
|
+
* The driver hands the client a handle carrying the child's stdin/stdout/stderr
|
|
5
|
+
* (projected from the dsh subprocess seam). The three engines differ in HOW the
|
|
6
|
+
* child is launched — `kimi acp`, `grok agent stdio`, `mcode acp` — and not at
|
|
7
|
+
* all in what the driver does with it, so the transport is declared once here and
|
|
8
|
+
* each engine's `process.ts` owns its own argv and executable resolution.
|
|
9
|
+
*
|
|
10
|
+
* The names are deliberately this generic: an engine that wants its own spelling
|
|
11
|
+
* for the published surface (`KimiProcess`) aliases these rather than restating
|
|
12
|
+
* them, so a change to the seam cannot reach one engine and miss another.
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-loop-engine-allinone/driver-acp/transport
|
|
15
|
+
*/
|
|
16
|
+
import type { Readable, Writable } from 'node:stream';
|
|
17
|
+
/** The exact argv/cwd/env the driver requests for one ACP child. */
|
|
18
|
+
export interface AcpSpawnSpec {
|
|
19
|
+
/** The program plus its flags; `argv[0]` is the engine's executable. */
|
|
20
|
+
readonly argv: readonly string[];
|
|
21
|
+
/** The session's working directory, also the ACP session's `cwd`. */
|
|
22
|
+
readonly cwd: string;
|
|
23
|
+
/** The child's environment, layered over the credential-scrubbed parent. */
|
|
24
|
+
readonly env: Record<string, string>;
|
|
25
|
+
/** Cancellation: the seam escalates process-tree termination when it fires. */
|
|
26
|
+
readonly signal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
/** A spawned ACP agent process, as the client's transport needs it. */
|
|
29
|
+
export interface AcpProcess {
|
|
30
|
+
/** Child stdin (JSON-RPC request frames). */
|
|
31
|
+
readonly stdin: Writable;
|
|
32
|
+
/** Child stdout (responses and `session/update` notifications). */
|
|
33
|
+
readonly stdout: Readable;
|
|
34
|
+
/** Child stderr (diagnostics; drained and dropped). */
|
|
35
|
+
readonly stderr: Readable;
|
|
36
|
+
/** Resolves when the child closes. */
|
|
37
|
+
readonly done: Promise<unknown>;
|
|
38
|
+
/** Request process-tree termination. */
|
|
39
|
+
terminate(): void;
|
|
40
|
+
}
|
|
41
|
+
/** Spawns one ACP child over the given spec (the driver's spawn capability). */
|
|
42
|
+
export type AcpSpawnCapability = (spec: AcpSpawnSpec) => AcpProcess;
|
|
43
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Client Protocol (ACP) wire types shared by every ACP engine driver.
|
|
3
|
+
*
|
|
4
|
+
* Three engines speak ACP over stdio (JSON-RPC 2.0): Kimi Code (`kimi acp`),
|
|
5
|
+
* Grok Build (`grok agent stdio`) and MiniMax Code (`mcode acp`). The driver
|
|
6
|
+
* speaks the minimal, stable subset a normal agent flow needs: initialize,
|
|
7
|
+
* session/new, session/prompt, session/cancel, plus the `session/update`
|
|
8
|
+
* notifications carrying incremental assistant text (`agent_message_chunk`),
|
|
9
|
+
* thinking (`agent_thought_chunk`), and tool calls (`tool_call` /
|
|
10
|
+
* `tool_call_update`). Tool-approval arrives as the reverse-RPC
|
|
11
|
+
* `session/request_permission`, which the client must answer.
|
|
12
|
+
*
|
|
13
|
+
* The shapes below were READ from live frames, not from the specification, and
|
|
14
|
+
* each engine's own deltas are recorded where they matter
|
|
15
|
+
* (`docs/engine-registry.md`): kimi nests tool content
|
|
16
|
+
* as `{ type: 'content', content: { type: 'text' } }`, all three carry text
|
|
17
|
+
* chunks as `content: { type: 'text', text }`, and a model is selected through
|
|
18
|
+
* a DIFFERENT method per engine (`session/set_model` on kimi,
|
|
19
|
+
* `session/set_config_option` on grok and MiniMax Code). Content blocks tolerate
|
|
20
|
+
* block types this build does not know by ignoring them. The image block is
|
|
21
|
+
* modelled for the OUTGOING side — the bytes a dsh step delivers as an ACP
|
|
22
|
+
* image block.
|
|
23
|
+
*
|
|
24
|
+
* @module dsh-loop-engine-allinone/driver-acp/types
|
|
25
|
+
*/
|
|
26
|
+
/** A text content block in an ACP update (the observed kimi shape). */
|
|
27
|
+
export interface AcpTextContent {
|
|
28
|
+
readonly type: 'text';
|
|
29
|
+
readonly text: string;
|
|
30
|
+
}
|
|
31
|
+
/** One tool-call content block: the observed kimi `{ type: 'content', content }` nesting. */
|
|
32
|
+
export interface AcpToolContentBlock {
|
|
33
|
+
readonly type: 'content';
|
|
34
|
+
readonly content: AcpTextContent;
|
|
35
|
+
}
|
|
36
|
+
/** An image content block, as ACP/MCP spells it (base64 bytes + media type). */
|
|
37
|
+
export interface AcpImageContent {
|
|
38
|
+
readonly type: 'image';
|
|
39
|
+
/** Base64-encoded image bytes. */
|
|
40
|
+
readonly data: string;
|
|
41
|
+
/** The image's own media type. */
|
|
42
|
+
readonly mimeType: string;
|
|
43
|
+
}
|
|
44
|
+
/** A content block in a chunk/thought update; text is handled, others are ignored. */
|
|
45
|
+
export type AcpContentBlock = AcpTextContent | AcpImageContent | {
|
|
46
|
+
readonly type: 'resource_link' | 'audio' | string;
|
|
47
|
+
readonly [key: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
/** One `session/update` notification (partial; the discriminator is `sessionUpdate`). */
|
|
50
|
+
export interface AcpUpdate {
|
|
51
|
+
readonly sessionUpdate: string;
|
|
52
|
+
readonly [key: string]: unknown;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A tool-call announcement (`sessionUpdate: 'tool_call'`).
|
|
56
|
+
*
|
|
57
|
+
* Measured against kimi 0.28.x: an announcement carries the call's identity
|
|
58
|
+
* (`toolCallId`/`title`/`kind`) and an empty content card, but **never** its
|
|
59
|
+
* input — `rawInput` only ever arrives on a later `tool_call_update` (the
|
|
60
|
+
* execution-start frame).
|
|
61
|
+
*/
|
|
62
|
+
export interface AcpToolCallExt extends AcpUpdate {
|
|
63
|
+
readonly sessionUpdate: 'tool_call';
|
|
64
|
+
readonly toolCallId: string;
|
|
65
|
+
readonly title: string;
|
|
66
|
+
readonly kind: string;
|
|
67
|
+
readonly status: string;
|
|
68
|
+
readonly content?: readonly AcpToolContentBlock[];
|
|
69
|
+
/** The call's real input; absent on the announcement (see above). */
|
|
70
|
+
readonly rawInput?: unknown;
|
|
71
|
+
}
|
|
72
|
+
/** A tool-call progress/result stream (`sessionUpdate: 'tool_call_update'`). */
|
|
73
|
+
export interface AcpToolCallStreamExt extends AcpUpdate {
|
|
74
|
+
readonly sessionUpdate: 'tool_call_update';
|
|
75
|
+
readonly toolCallId: string;
|
|
76
|
+
readonly status: string;
|
|
77
|
+
readonly content?: readonly AcpToolContentBlock[];
|
|
78
|
+
/** The call's real input, first present on the execution-start frame. */
|
|
79
|
+
readonly rawInput?: unknown;
|
|
80
|
+
/** The tool's raw structured output, present once the call settles. */
|
|
81
|
+
readonly rawOutput?: unknown;
|
|
82
|
+
}
|
|
83
|
+
/** One option the agent offers for a `session/request_permission`. */
|
|
84
|
+
export interface AcpPermissionOption {
|
|
85
|
+
/** Opaque id the client sends back in `outcome.optionId` (the only field the agent reads). */
|
|
86
|
+
readonly optionId: string;
|
|
87
|
+
/** Human-readable label for the option. */
|
|
88
|
+
readonly name?: string;
|
|
89
|
+
/** Option class: `allow_once` / `allow_always` / `reject_once` / `reject_always`. */
|
|
90
|
+
readonly kind?: string;
|
|
91
|
+
}
|
|
92
|
+
/** The `session/request_permission` reverse-RPC params. */
|
|
93
|
+
export interface AcpPermissionRequest {
|
|
94
|
+
readonly sessionId?: string;
|
|
95
|
+
/** Every option the agent will accept an answer for; empty means no answerable choice. */
|
|
96
|
+
readonly options?: readonly AcpPermissionOption[];
|
|
97
|
+
/** The tool call the prompt belongs to (presentation/correlation only). */
|
|
98
|
+
readonly toolCall?: unknown;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Terminal outcome of a permission request: the option the client picked, or
|
|
102
|
+
* `cancelled` when it had no answer to give.
|
|
103
|
+
*/
|
|
104
|
+
export type AcpPermissionOutcome = {
|
|
105
|
+
readonly outcome: 'selected';
|
|
106
|
+
readonly optionId: string;
|
|
107
|
+
} | {
|
|
108
|
+
readonly outcome: 'cancelled';
|
|
109
|
+
};
|
|
110
|
+
/** The `session/request_permission` result the agent expects (ACP `RequestPermissionResponse`). */
|
|
111
|
+
export interface AcpPermissionResponse {
|
|
112
|
+
readonly outcome: AcpPermissionOutcome;
|
|
113
|
+
}
|
|
114
|
+
/** Result of a completed `session/prompt` request (opaque; the turn ended). */
|
|
115
|
+
export interface AcpPromptResult {
|
|
116
|
+
readonly [key: string]: unknown;
|
|
117
|
+
}
|
|
118
|
+
/** The id-scoped response to a request (result or error). */
|
|
119
|
+
export interface AcpResponse {
|
|
120
|
+
readonly id: number;
|
|
121
|
+
readonly result?: unknown;
|
|
122
|
+
readonly error?: {
|
|
123
|
+
readonly code: number;
|
|
124
|
+
readonly message: string;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** A JSON-RPC request/notification frame on the wire. */
|
|
128
|
+
export interface AcpFrame {
|
|
129
|
+
readonly jsonrpc: '2.0';
|
|
130
|
+
readonly id?: number;
|
|
131
|
+
readonly method?: string;
|
|
132
|
+
readonly params?: unknown;
|
|
133
|
+
readonly result?: unknown;
|
|
134
|
+
readonly error?: {
|
|
135
|
+
readonly code: number;
|
|
136
|
+
readonly message: string;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Guards toolkit: is the frame a `session/update` notification carrying an update. */
|
|
140
|
+
export declare function isUpdateFrame(frame: AcpFrame): frame is AcpFrame & {
|
|
141
|
+
readonly method: 'session/update';
|
|
142
|
+
readonly params: {
|
|
143
|
+
readonly sessionId: string;
|
|
144
|
+
readonly update: AcpUpdate;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/** Guards toolkit: is the frame a reverse-RPC request needing an approval answer. */
|
|
148
|
+
export declare function isPermissionRequestFrame(frame: AcpFrame): frame is AcpFrame & {
|
|
149
|
+
readonly id: number;
|
|
150
|
+
readonly method: 'session/request_permission';
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability guards: what a driver does when its engine cannot do what the step
|
|
3
|
+
* asked for.
|
|
4
|
+
*
|
|
5
|
+
* The plugin's rule is that an engine which cannot honour something says so,
|
|
6
|
+
* rather than accepting it and quietly doing something else ("能力不足的引擎如实
|
|
7
|
+
* 降级,不假装支持"). The registry declares each engine's capabilities
|
|
8
|
+
* (`EngineCapabilities`), and this module turns a declaration into the check a
|
|
9
|
+
* driver runs before it spends a child process on a request the engine will
|
|
10
|
+
* mistranslate.
|
|
11
|
+
*
|
|
12
|
+
* The distinction that matters: a capability the engine LACKS is a refusal with
|
|
13
|
+
* the user's next action named, not a silent drop. Dropping an image and sending
|
|
14
|
+
* the text alone would look to the user like the engine read the picture.
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-loop-engine-allinone/driver-cli/capability
|
|
17
|
+
*/
|
|
18
|
+
import type { UserMessage } from '@deepseek-ai/dsh-session';
|
|
19
|
+
import type { EngineDescriptor } from '../engine-registry.ts';
|
|
20
|
+
/**
|
|
21
|
+
* Count the image blocks this step delivers.
|
|
22
|
+
*
|
|
23
|
+
* Only THIS step's own messages, not the whole transcript: an older image in the
|
|
24
|
+
* history is already a text placeholder in the serialized prompt (that is what
|
|
25
|
+
* `serializeHistory` does), so only a fresh attachment can be lost by an engine
|
|
26
|
+
* with no image channel.
|
|
27
|
+
* @param messages - the step's message batch.
|
|
28
|
+
* @returns how many image blocks they carry.
|
|
29
|
+
*/
|
|
30
|
+
export declare function stepImageCount(messages: readonly UserMessage[]): number;
|
|
31
|
+
/**
|
|
32
|
+
* The refusal to report when a step delivers images to an engine that declared
|
|
33
|
+
* it cannot take them, or undefined when the step is fine.
|
|
34
|
+
*
|
|
35
|
+
* The message names the engine, the count, and what the user can do — which is
|
|
36
|
+
* the difference between a refusal and a puzzle.
|
|
37
|
+
* @param messages - the step's message batch.
|
|
38
|
+
* @param descriptor - the engine's registry row.
|
|
39
|
+
* @returns the refusal message, or undefined.
|
|
40
|
+
*/
|
|
41
|
+
export declare function unsupportedImagesRefusal(messages: readonly UserMessage[], descriptor: EngineDescriptor): string | undefined;
|
|
42
|
+
//# sourceMappingURL=capability.d.ts.map
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared CLI agent: the turn/step lifecycle every external-CLI engine runs,
|
|
3
|
+
* in one place.
|
|
4
|
+
*
|
|
5
|
+
* The four original drivers each carry their own copy of this machinery — about
|
|
6
|
+
* 450 near-identical lines apiece (the `jscpd:ignore` markers in
|
|
7
|
+
* `src/engine-<id>/agent.ts` mark exactly that duplication). Keeping those copies
|
|
8
|
+
* was the right call at the time: they were written and hardened one engine at a
|
|
9
|
+
* time, against protocols whose failure modes differed. It is the wrong call for
|
|
10
|
+
* engines added after this refactor, because the machinery is where the subtle
|
|
11
|
+
* bugs live (a missing `agent/error`, a step left open, an inbox claim that
|
|
12
|
+
* never wakes the driver) and a new engine should not have to rediscover them.
|
|
13
|
+
*
|
|
14
|
+
* So a NEW engine implements {@link CliHostedAgent} instead. It supplies:
|
|
15
|
+
*
|
|
16
|
+
* - {@link CliHostedAgent.runStep} — the protocol round trip, which is the only
|
|
17
|
+
* part that is actually engine-specific; and
|
|
18
|
+
* - whatever transport, argv and frame mapping that round trip needs.
|
|
19
|
+
*
|
|
20
|
+
* Everything else — the phase machine, the durable inbox, cancellation, the
|
|
21
|
+
* pre-step waterfall, skill injection, maintenance, the status transitions —
|
|
22
|
+
* is inherited and therefore identical across engines by construction.
|
|
23
|
+
*
|
|
24
|
+
* The durable-log half of a step is {@link SessionSink}, which this base owns
|
|
25
|
+
* and hands to the subclass. The split is deliberate: the base owns *when*
|
|
26
|
+
* things happen (turn opened, step started, step closed), the sink owns *what
|
|
27
|
+
* lands in the log* (messages, tool calls, results, streams), and the subclass
|
|
28
|
+
* owns *what the engine said*.
|
|
29
|
+
*
|
|
30
|
+
* @module dsh-loop-engine-allinone/driver-cli/cli-agent
|
|
31
|
+
*/
|
|
32
|
+
import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
|
|
33
|
+
import type { ImageAttachmentAccessResolver, Message } from '@deepseek-ai/dsh-llm';
|
|
34
|
+
import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
35
|
+
import type { Session, SessionId, TurnEndReason, UserMessage } from '@deepseek-ai/dsh-session';
|
|
36
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
37
|
+
import type { HostedEngineId } from '../engine-registry.ts';
|
|
38
|
+
import { DriverInbox } from '../driver-core/inbox.ts';
|
|
39
|
+
import { SessionSink, type SinkPosition } from './session-sink.ts';
|
|
40
|
+
/**
|
|
41
|
+
* Provider route label every hosted engine logs into `request/header` and into
|
|
42
|
+
* message provenance — the ONE route they share
|
|
43
|
+
* ({@link HOSTED_ROUTE_LABEL}), so the model menu carries a single `external`
|
|
44
|
+
* group instead of one per engine.
|
|
45
|
+
*/
|
|
46
|
+
export declare const CLI_PROVIDER = "external";
|
|
47
|
+
/** The ending a step reports, before the turn loop folds it. */
|
|
48
|
+
type StepEndReason = Extract<TurnEndReason, {
|
|
49
|
+
kind: 'completed' | 'max-tokens';
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* What one subclass needs to tell the base about itself.
|
|
53
|
+
*
|
|
54
|
+
* A small object rather than a long constructor parameter list, because every
|
|
55
|
+
* engine adds fields to it over time and a positional list makes each addition a
|
|
56
|
+
* breaking change for every subclass.
|
|
57
|
+
*/
|
|
58
|
+
export interface CliAgentHost {
|
|
59
|
+
/** Which engine this agent runs, for tool-name projection and diagnostics. */
|
|
60
|
+
readonly engine: HostedEngineId;
|
|
61
|
+
/**
|
|
62
|
+
* The model label logged into `request/header` and message provenance.
|
|
63
|
+
*
|
|
64
|
+
* The engine's own word for "whatever it decides" when the deployment pinned
|
|
65
|
+
* none — it is what makes the session's `(provider, model)` resolve to this
|
|
66
|
+
* plugin's placeholder route entry instead of a composite string naming a
|
|
67
|
+
* model no adapter serves.
|
|
68
|
+
*/
|
|
69
|
+
readonly model: string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* The environment entries the child is spawned with, layered over the
|
|
72
|
+
* credential-scrubbed parent environment.
|
|
73
|
+
*/
|
|
74
|
+
readonly env: Record<string, string>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The shared base for every external-CLI engine.
|
|
78
|
+
*
|
|
79
|
+
* Subclasses implement {@link runStep} only. Everything else here is the
|
|
80
|
+
* lifecycle the harness's `Agent` contract requires, written once.
|
|
81
|
+
*/
|
|
82
|
+
export declare abstract class CliHostedAgent implements Agent {
|
|
83
|
+
/**
|
|
84
|
+
* The context the agent was built on.
|
|
85
|
+
*
|
|
86
|
+
* Protected rather than private: a subclass's protocol round trip needs the
|
|
87
|
+
* same host reads the four original drivers make from this context (the
|
|
88
|
+
* session's model selection, the attachment service), and re-deriving them
|
|
89
|
+
* from the agent-scoped context would be a second, subtly different read.
|
|
90
|
+
*/
|
|
91
|
+
protected readonly loopCtx: Context;
|
|
92
|
+
/** This agent's session id. */
|
|
93
|
+
readonly id: SessionId;
|
|
94
|
+
/** The loop options the session was built with. */
|
|
95
|
+
readonly options: AgentOptions;
|
|
96
|
+
/** The durable session this agent serves. */
|
|
97
|
+
readonly session: Session;
|
|
98
|
+
/** Engine facts the base cannot answer for itself. */
|
|
99
|
+
protected readonly hostFacts: CliAgentHost;
|
|
100
|
+
/** The durable inbox, driven from the session's own log. */
|
|
101
|
+
readonly inbox: DriverInbox;
|
|
102
|
+
/** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
|
|
103
|
+
readonly scope: Scope;
|
|
104
|
+
/** The agent's own context, carrying the agent in its store. */
|
|
105
|
+
readonly ctx: Context;
|
|
106
|
+
/** The shared durable-log writer for this agent's lifecycle. */
|
|
107
|
+
protected readonly sink: SessionSink;
|
|
108
|
+
/** Resolves the read-only path behind each image block of the prompt. */
|
|
109
|
+
protected readonly imageAccess: ImageAttachmentAccessResolver;
|
|
110
|
+
private phase;
|
|
111
|
+
private activityDone;
|
|
112
|
+
private readonly dispatch;
|
|
113
|
+
private requestHeaderLogged;
|
|
114
|
+
/**
|
|
115
|
+
* Whether a wake was requested while the activity could not accept it.
|
|
116
|
+
*
|
|
117
|
+
* Held on the phase rather than here so it is reset with the phase, which is
|
|
118
|
+
* what makes "the last wake wins" true across a turn boundary.
|
|
119
|
+
*/
|
|
120
|
+
constructor(
|
|
121
|
+
/**
|
|
122
|
+
* The context the agent was built on.
|
|
123
|
+
*
|
|
124
|
+
* Protected rather than private: a subclass's protocol round trip needs the
|
|
125
|
+
* same host reads the four original drivers make from this context (the
|
|
126
|
+
* session's model selection, the attachment service), and re-deriving them
|
|
127
|
+
* from the agent-scoped context would be a second, subtly different read.
|
|
128
|
+
*/
|
|
129
|
+
loopCtx: Context,
|
|
130
|
+
/** This agent's session id. */
|
|
131
|
+
id: SessionId,
|
|
132
|
+
/** The loop options the session was built with. */
|
|
133
|
+
options: AgentOptions,
|
|
134
|
+
/** The durable session this agent serves. */
|
|
135
|
+
session: Session,
|
|
136
|
+
/** Engine facts the base cannot answer for itself. */
|
|
137
|
+
hostFacts: CliAgentHost);
|
|
138
|
+
/** The label the session's requests and messages are recorded under. */
|
|
139
|
+
protected modelLabel(): string;
|
|
140
|
+
/** The engine this agent runs. */
|
|
141
|
+
protected get engine(): HostedEngineId;
|
|
142
|
+
/** The environment entries the child is spawned with. */
|
|
143
|
+
protected get env(): Record<string, string>;
|
|
144
|
+
/**
|
|
145
|
+
* Run one step's protocol round trip.
|
|
146
|
+
*
|
|
147
|
+
* The ONLY method a subclass must implement, and the only place an engine is
|
|
148
|
+
* actually different. It is handed the position its frames belong to and must
|
|
149
|
+
* feed them to {@link CliHostedAgent.sink}; returning means the engine
|
|
150
|
+
* settled.
|
|
151
|
+
*
|
|
152
|
+
* A protocol failure (a malformed frame, a child that died mid-run) should
|
|
153
|
+
* throw — the base reports it as the turn's `error` ending. An engine that
|
|
154
|
+
* merely answered badly is NOT a protocol failure: report it through the sink
|
|
155
|
+
* (`{ kind: 'error' }`) and the base turns it into a failed turn with the
|
|
156
|
+
* engine's own words.
|
|
157
|
+
* The return value is the step's own verdict on the turn: `{ kind: 'completed' }`
|
|
158
|
+
* for an ordinary answer, `{ kind: 'max-tokens' }` when the engine hit its
|
|
159
|
+
* output ceiling, or `null` to say the sink's own verdict stands. The turn loop
|
|
160
|
+
* takes the FIRST verdict a step reports and keeps it for the turn.
|
|
161
|
+
* @param messages - the messages this step delivers.
|
|
162
|
+
* @param position - the turn/step the frames belong to.
|
|
163
|
+
* @param signal - cancellation.
|
|
164
|
+
* @returns the step's ending.
|
|
165
|
+
*/
|
|
166
|
+
protected abstract runStep(messages: readonly UserMessage[], position: SinkPosition, signal: AbortSignal): Promise<StepEndReason | null>;
|
|
167
|
+
/**
|
|
168
|
+
* Called after the sink rotates to a new step, so a subclass can reset any
|
|
169
|
+
* per-step state it holds.
|
|
170
|
+
* @param step - the step number now open.
|
|
171
|
+
*/
|
|
172
|
+
protected onStepRotated(step: number): void;
|
|
173
|
+
/**
|
|
174
|
+
* Release whatever the subclass opened for the step that just ended.
|
|
175
|
+
*
|
|
176
|
+
* Called from the step's `finally`, so it runs on success, failure and
|
|
177
|
+
* cancellation alike. A driver that spawns per step disposes its child here; a
|
|
178
|
+
* driver holding a persistent child leaves it.
|
|
179
|
+
*/
|
|
180
|
+
protected releaseStep(): void;
|
|
181
|
+
/**
|
|
182
|
+
* The transport's own teardown, when the agent's scope is unwound.
|
|
183
|
+
*
|
|
184
|
+
* Where a persistent child is finally closed. A per-step transport has
|
|
185
|
+
* nothing to do here, because {@link releaseStep} already released it.
|
|
186
|
+
*/
|
|
187
|
+
protected disposeTransport(): void;
|
|
188
|
+
get status(): AgentStatus;
|
|
189
|
+
/** Commit a phase and publish its externally visible status transition. */
|
|
190
|
+
private setPhase;
|
|
191
|
+
send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
|
|
192
|
+
/**
|
|
193
|
+
* Queue a message for the next turn and wake the driver.
|
|
194
|
+
* @param input - the user message to deliver.
|
|
195
|
+
*/
|
|
196
|
+
followup(input: UserMessage): void;
|
|
197
|
+
/**
|
|
198
|
+
* Queue a message for the running step and wake the driver.
|
|
199
|
+
* @param input - the user message to deliver.
|
|
200
|
+
*/
|
|
201
|
+
steer(input: UserMessage): void;
|
|
202
|
+
/**
|
|
203
|
+
* Queue a message for the running step without waking the driver.
|
|
204
|
+
* @param input - the user message to deliver.
|
|
205
|
+
*/
|
|
206
|
+
inject(input: UserMessage): void;
|
|
207
|
+
cancel(cause: AgentCancelCause, options?: CancelOptions): void;
|
|
208
|
+
/**
|
|
209
|
+
* Run a maintenance job while the agent is idle.
|
|
210
|
+
* @param job - the maintenance operation, receiving the phase abort signal.
|
|
211
|
+
* @returns the maintenance result.
|
|
212
|
+
*/
|
|
213
|
+
runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
214
|
+
/**
|
|
215
|
+
* Start one driver, or latch its wake behind maintenance or an aborted
|
|
216
|
+
* activity.
|
|
217
|
+
* @param wakeAfterAbort - the {@link send} classification, captured before the
|
|
218
|
+
* inbox insertion so a reentrant cancel cannot reclassify it.
|
|
219
|
+
*/
|
|
220
|
+
private wakeDriver;
|
|
221
|
+
whenIdle(): Promise<void>;
|
|
222
|
+
/** Report one failure at its live boundary, then preserve it for driver containment. */
|
|
223
|
+
private throwError;
|
|
224
|
+
private kick;
|
|
225
|
+
/** Open one turn and run its steps until the inbox stops feeding it. */
|
|
226
|
+
private turn;
|
|
227
|
+
/**
|
|
228
|
+
* Claim this step's messages and run them past the pre-step waterfall.
|
|
229
|
+
* @param target - which inbox queue to claim from.
|
|
230
|
+
* @param position - the turn/step being proposed.
|
|
231
|
+
* @returns the step's decision.
|
|
232
|
+
*/
|
|
233
|
+
private preStep;
|
|
234
|
+
/**
|
|
235
|
+
* Scan the step's user messages for `/name` skill gestures, load each matching
|
|
236
|
+
* skill, and inject the rendered content into the batch.
|
|
237
|
+
* @param messages - the step's message batch.
|
|
238
|
+
* @param signal - cancellation (aborted loads are silently dropped).
|
|
239
|
+
* @returns the original batch, or an extended one.
|
|
240
|
+
*/
|
|
241
|
+
private injectSkills;
|
|
242
|
+
/**
|
|
243
|
+
* Append the session's request-header snapshot, once per loop instance.
|
|
244
|
+
*
|
|
245
|
+
* Written by the driver rather than the sink because it describes the request
|
|
246
|
+
* the ENGINE was asked to make, and only the driver knows when the first one
|
|
247
|
+
* is about to go out.
|
|
248
|
+
*/
|
|
249
|
+
protected assertRequestHeader(): void;
|
|
250
|
+
/** Append a `tool/call` event for one call this step announced. */
|
|
251
|
+
protected appendToolCall(callId: string, name: string, args: string): void;
|
|
252
|
+
/** The engine-scope teardown hook, installed in the constructor. */
|
|
253
|
+
protected installTransportTeardown(): void;
|
|
254
|
+
/** The transcript this step should send, as the sink's caller sees it. */
|
|
255
|
+
protected history(): Message[];
|
|
256
|
+
}
|
|
257
|
+
export {};
|
|
258
|
+
//# sourceMappingURL=cli-agent.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared runtime base for every external-CLI engine.
|
|
3
|
+
*
|
|
4
|
+
* Each engine's own `loop.ts` used to carry the same four lines of ceremony:
|
|
5
|
+
* resolve the subprocess service, throw a loud engine-specific error if the
|
|
6
|
+
* profile has none, and build a spawn capability that remembers the termination
|
|
7
|
+
* grace period and whether the prompt travels on stdin. That is identical for
|
|
8
|
+
* every CLI engine, so it lives here, and an engine's runtime is left with the
|
|
9
|
+
* two things that are actually its own: how to read its composition-entry knobs,
|
|
10
|
+
* and how to construct its agent.
|
|
11
|
+
*
|
|
12
|
+
* The subprocess service is still resolved LAZILY (at construction, not at
|
|
13
|
+
* plugin apply), which is the original drivers' deliberate choice and is
|
|
14
|
+
* preserved here: an engine is built only when a session actually selects it, so
|
|
15
|
+
* a profile without the subprocess service fails THAT session loud instead of
|
|
16
|
+
* stalling the whole plugin at boot.
|
|
17
|
+
*
|
|
18
|
+
* @module dsh-loop-engine-allinone/driver-cli/cli-loop
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
21
|
+
import { HostedEngineRuntime } from '../driver-core/hosted-engine-runtime.ts';
|
|
22
|
+
import type { HostedAgent } from '../driver-core/hosted-engine-runtime.ts';
|
|
23
|
+
import type { CliSpawnCapability } from './transport.ts';
|
|
24
|
+
/**
|
|
25
|
+
* One CLI engine's creation/resume machinery.
|
|
26
|
+
*
|
|
27
|
+
* `TConfig` is the engine's own resolved configuration and `TAgent` its agent;
|
|
28
|
+
* the spawn capability is owned here so a subclass never has to thread it.
|
|
29
|
+
*/
|
|
30
|
+
export declare abstract class CliEngineRuntime<TConfig, TAgent extends HostedAgent> extends HostedEngineRuntime<TConfig, TAgent> {
|
|
31
|
+
/**
|
|
32
|
+
* The engine's spawn capability: the dsh subprocess seam with this engine's
|
|
33
|
+
* grace period and stdin decision already applied.
|
|
34
|
+
*/
|
|
35
|
+
protected readonly spawn: CliSpawnCapability;
|
|
36
|
+
/**
|
|
37
|
+
* @param ctx - the owning context.
|
|
38
|
+
* @param label - effect-label prefix, e.g. `agentLoopQoderclicn`.
|
|
39
|
+
* @param config - already-resolved engine configuration.
|
|
40
|
+
* @param engine - the engine id, for the "needs the subprocess service" diagnostic.
|
|
41
|
+
* @param graceMs - grace period for process-tree termination.
|
|
42
|
+
* @param pipeStdin - whether this engine reads its prompt from the child's stdin.
|
|
43
|
+
*/
|
|
44
|
+
constructor(ctx: Context, label: string, config: TConfig, engine: string, graceMs: number, pipeStdin: boolean);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=cli-loop.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten one engine-reported tool result to the text a durable message stores.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a helper inside the sink, for one structural
|
|
5
|
+
* reason: the protocol adapters need it (`stream-json.ts` calls it for every
|
|
6
|
+
* `tool_result` block), and the sink imports the HOST plane
|
|
7
|
+
* (`@deepseek-ai/dsh-llm`, `@deepseek-ai/dsh-session`). Keeping the flattener
|
|
8
|
+
* here means an adapter can be exercised against a real CLI — as
|
|
9
|
+
* `scripts/probe/smoke-driver.ts` does — without loading a Cordis context.
|
|
10
|
+
*
|
|
11
|
+
* The rules:
|
|
12
|
+
*
|
|
13
|
+
* - a string is the answer;
|
|
14
|
+
* - `null`/`undefined` is empty, which the caller turns into a visible
|
|
15
|
+
* placeholder rather than an unrenderable empty block;
|
|
16
|
+
* - a list of blocks is joined, reading each block's own `text`;
|
|
17
|
+
* - a block that carries no text is JSON-encoded rather than dropped, so a
|
|
18
|
+
* structured result stays inspectable in the transcript;
|
|
19
|
+
* - an object with a `content` member is unwrapped, because that is the
|
|
20
|
+
* wrapper several CLIs put around their real payload.
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-loop-engine-allinone/driver-cli/flatten
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Flatten one engine tool result to the text dsh stores.
|
|
26
|
+
*
|
|
27
|
+
* Engines report a result as a string, as a list of content blocks, or as an
|
|
28
|
+
* arbitrary JSON value; dsh's durable shape is text. Anything that is not text
|
|
29
|
+
* is JSON-encoded rather than dropped, so a structured result stays inspectable
|
|
30
|
+
* in the transcript instead of vanishing.
|
|
31
|
+
* @param result - the engine's result payload.
|
|
32
|
+
* @returns the text to store.
|
|
33
|
+
*/
|
|
34
|
+
export declare function flattenToolResult(result: unknown): string;
|
|
35
|
+
//# sourceMappingURL=flatten.d.ts.map
|