@agentic-kit/dsh 0.2.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/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
4
+ Copyright (c) 2025 Constructive <developers@constructive.io>
5
+ Copyright (c) 2020-present, Interweb, Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @agentic-kit/dsh
2
+
3
+ <p align="center" width="100%">
4
+ <img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
5
+ </p>
6
+
7
+ <p align="center" width="100%">
8
+ <a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
9
+ <img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
10
+ </a>
11
+ <a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
12
+ <a href="https://www.npmjs.com/package/@agentic-kit/dsh"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=agentic%2Fdsh%2Fpackage.json"/></a>
13
+ </p>
14
+
15
+ The **DeepSeek Harness (dsh)** adapter — the sibling of [`@agentic-kit/pi`](https://www.npmjs.com/package/@agentic-kit/pi), and the reason the harness contracts are neutral. The same 18 [`@agentic-kit/db-tools`](https://www.npmjs.com/package/@agentic-kit/db-tools), the same confirm gate and the same run-log vocabulary, bound to a second harness without any of them changing.
16
+
17
+ ```
18
+ neutral contracts: HarnessTool ConfirmGate TranscriptEvent
19
+ │ │ ▲
20
+ @agentic-kit/pi ────┼───────────────┼─────────────────┤ pi's ToolDefinition, tool_call, pi session
21
+ @agentic-kit/dsh ───┴───────────────┴─────────────────┘ dsh's ToolDefinition, tools/pre-execute, dsh events
22
+ ```
23
+
24
+ ```bash
25
+ npm install @agentic-kit/dsh
26
+ ```
27
+
28
+ ## What's inside
29
+
30
+ - **`toDshTool` / `toDshTools`** — a neutral `HarnessTool` as dsh's `ToolDefinition`: parameters in dsh's JSON Schema subset, a declared canonical output whose value *is* the neutral `HarnessToolResult` (so dsh's durable log keeps the tool's structured `details`), and the caller's `AbortSignal` threaded to the tool.
31
+ - **`createConstructivePlugin`** — the tools as a dsh plugin, with Constructive's confirm gate on dsh's `tools/pre-execute` waterfall. A gated call asks dsh's approval service; a host with no approval service composed gets a `deny`, never a silent mutation.
32
+ - **`toDshParameters` / `convertDshParameters`** — zod → dsh's subset (`type`, `properties`, `required`, `items`, `oneOf`, `enum`, `const`, boolean `additionalProperties`). Constraints outside it are dropped from the *model-facing* schema and reported in `dropped`; they still hold, because a bound tool parses arguments with its own zod schema before the body runs. Structure that cannot degrade safely — a non-object root, a `$ref` — throws.
33
+ - **`dshTranscriptReader`** — dsh's session-event log as neutral `TranscriptEvent`s, so a Constructive surface renders a dsh run through the same projectors as a pi one. Import it from `@agentic-kit/dsh/transcript` in a browser: that entry point has no node dependency.
34
+
35
+ ## Usage
36
+
37
+ ```ts
38
+ import { configureHost } from '@agentic-kit/db-tools';
39
+ import { createConstructivePlugin } from '@agentic-kit/dsh';
40
+
41
+ configureHost(host);
42
+
43
+ export default createConstructivePlugin({ cwd: () => projectDir });
44
+ ```
45
+
46
+ Reading a dsh run back:
47
+
48
+ ```ts
49
+ import { dshTranscriptReader } from '@agentic-kit/dsh/transcript';
50
+ import { TranscriptReaderRegistry } from '@agentic-kit/run-log';
51
+
52
+ const readers = new TranscriptReaderRegistry([piTranscriptReader, dshTranscriptReader]);
53
+ const events = readers.require(record.transcriptFormat).toEvents(record.entry);
54
+ ```
55
+
56
+ ## No dsh dependency
57
+
58
+ dsh is a developer preview whose packages promise breaking changes, and whose published rc's trail its own source. So this adapter binds to the *shape* dsh asks for — a tool definition, a tool run context, a content block, a plugin's `apply` — declared structurally in `dsh-types.ts`, and has no `@deepseek-ai/*` dependency. A host on any rc registers the plugin; dsh's ESM-only graph never reaches a CJS consumer of this package.
59
+
60
+ ---
61
+
62
+ ## Education and Tutorials
63
+
64
+ 1. 🚀 [Quickstart: Getting Up and Running](https://constructive.io/learn/quickstart)
65
+ Get started with modular databases in minutes. Install prerequisites and deploy your first module.
66
+
67
+ 2. 📦 [Modular PostgreSQL Development with Database Packages](https://constructive.io/learn/modular-postgres)
68
+ Learn to organize PostgreSQL projects with pgpm workspaces and reusable database modules.
69
+
70
+ 3. ✏️ [Authoring Database Changes](https://constructive.io/learn/authoring-database-changes)
71
+ Master the workflow for adding, organizing, and managing database changes with pgpm.
72
+
73
+ 4. 🧪 [End-to-End PostgreSQL Testing with TypeScript](https://constructive.io/learn/e2e-postgres-testing)
74
+ Master end-to-end PostgreSQL testing with ephemeral databases, RLS testing, and CI/CD automation.
75
+
76
+ 5. ⚡ [Supabase Testing](https://constructive.io/learn/supabase)
77
+ Use TypeScript-first tools to test Supabase projects with realistic RLS, policies, and auth contexts.
78
+
79
+ 6. 💧 [Drizzle ORM Testing](https://constructive.io/learn/drizzle-testing)
80
+ Run full-stack tests with Drizzle ORM, including database setup, teardown, and RLS enforcement.
81
+
82
+ 7. 🔧 [Troubleshooting](https://constructive.io/learn/troubleshooting)
83
+ Common issues and solutions for pgpm, PostgreSQL, and testing.
84
+
85
+ ## Related Constructive Tooling
86
+
87
+ ### 📦 Package Management
88
+
89
+ * [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
90
+
91
+ ### 🧪 Testing
92
+
93
+ * [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
94
+ * [pglite-test](https://github.com/constructive-io/constructive/tree/main/postgres/pglite-test): **🪶 Drop-in pgsql-test replacement backed by PGlite** — in-process Postgres, no server required, instance-per-suite isolation.
95
+ * [pgsql-seed](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-seed): **🌱 PostgreSQL seeding utilities** for CSV, JSON, SQL data loading, and pgpm deployment.
96
+ * [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
97
+ * [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
98
+ * [pg-query-context](https://github.com/constructive-io/constructive/tree/main/postgres/pg-query-context): **🔒 Session context injection** to add session-local context (e.g., `SET LOCAL`) into queries—ideal for setting `role`, `jwt.claims`, and other session settings.
99
+
100
+ ### 🧠 Parsing & AST
101
+
102
+ * [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
103
+ * [libpg-query-node](https://www.npmjs.com/package/libpg-query): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
104
+ * [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
105
+ * [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **🏷️ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
106
+ * [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
107
+ * [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.
108
+
109
+ ### 📚 Documentation & Skills
110
+
111
+ * [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.
112
+
113
+ Install skills for AI coding agents:
114
+
115
+ ```bash
116
+ # All platform skills (security, blueprints, codegen, billing, etc.)
117
+ npx skills add constructive-io/constructive-skills
118
+
119
+ # Individual repo skills (pgpm, testing, CLI, search, etc.)
120
+ npx skills add https://github.com/constructive-io/constructive --skill pgpm
121
+ npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
122
+ ```
123
+
124
+ ## Credits
125
+
126
+ **🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
127
+
128
+ ## Disclaimer
129
+
130
+ AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
131
+
132
+ No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
package/dsh-tool.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { AnyHarnessTool, HarnessTool } from '@agentic-kit/harness';
2
+ import type { z } from 'zod';
3
+ import type { DshToolDefinition } from './dsh-types';
4
+ /** What a bound tool needs that a `HarnessTool` does not state. */
5
+ export interface ToDshToolOptions {
6
+ /**
7
+ * The working directory a tool resolves project context from. dsh keeps the
8
+ * directory on the session header and its filesystem service rather than on a
9
+ * tool's execution context, so the adapter is told once instead of guessing
10
+ * per call. Defaults to `process.cwd()`.
11
+ */
12
+ cwd?: () => string;
13
+ }
14
+ /**
15
+ * Bind a neutral `HarnessTool` to dsh's `ToolDefinition`.
16
+ *
17
+ * The sibling of `toPiTool` — same tools, a second harness's shape. dsh asks
18
+ * for three things pi does not: parameters in its JSON Schema subset (see
19
+ * `./schema`), a *declared canonical output* with a pure projection from that
20
+ * value to model-facing content, and a body that returns the value rather than
21
+ * the content. So the neutral `HarnessToolResult` becomes the canonical value
22
+ * verbatim and `output.render` projects its `content` — which means dsh's
23
+ * durable log keeps the tool's structured `details`, and a Constructive
24
+ * renderer reads the same detail out of a dsh transcript as out of a pi one.
25
+ *
26
+ * Arguments are parsed with the tool's own zod schema before the body runs:
27
+ * dsh validates against the narrowed subset schema, and this restores every
28
+ * constraint that narrowing dropped.
29
+ */
30
+ export declare function toDshTool<TParams extends z.ZodType, TDetails>(tool: HarnessTool<TParams, TDetails>, options?: ToDshToolOptions): DshToolDefinition;
31
+ /** Bind a whole tool set, in registration order. */
32
+ export declare function toDshTools(tools: readonly AnyHarnessTool[], options?: ToDshToolOptions): DshToolDefinition[];
package/dsh-tool.js ADDED
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toDshTool = toDshTool;
4
+ exports.toDshTools = toDshTools;
5
+ const schema_1 = require("./schema");
6
+ /** The canonical value a bound tool returns: the neutral result, as JSON. */
7
+ const OUTPUT_SCHEMA = {
8
+ type: 'object',
9
+ properties: {
10
+ content: { type: 'array', items: { type: 'object' } },
11
+ details: {},
12
+ terminate: { type: 'boolean' }
13
+ },
14
+ required: ['content']
15
+ };
16
+ /**
17
+ * Bind a neutral `HarnessTool` to dsh's `ToolDefinition`.
18
+ *
19
+ * The sibling of `toPiTool` — same tools, a second harness's shape. dsh asks
20
+ * for three things pi does not: parameters in its JSON Schema subset (see
21
+ * `./schema`), a *declared canonical output* with a pure projection from that
22
+ * value to model-facing content, and a body that returns the value rather than
23
+ * the content. So the neutral `HarnessToolResult` becomes the canonical value
24
+ * verbatim and `output.render` projects its `content` — which means dsh's
25
+ * durable log keeps the tool's structured `details`, and a Constructive
26
+ * renderer reads the same detail out of a dsh transcript as out of a pi one.
27
+ *
28
+ * Arguments are parsed with the tool's own zod schema before the body runs:
29
+ * dsh validates against the narrowed subset schema, and this restores every
30
+ * constraint that narrowing dropped.
31
+ */
32
+ function toDshTool(tool, options = {}) {
33
+ const cwd = options.cwd ?? (() => process.cwd());
34
+ return {
35
+ name: tool.name,
36
+ description: describe(tool),
37
+ parameters: (0, schema_1.toDshParameters)(tool.parameters),
38
+ output: {
39
+ schema: OUTPUT_SCHEMA,
40
+ render: (_args, value) => renderContent(value)
41
+ },
42
+ async execute(args, exec) {
43
+ const params = tool.parameters.parse(args);
44
+ const result = await tool.execute(params, {
45
+ cwd: cwd(),
46
+ signal: exec.signal
47
+ });
48
+ return {
49
+ content: result.content,
50
+ details: result.details === undefined ? null : result.details,
51
+ ...(result.terminate === undefined ? {} : { terminate: result.terminate })
52
+ };
53
+ }
54
+ };
55
+ }
56
+ /** Bind a whole tool set, in registration order. */
57
+ function toDshTools(tools, options = {}) {
58
+ return tools.map((tool) => toDshTool(tool, options));
59
+ }
60
+ /**
61
+ * dsh has one description field where pi has three, so a tool's prompt snippet
62
+ * and guidelines — the parts that tell a model *when* to reach for it — are
63
+ * folded in rather than dropped.
64
+ */
65
+ function describe(tool) {
66
+ const parts = [tool.description];
67
+ if (tool.promptSnippet)
68
+ parts.push(tool.promptSnippet);
69
+ if (tool.promptGuidelines?.length) {
70
+ parts.push(tool.promptGuidelines.map((line) => `- ${line}`).join('\n'));
71
+ }
72
+ return parts.join('\n\n');
73
+ }
74
+ /** The neutral result's content blocks, in dsh's block vocabulary. */
75
+ function renderContent(value) {
76
+ const content = value?.content;
77
+ if (!Array.isArray(content))
78
+ return [];
79
+ return content.map((block) => {
80
+ const typed = block;
81
+ if (typed.type === 'text')
82
+ return { type: 'text', text: String(typed.text ?? '') };
83
+ // dsh's image block references an attachment the attachment service owns, so
84
+ // an inline image cannot be handed over as one; its text form is honest.
85
+ return { type: 'text', text: JSON.stringify(block) };
86
+ });
87
+ }
package/dsh-types.d.ts ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The DeepSeek Harness surface this adapter binds to, declared structurally.
3
+ *
4
+ * Nothing here imports `@deepseek-ai/dsh-*`, and the package has no dependency
5
+ * on it. That is deliberate rather than lazy: dsh is a developer preview whose
6
+ * packages promise breaking changes, and its published rc's trail its own
7
+ * source. A structural declaration of the four things we actually touch — a
8
+ * tool definition, a tool's run context, a content block, a plugin's `apply` —
9
+ * binds to the *shape* dsh asks for, so a host on any rc can register our tools
10
+ * without this package tracking their release train. It also keeps the adapter
11
+ * free of dsh's ESM-only graph: a CJS consumer imports it like any other
12
+ * agentic-kit package.
13
+ *
14
+ * Mirrored from dsh `0.1.0-rc.7` (`packages/core/tools`, `packages/core/session`,
15
+ * `packages/llm/llm`). Where dsh brands a string (`CallId`, `SessionId`) this
16
+ * uses `string` — a brand is theirs to enforce, and ours to carry.
17
+ */
18
+ /** dsh's supported JSON Schema subset, as a tool declares its parameters. */
19
+ export interface DshJsonSchema {
20
+ type?: string;
21
+ properties?: Record<string, DshJsonSchema>;
22
+ required?: string[];
23
+ items?: DshJsonSchema;
24
+ oneOf?: DshJsonSchema[];
25
+ enum?: unknown[];
26
+ const?: unknown;
27
+ additionalProperties?: boolean;
28
+ description?: string;
29
+ title?: string;
30
+ default?: unknown;
31
+ examples?: unknown[];
32
+ }
33
+ /** A model-facing content block. dsh names reasoning `reasoning`, not `thinking`. */
34
+ export type DshContentBlock = {
35
+ type: 'text';
36
+ text: string;
37
+ } | {
38
+ type: 'reasoning';
39
+ text: string;
40
+ } | {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ };
44
+ /**
45
+ * What dsh hands a tool body: call identity, the caller's cancellation, and
46
+ * the agent the call runs for. Notably *not* a working directory — dsh keeps
47
+ * that on the session header and its filesystem service — so an adapter has to
48
+ * supply one (see `ConstructivePluginOptions.cwd`).
49
+ */
50
+ export interface DshToolRunContext {
51
+ readonly callId: string;
52
+ readonly name: string;
53
+ readonly signal: AbortSignal;
54
+ readonly agent?: unknown;
55
+ }
56
+ /** A tool's canonical-output contract: a schema for the value, and its rendering. */
57
+ export interface DshToolOutputDefinition {
58
+ readonly schema: DshJsonSchema;
59
+ render(args: unknown, value: unknown): DshContentBlock[];
60
+ }
61
+ /** A dsh tool, as `tools.register()` takes it. */
62
+ export interface DshToolDefinition {
63
+ readonly name: string;
64
+ readonly description: string;
65
+ readonly parameters: DshJsonSchema;
66
+ readonly output: DshToolOutputDefinition;
67
+ execute(args: unknown, exec: DshToolRunContext): Promise<unknown>;
68
+ }
69
+ /** The pending call a `tools/pre-execute` listener decides on. */
70
+ export interface DshToolExecution {
71
+ readonly callId: string;
72
+ readonly name: string;
73
+ readonly arguments: unknown;
74
+ readonly agent?: unknown;
75
+ }
76
+ /** dsh's pre-dispatch decision. `ask` defers to its approval answerers. */
77
+ export type DshPreToolDecision = {
78
+ kind: 'allow';
79
+ } | {
80
+ kind: 'deny';
81
+ reason: string;
82
+ } | {
83
+ kind: 'ask';
84
+ reason?: string;
85
+ };
86
+ /** dsh's approval outcomes; only `allowed-once` is an approval. */
87
+ export type DshApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
88
+ /**
89
+ * dsh's approval service, as much of it as a gate needs. Present on the plugin
90
+ * context only when the host composed `@deepseek-ai/dsh-user-approval`.
91
+ */
92
+ export interface DshApprovalService {
93
+ request(request: {
94
+ toolName: string;
95
+ callId?: string;
96
+ reason?: string;
97
+ agent?: unknown;
98
+ }): Promise<DshApprovalOutcome>;
99
+ }
100
+ /** The tool registry service (`ctx.tools`). */
101
+ export interface DshToolRuntime {
102
+ register(definition: DshToolDefinition): () => void;
103
+ }
104
+ /**
105
+ * The plugin context, narrowed to the services this adapter uses. dsh's own
106
+ * `Context` carries every composed service and a cordis event bus; a plugin
107
+ * only ever needs the parts it declared.
108
+ */
109
+ export interface DshPluginContext {
110
+ tools: DshToolRuntime;
111
+ approval?: DshApprovalService;
112
+ on(event: 'tools/pre-execute', listener: (exec: DshToolExecution, next: () => Promise<DshPreToolDecision>) => Promise<DshPreToolDecision>): unknown;
113
+ }
114
+ /** A cordis plugin in its object form, which is how dsh bundles load one. */
115
+ export interface DshPlugin {
116
+ readonly name: string;
117
+ readonly inject?: readonly string[];
118
+ apply(ctx: DshPluginContext): void;
119
+ }
package/dsh-types.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * The DeepSeek Harness surface this adapter binds to, declared structurally.
4
+ *
5
+ * Nothing here imports `@deepseek-ai/dsh-*`, and the package has no dependency
6
+ * on it. That is deliberate rather than lazy: dsh is a developer preview whose
7
+ * packages promise breaking changes, and its published rc's trail its own
8
+ * source. A structural declaration of the four things we actually touch — a
9
+ * tool definition, a tool's run context, a content block, a plugin's `apply` —
10
+ * binds to the *shape* dsh asks for, so a host on any rc can register our tools
11
+ * without this package tracking their release train. It also keeps the adapter
12
+ * free of dsh's ESM-only graph: a CJS consumer imports it like any other
13
+ * agentic-kit package.
14
+ *
15
+ * Mirrored from dsh `0.1.0-rc.7` (`packages/core/tools`, `packages/core/session`,
16
+ * `packages/llm/llm`). Where dsh brands a string (`CallId`, `SessionId`) this
17
+ * uses `string` — a brand is theirs to enforce, and ours to carry.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,32 @@
1
+ import type { AnyHarnessTool, HarnessTool } from '@agentic-kit/harness';
2
+ import type { z } from 'zod';
3
+ import type { DshToolDefinition } from './dsh-types';
4
+ /** What a bound tool needs that a `HarnessTool` does not state. */
5
+ export interface ToDshToolOptions {
6
+ /**
7
+ * The working directory a tool resolves project context from. dsh keeps the
8
+ * directory on the session header and its filesystem service rather than on a
9
+ * tool's execution context, so the adapter is told once instead of guessing
10
+ * per call. Defaults to `process.cwd()`.
11
+ */
12
+ cwd?: () => string;
13
+ }
14
+ /**
15
+ * Bind a neutral `HarnessTool` to dsh's `ToolDefinition`.
16
+ *
17
+ * The sibling of `toPiTool` — same tools, a second harness's shape. dsh asks
18
+ * for three things pi does not: parameters in its JSON Schema subset (see
19
+ * `./schema`), a *declared canonical output* with a pure projection from that
20
+ * value to model-facing content, and a body that returns the value rather than
21
+ * the content. So the neutral `HarnessToolResult` becomes the canonical value
22
+ * verbatim and `output.render` projects its `content` — which means dsh's
23
+ * durable log keeps the tool's structured `details`, and a Constructive
24
+ * renderer reads the same detail out of a dsh transcript as out of a pi one.
25
+ *
26
+ * Arguments are parsed with the tool's own zod schema before the body runs:
27
+ * dsh validates against the narrowed subset schema, and this restores every
28
+ * constraint that narrowing dropped.
29
+ */
30
+ export declare function toDshTool<TParams extends z.ZodType, TDetails>(tool: HarnessTool<TParams, TDetails>, options?: ToDshToolOptions): DshToolDefinition;
31
+ /** Bind a whole tool set, in registration order. */
32
+ export declare function toDshTools(tools: readonly AnyHarnessTool[], options?: ToDshToolOptions): DshToolDefinition[];
@@ -0,0 +1,83 @@
1
+ import { toDshParameters } from './schema';
2
+ /** The canonical value a bound tool returns: the neutral result, as JSON. */
3
+ const OUTPUT_SCHEMA = {
4
+ type: 'object',
5
+ properties: {
6
+ content: { type: 'array', items: { type: 'object' } },
7
+ details: {},
8
+ terminate: { type: 'boolean' }
9
+ },
10
+ required: ['content']
11
+ };
12
+ /**
13
+ * Bind a neutral `HarnessTool` to dsh's `ToolDefinition`.
14
+ *
15
+ * The sibling of `toPiTool` — same tools, a second harness's shape. dsh asks
16
+ * for three things pi does not: parameters in its JSON Schema subset (see
17
+ * `./schema`), a *declared canonical output* with a pure projection from that
18
+ * value to model-facing content, and a body that returns the value rather than
19
+ * the content. So the neutral `HarnessToolResult` becomes the canonical value
20
+ * verbatim and `output.render` projects its `content` — which means dsh's
21
+ * durable log keeps the tool's structured `details`, and a Constructive
22
+ * renderer reads the same detail out of a dsh transcript as out of a pi one.
23
+ *
24
+ * Arguments are parsed with the tool's own zod schema before the body runs:
25
+ * dsh validates against the narrowed subset schema, and this restores every
26
+ * constraint that narrowing dropped.
27
+ */
28
+ export function toDshTool(tool, options = {}) {
29
+ const cwd = options.cwd ?? (() => process.cwd());
30
+ return {
31
+ name: tool.name,
32
+ description: describe(tool),
33
+ parameters: toDshParameters(tool.parameters),
34
+ output: {
35
+ schema: OUTPUT_SCHEMA,
36
+ render: (_args, value) => renderContent(value)
37
+ },
38
+ async execute(args, exec) {
39
+ const params = tool.parameters.parse(args);
40
+ const result = await tool.execute(params, {
41
+ cwd: cwd(),
42
+ signal: exec.signal
43
+ });
44
+ return {
45
+ content: result.content,
46
+ details: result.details === undefined ? null : result.details,
47
+ ...(result.terminate === undefined ? {} : { terminate: result.terminate })
48
+ };
49
+ }
50
+ };
51
+ }
52
+ /** Bind a whole tool set, in registration order. */
53
+ export function toDshTools(tools, options = {}) {
54
+ return tools.map((tool) => toDshTool(tool, options));
55
+ }
56
+ /**
57
+ * dsh has one description field where pi has three, so a tool's prompt snippet
58
+ * and guidelines — the parts that tell a model *when* to reach for it — are
59
+ * folded in rather than dropped.
60
+ */
61
+ function describe(tool) {
62
+ const parts = [tool.description];
63
+ if (tool.promptSnippet)
64
+ parts.push(tool.promptSnippet);
65
+ if (tool.promptGuidelines?.length) {
66
+ parts.push(tool.promptGuidelines.map((line) => `- ${line}`).join('\n'));
67
+ }
68
+ return parts.join('\n\n');
69
+ }
70
+ /** The neutral result's content blocks, in dsh's block vocabulary. */
71
+ function renderContent(value) {
72
+ const content = value?.content;
73
+ if (!Array.isArray(content))
74
+ return [];
75
+ return content.map((block) => {
76
+ const typed = block;
77
+ if (typed.type === 'text')
78
+ return { type: 'text', text: String(typed.text ?? '') };
79
+ // dsh's image block references an attachment the attachment service owns, so
80
+ // an inline image cannot be handed over as one; its text form is honest.
81
+ return { type: 'text', text: JSON.stringify(block) };
82
+ });
83
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The DeepSeek Harness surface this adapter binds to, declared structurally.
3
+ *
4
+ * Nothing here imports `@deepseek-ai/dsh-*`, and the package has no dependency
5
+ * on it. That is deliberate rather than lazy: dsh is a developer preview whose
6
+ * packages promise breaking changes, and its published rc's trail its own
7
+ * source. A structural declaration of the four things we actually touch — a
8
+ * tool definition, a tool's run context, a content block, a plugin's `apply` —
9
+ * binds to the *shape* dsh asks for, so a host on any rc can register our tools
10
+ * without this package tracking their release train. It also keeps the adapter
11
+ * free of dsh's ESM-only graph: a CJS consumer imports it like any other
12
+ * agentic-kit package.
13
+ *
14
+ * Mirrored from dsh `0.1.0-rc.7` (`packages/core/tools`, `packages/core/session`,
15
+ * `packages/llm/llm`). Where dsh brands a string (`CallId`, `SessionId`) this
16
+ * uses `string` — a brand is theirs to enforce, and ours to carry.
17
+ */
18
+ /** dsh's supported JSON Schema subset, as a tool declares its parameters. */
19
+ export interface DshJsonSchema {
20
+ type?: string;
21
+ properties?: Record<string, DshJsonSchema>;
22
+ required?: string[];
23
+ items?: DshJsonSchema;
24
+ oneOf?: DshJsonSchema[];
25
+ enum?: unknown[];
26
+ const?: unknown;
27
+ additionalProperties?: boolean;
28
+ description?: string;
29
+ title?: string;
30
+ default?: unknown;
31
+ examples?: unknown[];
32
+ }
33
+ /** A model-facing content block. dsh names reasoning `reasoning`, not `thinking`. */
34
+ export type DshContentBlock = {
35
+ type: 'text';
36
+ text: string;
37
+ } | {
38
+ type: 'reasoning';
39
+ text: string;
40
+ } | {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ };
44
+ /**
45
+ * What dsh hands a tool body: call identity, the caller's cancellation, and
46
+ * the agent the call runs for. Notably *not* a working directory — dsh keeps
47
+ * that on the session header and its filesystem service — so an adapter has to
48
+ * supply one (see `ConstructivePluginOptions.cwd`).
49
+ */
50
+ export interface DshToolRunContext {
51
+ readonly callId: string;
52
+ readonly name: string;
53
+ readonly signal: AbortSignal;
54
+ readonly agent?: unknown;
55
+ }
56
+ /** A tool's canonical-output contract: a schema for the value, and its rendering. */
57
+ export interface DshToolOutputDefinition {
58
+ readonly schema: DshJsonSchema;
59
+ render(args: unknown, value: unknown): DshContentBlock[];
60
+ }
61
+ /** A dsh tool, as `tools.register()` takes it. */
62
+ export interface DshToolDefinition {
63
+ readonly name: string;
64
+ readonly description: string;
65
+ readonly parameters: DshJsonSchema;
66
+ readonly output: DshToolOutputDefinition;
67
+ execute(args: unknown, exec: DshToolRunContext): Promise<unknown>;
68
+ }
69
+ /** The pending call a `tools/pre-execute` listener decides on. */
70
+ export interface DshToolExecution {
71
+ readonly callId: string;
72
+ readonly name: string;
73
+ readonly arguments: unknown;
74
+ readonly agent?: unknown;
75
+ }
76
+ /** dsh's pre-dispatch decision. `ask` defers to its approval answerers. */
77
+ export type DshPreToolDecision = {
78
+ kind: 'allow';
79
+ } | {
80
+ kind: 'deny';
81
+ reason: string;
82
+ } | {
83
+ kind: 'ask';
84
+ reason?: string;
85
+ };
86
+ /** dsh's approval outcomes; only `allowed-once` is an approval. */
87
+ export type DshApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
88
+ /**
89
+ * dsh's approval service, as much of it as a gate needs. Present on the plugin
90
+ * context only when the host composed `@deepseek-ai/dsh-user-approval`.
91
+ */
92
+ export interface DshApprovalService {
93
+ request(request: {
94
+ toolName: string;
95
+ callId?: string;
96
+ reason?: string;
97
+ agent?: unknown;
98
+ }): Promise<DshApprovalOutcome>;
99
+ }
100
+ /** The tool registry service (`ctx.tools`). */
101
+ export interface DshToolRuntime {
102
+ register(definition: DshToolDefinition): () => void;
103
+ }
104
+ /**
105
+ * The plugin context, narrowed to the services this adapter uses. dsh's own
106
+ * `Context` carries every composed service and a cordis event bus; a plugin
107
+ * only ever needs the parts it declared.
108
+ */
109
+ export interface DshPluginContext {
110
+ tools: DshToolRuntime;
111
+ approval?: DshApprovalService;
112
+ on(event: 'tools/pre-execute', listener: (exec: DshToolExecution, next: () => Promise<DshPreToolDecision>) => Promise<DshPreToolDecision>): unknown;
113
+ }
114
+ /** A cordis plugin in its object form, which is how dsh bundles load one. */
115
+ export interface DshPlugin {
116
+ readonly name: string;
117
+ readonly inject?: readonly string[];
118
+ apply(ctx: DshPluginContext): void;
119
+ }