ns-dsh-llm-devin 0.0.0-stage → 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/README.md CHANGED
@@ -1,3 +1,16 @@
1
- # Temporary Holding Version
1
+ # ns-dsh-llm-devin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness (dsh) LLM adapter that adds Devin (Cognition Cascade) as a
4
+ model provider. Sessions come from the machine — `devin auth login` — so no
5
+ credential setup lives here.
6
+
7
+ ```yaml
8
+ - id: llm-devin
9
+ name: 'dsh-llm-devin'
10
+ config:
11
+ provider: devin
12
+ ```
13
+
14
+ Streams text, reasoning, and tool calls through `ns-devin-core` and maps
15
+ Devin's failures onto the Harness `LlmError` routing codes. Works with dsh
16
+ 0.2 (tool-role messages) and still reads the dsh 0.1 `tool-result` blocks.
@@ -0,0 +1,30 @@
1
+ import type { AttachmentStore } from "@deepseek-ai/dsh-attachment";
2
+ import { type GenerateOptions, LlmAdapter, type LlmModelInfo, type LlmProviderInfo, type LlmResolvedModelInfo, type StreamChunk } from "@deepseek-ai/dsh-llm";
3
+ import { type DevinCredentials } from "ns-devin-core";
4
+ /** What the plugin resolves per request and freezes for the duration of one call. */
5
+ export interface DevinAdapterOptions {
6
+ /** The route this adapter is registered under. */
7
+ provider: string;
8
+ /** Human-readable provider name for selectors and diagnostics. */
9
+ displayName: string;
10
+ /** Resolve the current session; called once per request. */
11
+ credentials: () => Promise<DevinCredentials>;
12
+ /** Optional durable attachment service, resolved at request time. */
13
+ attachments?: () => AttachmentStore | undefined;
14
+ }
15
+ export declare class DevinAdapter extends LlmAdapter {
16
+ private readonly options;
17
+ constructor(options: DevinAdapterOptions);
18
+ providerInfo(provider: string): LlmProviderInfo;
19
+ private refreshPromise;
20
+ /**
21
+ * `GetCliModelConfigs` refreshes the catalog when the disk cache is stale.
22
+ * A failed refresh leaves the stale (or seed) catalog in place — listing
23
+ * must never fail just because discovery could not reach the server.
24
+ */
25
+ private refreshCatalog;
26
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
27
+ resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
28
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
29
+ private streamInner;
30
+ }
@@ -0,0 +1,237 @@
1
+ // ABOUTME: The Harness LLM seam over devin-core.
2
+ import { LlmAdapter, LlmError, } from "@deepseek-ai/dsh-llm";
3
+ import { fetchDevinModels, getCachedModels, isCacheStale, logger, streamDevin, updateDevinModelsCache, } from "ns-devin-core";
4
+ import { toLlmError } from "./errors.js";
5
+ import { toDevinMessages } from "./messages.js";
6
+ const EFFORT_NAMES = {
7
+ minimal: "Minimal",
8
+ low: "Low",
9
+ medium: "Medium",
10
+ high: "High",
11
+ xhigh: "Extra high",
12
+ max: "Maximum",
13
+ };
14
+ /**
15
+ * Project one core usage record onto the harness token buckets.
16
+ *
17
+ * Devin reports real cache counters when the Cascade backend bills them.
18
+ * The session pill treats a missing `cacheReadTokens` as zero, and a turn
19
+ * drops its cache sum unless every step reported both cache fields — and the
20
+ * fields have to add up to `totalTokens` or the harness discards the sample.
21
+ */
22
+ function toHarnessUsage(usage) {
23
+ const cacheReadTokens = usage.cacheRead ?? 0;
24
+ const cacheWriteTokens = usage.cacheWrite ?? 0;
25
+ const summed = usage.input + cacheReadTokens + cacheWriteTokens + usage.output;
26
+ if (usage.totalTokens !== summed) {
27
+ return {
28
+ inputTokens: usage.input,
29
+ outputTokens: usage.output,
30
+ totalTokens: usage.totalTokens,
31
+ };
32
+ }
33
+ return {
34
+ inputTokens: usage.input,
35
+ outputTokens: usage.output,
36
+ totalTokens: summed,
37
+ cacheReadTokens,
38
+ cacheWriteTokens,
39
+ };
40
+ }
41
+ export class DevinAdapter extends LlmAdapter {
42
+ options;
43
+ constructor(options) {
44
+ super();
45
+ this.options = options;
46
+ }
47
+ providerInfo(provider) {
48
+ return { id: provider, name: this.options.displayName };
49
+ }
50
+ refreshPromise;
51
+ /**
52
+ * `GetCliModelConfigs` refreshes the catalog when the disk cache is stale.
53
+ * A failed refresh leaves the stale (or seed) catalog in place — listing
54
+ * must never fail just because discovery could not reach the server.
55
+ */
56
+ refreshCatalog() {
57
+ this.refreshPromise ??= (async () => {
58
+ try {
59
+ if (!isCacheStale())
60
+ return;
61
+ const credentials = await this.options.credentials();
62
+ const fetched = await fetchDevinModels({ apiKey: credentials.access });
63
+ if (fetched)
64
+ updateDevinModelsCache(fetched);
65
+ }
66
+ catch (error) {
67
+ logger.warn("model discovery refresh failed", {
68
+ message: error instanceof Error ? error.message : String(error),
69
+ });
70
+ }
71
+ finally {
72
+ this.refreshPromise = undefined;
73
+ }
74
+ })();
75
+ return this.refreshPromise;
76
+ }
77
+ async listModels(provider) {
78
+ await this.refreshCatalog();
79
+ return getCachedModels().map((model) => ({
80
+ provider,
81
+ id: model.id,
82
+ name: model.name,
83
+ inputModalities: model.input.filter((modality) => modality === "text" || modality === "image"),
84
+ }));
85
+ }
86
+ async resolveModel(provider, model, _signal) {
87
+ await this.refreshCatalog();
88
+ const known = getCachedModels().find((candidate) => candidate.id === model);
89
+ if (!known)
90
+ throw new LlmError(`Unknown Devin model: ${model}`, "UNKNOWN_MODEL");
91
+ return {
92
+ provider,
93
+ id: known.id,
94
+ name: known.name,
95
+ inputModalities: known.input.filter((modality) => modality === "text" || modality === "image"),
96
+ context: { contextWindow: known.contextWindow },
97
+ defaultMaxTokens: known.maxTokens,
98
+ ...(known.efforts?.length
99
+ ? {
100
+ reasoning: {
101
+ efforts: known.efforts.map((effort) => ({
102
+ id: effort,
103
+ name: EFFORT_NAMES[effort],
104
+ })),
105
+ },
106
+ }
107
+ : {}),
108
+ };
109
+ }
110
+ async *stream(options) {
111
+ try {
112
+ yield* this.streamInner(options);
113
+ }
114
+ catch (error) {
115
+ // `LlmRuntime.stream()` normalizes a throw into a terminal finish, but only
116
+ // after this generator has surfaced it. Converting here is what gives the
117
+ // loop a routing code instead of Devin's raw wording.
118
+ throw toLlmError(error);
119
+ }
120
+ }
121
+ async *streamInner(options) {
122
+ const credentials = await this.options.credentials();
123
+ const model = getCachedModels().find((candidate) => candidate.id === options.model);
124
+ if (!model)
125
+ throw new LlmError(`Unknown Devin model: ${options.model}`, "UNKNOWN_MODEL");
126
+ const projected = await toDevinMessages(options.messages, {
127
+ attachments: this.options.attachments?.(),
128
+ signal: options.signal,
129
+ });
130
+ // The Harness passes the system prompt as a request field, but a history it
131
+ // replays may also carry `system`-role messages. Both are real system text,
132
+ // so they are joined rather than one silently winning.
133
+ const system = [options.system, projected.system].filter((part) => !!part).join("\n\n") || undefined;
134
+ const tools = options.tools?.map((tool) => ({
135
+ name: tool.name,
136
+ description: tool.description,
137
+ parameters: tool.parameters,
138
+ }));
139
+ const openBlocks = new Set();
140
+ for await (const event of streamDevin({
141
+ model,
142
+ messages: projected.messages,
143
+ systemPrompt: system,
144
+ tools,
145
+ effort: options.reasoningEffort,
146
+ apiKey: credentials.access,
147
+ sessionId: options.sessionId,
148
+ signal: options.signal,
149
+ })) {
150
+ switch (event.type) {
151
+ case "text_start":
152
+ openBlocks.add(event.index);
153
+ yield { type: "block-start", index: event.index, blockType: "text" };
154
+ break;
155
+ case "text_delta":
156
+ yield { type: "text-delta", index: event.index, text: event.delta };
157
+ break;
158
+ case "text_end":
159
+ openBlocks.delete(event.index);
160
+ yield { type: "block-end", index: event.index, block: { type: "text", text: event.text } };
161
+ break;
162
+ case "thinking_start":
163
+ openBlocks.add(event.index);
164
+ yield { type: "block-start", index: event.index, blockType: "reasoning" };
165
+ break;
166
+ case "thinking_delta":
167
+ yield { type: "reasoning-delta", index: event.index, text: event.delta };
168
+ break;
169
+ case "thinking_end":
170
+ openBlocks.delete(event.index);
171
+ yield { type: "block-end", index: event.index, block: { type: "reasoning", text: event.thinking } };
172
+ break;
173
+ case "tool_call_start":
174
+ openBlocks.add(event.index);
175
+ yield { type: "block-start", index: event.index, blockType: "tool-call" };
176
+ yield {
177
+ type: "tool-call-delta",
178
+ index: event.index,
179
+ id: event.id,
180
+ name: event.name,
181
+ argumentsDelta: "",
182
+ };
183
+ break;
184
+ case "tool_call_delta":
185
+ yield {
186
+ type: "tool-call-delta",
187
+ index: event.index,
188
+ id: event.id,
189
+ argumentsDelta: event.argumentsDelta,
190
+ };
191
+ break;
192
+ case "tool_call_end":
193
+ openBlocks.delete(event.index);
194
+ yield {
195
+ type: "block-end",
196
+ index: event.index,
197
+ block: {
198
+ type: "tool-call",
199
+ id: event.id,
200
+ name: event.name,
201
+ // The Harness keeps the model's raw JSON and reports invalid
202
+ // arguments back to the model; re-serializing a parsed preview
203
+ // would run a truncated call with auto-closed arguments.
204
+ arguments: event.argumentsJson.trim() ? event.argumentsJson : "{}",
205
+ },
206
+ };
207
+ break;
208
+ case "usage":
209
+ yield { type: "usage", usage: toHarnessUsage(event.usage) };
210
+ break;
211
+ case "done":
212
+ yield {
213
+ type: "finish",
214
+ reason: event.stopReason === "toolUse"
215
+ ? { kind: "tool-calls" }
216
+ : event.stopReason === "length"
217
+ ? { kind: "max-tokens" }
218
+ : { kind: "stop" },
219
+ // The finish union carries no message field for a successful
220
+ // reason, so a terminal diagnostic rides the replay envelope —
221
+ // the harness stores it on the assembled message's model source.
222
+ ...(event.errorMessage ? { replayState: { response: { errorMessage: event.errorMessage } } } : {}),
223
+ };
224
+ break;
225
+ // `start` needs no chunk. `reset` can arrive on a mid-stream-error
226
+ // retry; this adapter cannot un-deliver a block, so it ignores the
227
+ // marker and lets the retried attempt continue on fresh indexes.
228
+ }
229
+ }
230
+ // A block the core opened but never closed would leave the assembler waiting
231
+ // for content that is not coming. Close them rather than trust it.
232
+ for (const index of openBlocks) {
233
+ yield { type: "block-end", index, block: { type: "text", text: "" } };
234
+ }
235
+ }
236
+ }
237
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { LlmError } from "@deepseek-ai/dsh-llm";
2
+ /**
3
+ * Classify a core failure for the Harness.
4
+ *
5
+ * The core throws Devin errors because it serves two hosts with different
6
+ * error types. This is where they become one of the Harness's routing codes —
7
+ * the loop branches on `code`, so a mislabelled failure either retries forever
8
+ * or gives up on something transient.
9
+ */
10
+ export declare function toLlmError(error: unknown): LlmError;
package/dist/errors.js ADDED
@@ -0,0 +1,35 @@
1
+ // ABOUTME: Maps Devin's failure vocabulary onto the Harness LlmError taxonomy.
2
+ import { LlmError } from "@deepseek-ai/dsh-llm";
3
+ import { DevinApiError, isDevinAuthError, isDevinCapacityError, isDevinContextOverflowError, isDevinRateLimitError, } from "ns-devin-core";
4
+ /**
5
+ * Classify a core failure for the Harness.
6
+ *
7
+ * The core throws Devin errors because it serves two hosts with different
8
+ * error types. This is where they become one of the Harness's routing codes —
9
+ * the loop branches on `code`, so a mislabelled failure either retries forever
10
+ * or gives up on something transient.
11
+ */
12
+ export function toLlmError(error) {
13
+ if (error instanceof LlmError)
14
+ return error;
15
+ const cause = error instanceof Error ? error : undefined;
16
+ const message = cause?.message ?? String(error);
17
+ if (cause?.name === "AbortError")
18
+ return new LlmError(message, "ABORTED", { cause });
19
+ const status = error instanceof DevinApiError ? error.status : undefined;
20
+ const options = { cause, ...(status !== undefined ? { status } : {}) };
21
+ if (isDevinContextOverflowError(error))
22
+ return new LlmError(message, "CONTEXT_OVERFLOW", options);
23
+ if (isDevinRateLimitError(error))
24
+ return new LlmError(message, "RATE_LIMIT", options);
25
+ if (isDevinCapacityError(error))
26
+ return new LlmError(message, "OVERLOADED", options);
27
+ if (isDevinAuthError(error) || message.includes("credentials not set") || message.includes("No Devin session")) {
28
+ return new LlmError(message, "AUTH", options);
29
+ }
30
+ if (message.includes("timeout") || message.includes("timed out")) {
31
+ return new LlmError(message, "TIMEOUT", options);
32
+ }
33
+ return new LlmError(message, "PROVIDER_ERROR", options);
34
+ }
35
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAEhD,IAAI,KAAK,EAAE,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAErF,MAAM,MAAM,GAAG,KAAK,YAAY,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAEvE,IAAI,2BAA2B,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAClG,IAAI,qBAAqB,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACtF,IAAI,oBAAoB,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACrF,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC/G,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACjE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Devin adapter plugin for the DeepSeek Harness LLM seam.
3
+ *
4
+ * Devin speaks Connect server-streaming over `server.codeium.com` — protobuf
5
+ * frames carrying text/thinking/tool-call deltas — which no configurable route
6
+ * can describe, so this registers a real adapter rather than a profile.
7
+ *
8
+ * Sessions come from the machine: `devin auth login` writes
9
+ * `~/.local/share/devin/credentials.toml`, which this plugin reads per request
10
+ * so a CLI token rotation is picked up without a restart.
11
+ *
12
+ * ```yaml
13
+ * - id: llm-devin
14
+ * name: 'dsh-llm-devin'
15
+ * config:
16
+ * provider: devin
17
+ * ```
18
+ *
19
+ * @module dsh-llm-devin
20
+ */
21
+ import type { Context } from "@deepseek-ai/cordis";
22
+ import z from "@deepseek-ai/schemastery";
23
+ export { DevinAdapter, type DevinAdapterOptions } from "./adapter.js";
24
+ export { toLlmError } from "./errors.js";
25
+ export { toDevinMessages } from "./messages.js";
26
+ export declare const name = "llm-devin";
27
+ /**
28
+ * Only `llm` is a hard dependency. Cordis's object form of `inject` maps a
29
+ * service name to intercept config rather than marking it optional, and reading
30
+ * an uninjected service throws — so the optional attachment store is picked up
31
+ * through a scoped `ctx.inject` inside {@link apply} instead.
32
+ */
33
+ export declare const inject: string[];
34
+ export interface Config {
35
+ /** Provider route to register the adapter under. */
36
+ provider: string;
37
+ /** Display name for selectors and status labels. */
38
+ displayName: string;
39
+ }
40
+ export declare const Config: z<Config>;
41
+ export declare function apply(ctx: Context, config: Config): void;
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Devin adapter plugin for the DeepSeek Harness LLM seam.
3
+ *
4
+ * Devin speaks Connect server-streaming over `server.codeium.com` — protobuf
5
+ * frames carrying text/thinking/tool-call deltas — which no configurable route
6
+ * can describe, so this registers a real adapter rather than a profile.
7
+ *
8
+ * Sessions come from the machine: `devin auth login` writes
9
+ * `~/.local/share/devin/credentials.toml`, which this plugin reads per request
10
+ * so a CLI token rotation is picked up without a restart.
11
+ *
12
+ * ```yaml
13
+ * - id: llm-devin
14
+ * name: 'dsh-llm-devin'
15
+ * config:
16
+ * provider: devin
17
+ * ```
18
+ *
19
+ * @module dsh-llm-devin
20
+ */
21
+ import { LlmError } from "@deepseek-ai/dsh-llm";
22
+ import z from "@deepseek-ai/schemastery";
23
+ import { credentialsFromToken, isExpired, refreshDevinToken, resolveDevinCredentials, } from "ns-devin-core";
24
+ import { DevinAdapter } from "./adapter.js";
25
+ export { DevinAdapter } from "./adapter.js";
26
+ export { toLlmError } from "./errors.js";
27
+ export { toDevinMessages } from "./messages.js";
28
+ export const name = "llm-devin";
29
+ /**
30
+ * Only `llm` is a hard dependency. Cordis's object form of `inject` maps a
31
+ * service name to intercept config rather than marking it optional, and reading
32
+ * an uninjected service throws — so the optional attachment store is picked up
33
+ * through a scoped `ctx.inject` inside {@link apply} instead.
34
+ */
35
+ export const inject = ["llm"];
36
+ export const Config = z.object({
37
+ provider: z.string().default("devin").description("Provider route to register the adapter under."),
38
+ displayName: z.string().default("Devin").description("Display name shown in model selectors."),
39
+ });
40
+ /**
41
+ * Resolve the current session for one request.
42
+ *
43
+ * Sessions are re-read rather than cached: `devin auth` rotates the token from
44
+ * another process, and a cached copy would send a token the service has already
45
+ * replaced. The read is a local file, so the cost is not worth the staleness.
46
+ */
47
+ async function currentCredentials() {
48
+ const stored = resolveDevinCredentials();
49
+ if (!stored) {
50
+ throw new LlmError("No Devin session found. Sign in with `devin auth login`, or paste a Devin API key.", "MISSING_CREDENTIAL");
51
+ }
52
+ const credentials = credentialsFromToken(stored.apiKey);
53
+ return isExpired(credentials) ? refreshDevinToken(credentials) : credentials;
54
+ }
55
+ export function apply(ctx, config) {
56
+ // Held here rather than read per request: a deployment without the attachment
57
+ // service never mounts it, and reading an uninjected service throws. The
58
+ // scoped fiber below sets it while the service exists and clears it when the
59
+ // service goes away, so an image-capable deployment gains image support
60
+ // without a text-only one failing to boot.
61
+ let attachments;
62
+ ctx.inject(["attachments"], (scope) => {
63
+ attachments = scope.attachments;
64
+ scope.effect(() => () => {
65
+ attachments = undefined;
66
+ }, "dsh-llm-devin attachment store");
67
+ });
68
+ const adapter = new DevinAdapter({
69
+ provider: config.provider,
70
+ displayName: config.displayName,
71
+ credentials: currentCredentials,
72
+ attachments: () => attachments,
73
+ });
74
+ ctx.llm.registerAdapter([config.provider], adapter);
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,CAAC,MAAM,0BAA0B,CAAC;AACzC,OAAO,EACL,oBAAoB,EAEpB,SAAS,EACT,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,YAAY,EAA4B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC;AAS9B,MAAM,CAAC,MAAM,MAAM,GAAc,CAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,+CAA+C,CAAC;IAClG,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,wCAAwC,CAAC;CAC/F,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,KAAK,UAAU,kBAAkB;IAC/B,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,oFAAoF,EACpF,oBAAoB,CACrB,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAc;IAChD,8EAA8E;IAC9E,yEAAyE;IACzE,6EAA6E;IAC7E,wEAAwE;IACxE,2CAA2C;IAC3C,IAAI,WAAwC,CAAC;IAC7C,GAAG,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACpC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QAChC,KAAK,CAAC,MAAM,CACV,GAAG,EAAE,CAAC,GAAG,EAAE;YACT,WAAW,GAAG,SAAS,CAAC;QAC1B,CAAC,EACD,gCAAgC,CACjC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC;QAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,kBAAkB;QAC/B,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW;KAC/B,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { AttachmentStore } from "@deepseek-ai/dsh-attachment";
2
+ import { type RequestMessage } from "@deepseek-ai/dsh-llm";
3
+ import type { DevinMessage } from "ns-devin-core";
4
+ /**
5
+ * Harness images live in the attachment service, so their bytes are read here
6
+ * rather than carried on the block. A store is optional: without one the images
7
+ * are dropped and the rest of the turn still reaches the model, which is the
8
+ * behaviour a text-only deployment wants.
9
+ */
10
+ export interface MessageProjectionContext {
11
+ attachments?: AttachmentStore;
12
+ signal?: AbortSignal;
13
+ }
14
+ /**
15
+ * Flatten one Harness history into the neutral shape.
16
+ *
17
+ * Tool results arrive as `tool`-role messages (dsh 0.2) or as `tool-result`
18
+ * blocks inside a user message (dsh 0.1); both become neutral `toolResult`
19
+ * messages. The system prompt travels as a `system`-role message rather than a
20
+ * request field, so its text is returned separately for the system slot.
21
+ * `developer` messages only record tool additions/removals, which this route
22
+ * does not read in-history (it declares no `toolUpdate`), so they are skipped.
23
+ */
24
+ export declare function toDevinMessages(messages: readonly RequestMessage[], context?: MessageProjectionContext): Promise<{
25
+ messages: DevinMessage[];
26
+ system?: string;
27
+ }>;
@@ -0,0 +1,126 @@
1
+ // ABOUTME: Projects the Harness conversation vocabulary onto the neutral one devin-core reads.
2
+ // ABOUTME: One direction only — responses travel back as stream events, not messages.
3
+ import { fileHandleText, offloadedImageText } from "@deepseek-ai/dsh-llm";
4
+ function isLegacyToolResult(block) {
5
+ return block.type === "tool-result";
6
+ }
7
+ async function userContent(blocks, context) {
8
+ const out = [];
9
+ for (const block of blocks) {
10
+ if (block.type === "text")
11
+ out.push({ type: "text", text: block.text });
12
+ else if (block.type === "reasoning")
13
+ out.push({ type: "text", text: block.text });
14
+ else if (block.type === "file") {
15
+ // Files never go to a provider as bytes: the harness projects them to
16
+ // handle text, and a hand-built call that still carries a file block
17
+ // gets the same representation here.
18
+ out.push({
19
+ type: "text",
20
+ text: fileHandleText(block.attachment, context.attachments?.fileHostPath(block.attachment)),
21
+ });
22
+ }
23
+ else if (block.type === "image") {
24
+ // An offloaded occurrence is a durable decision to send the placeholder
25
+ // naming the image and its read-only path instead of its bytes.
26
+ if (block.offloaded === true) {
27
+ const hostPath = context.attachments?.imageHostPath(block.attachment);
28
+ out.push({
29
+ type: "text",
30
+ text: offloadedImageText(block.attachment, hostPath ? { readonlyPath: hostPath } : undefined),
31
+ });
32
+ continue;
33
+ }
34
+ if (!context.attachments)
35
+ continue;
36
+ const stored = await context.attachments.readImage(block.attachment, context.signal);
37
+ out.push({
38
+ type: "image",
39
+ data: Buffer.from(stored.data).toString("base64"),
40
+ mimeType: block.attachment.mediaType,
41
+ });
42
+ }
43
+ // `tool-addition` / `tool-removal` belong to developer messages; this route
44
+ // declares no `toolUpdate`, so every request already carries the full tool list.
45
+ }
46
+ return out;
47
+ }
48
+ /**
49
+ * Flatten one Harness history into the neutral shape.
50
+ *
51
+ * Tool results arrive as `tool`-role messages (dsh 0.2) or as `tool-result`
52
+ * blocks inside a user message (dsh 0.1); both become neutral `toolResult`
53
+ * messages. The system prompt travels as a `system`-role message rather than a
54
+ * request field, so its text is returned separately for the system slot.
55
+ * `developer` messages only record tool additions/removals, which this route
56
+ * does not read in-history (it declares no `toolUpdate`), so they are skipped.
57
+ */
58
+ export async function toDevinMessages(messages, context = {}) {
59
+ const out = [];
60
+ const systemParts = [];
61
+ // The Harness correlates a result by call id alone; the call's name is only
62
+ // on the assistant block, so it is remembered here for the result.
63
+ const toolNames = new Map();
64
+ for (const message of messages) {
65
+ if (message.role === "system") {
66
+ for (const block of message.content)
67
+ if (block.type === "text")
68
+ systemParts.push(block.text);
69
+ continue;
70
+ }
71
+ if (message.role === "developer")
72
+ continue;
73
+ if (message.role === "assistant") {
74
+ const content = [];
75
+ for (const block of message.content) {
76
+ if (block.type === "text")
77
+ content.push({ type: "text", text: block.text });
78
+ else if (block.type === "reasoning")
79
+ content.push({ type: "thinking", thinking: block.text });
80
+ else if (block.type === "tool-call") {
81
+ let args;
82
+ try {
83
+ args = JSON.parse(block.arguments || "{}");
84
+ }
85
+ catch {
86
+ // A stored call whose arguments no longer parse is still part of
87
+ // the exchange the model must see; sending it with empty arguments
88
+ // keeps the call/result pairing, which dropping would break.
89
+ args = {};
90
+ }
91
+ toolNames.set(block.id, block.name);
92
+ content.push({ type: "toolCall", id: block.id, name: block.name, arguments: args });
93
+ }
94
+ }
95
+ out.push({ role: "assistant", content });
96
+ continue;
97
+ }
98
+ if (message.role === "tool") {
99
+ out.push({
100
+ role: "toolResult",
101
+ toolCallId: message.toolCallId,
102
+ toolName: toolNames.get(message.toolCallId) ?? "tool",
103
+ content: await userContent(message.content, context),
104
+ isError: message.isError === true,
105
+ });
106
+ continue;
107
+ }
108
+ // user (durable or request-only input)
109
+ const blocks = message.content;
110
+ const results = blocks.filter(isLegacyToolResult);
111
+ const plain = blocks.filter((block) => !isLegacyToolResult(block));
112
+ if (plain.length > 0)
113
+ out.push({ role: "user", content: await userContent(plain, context) });
114
+ for (const result of results) {
115
+ out.push({
116
+ role: "toolResult",
117
+ toolCallId: result.toolCallId,
118
+ toolName: toolNames.get(result.toolCallId) ?? "tool",
119
+ content: await userContent(result.content, context),
120
+ isError: result.isError === true,
121
+ });
122
+ }
123
+ }
124
+ return { messages: out, ...(systemParts.length > 0 ? { system: systemParts.join("\n\n") } : {}) };
125
+ }
126
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "ns-dsh-llm-devin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "DeepSeek Harness (dsh) LLM adapter adding Devin (Cognition Cascade) as a model provider",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/ngosangns/ns-devin-provider.git",
19
+ "directory": "packages/dsh-llm-devin"
20
+ },
21
+ "keywords": [
22
+ "deepseek-harness",
23
+ "dsh",
24
+ "dsh-plugin",
25
+ "cordis",
26
+ "devin",
27
+ "cognition",
28
+ "cascade",
29
+ "ai",
30
+ "llm"
31
+ ],
32
+ "files": [
33
+ "dist",
34
+ "README.md"
35
+ ],
36
+ "dependencies": {
37
+ "@deepseek-ai/schemastery": "^3.18.2",
38
+ "ns-devin-core": "0.2.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@deepseek-ai/cordis": "^4.0.2",
42
+ "@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1 <0.3.0",
43
+ "@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1 <0.3.0",
44
+ "@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.3.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@deepseek-ai/dsh-attachment": {
48
+ "optional": true
49
+ },
50
+ "@deepseek-ai/dsh-credentials": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@deepseek-ai/cordis": "4.0.4",
56
+ "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
58
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
59
+ "@types/node": "^22.20.4"
60
+ },
61
+ "scripts": {
62
+ "build": "tsc -p tsconfig.json",
63
+ "check": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json"
64
+ }
6
65
  }