ns-dsh-llm-devin 0.1.0 → 0.2.1
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 +22 -0
- package/README.md +5 -1
- package/dist/adapter.js +5 -112
- package/dist/adapter.js.map +1 -1
- package/dist/messages.d.ts +1 -26
- package/dist/messages.js +2 -93
- package/dist/messages.js.map +1 -1
- package/package.json +11 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ngosangns
|
|
4
|
+
Copyright (c) 2025-2026 Mike O'Brien (pi-provider-kiro)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -12,4 +12,8 @@ credential setup lives here.
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Streams text, reasoning, and tool calls through `ns-devin-core` and maps
|
|
15
|
-
Devin's failures onto the Harness `LlmError` routing codes.
|
|
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 —
|
|
17
|
+
that projection is `ns-bridge-core/dsh`, shared with `ns-dsh-llm-kiro`.
|
|
18
|
+
|
|
19
|
+
Part of [ns-bridge](https://github.com/ngosangns/ns-bridge).
|
package/dist/adapter.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// ABOUTME: The Harness LLM seam over devin-core.
|
|
2
2
|
import { LlmAdapter, LlmError, } from "@deepseek-ai/dsh-llm";
|
|
3
|
+
import { streamToDsh } from "ns-bridge-core/dsh";
|
|
3
4
|
import { fetchDevinModels, getCachedModels, isCacheStale, logger, streamDevin, updateDevinModelsCache, } from "ns-devin-core";
|
|
4
5
|
import { toLlmError } from "./errors.js";
|
|
5
6
|
import { toDevinMessages } from "./messages.js";
|
|
@@ -11,33 +12,6 @@ const EFFORT_NAMES = {
|
|
|
11
12
|
xhigh: "Extra high",
|
|
12
13
|
max: "Maximum",
|
|
13
14
|
};
|
|
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
15
|
export class DevinAdapter extends LlmAdapter {
|
|
42
16
|
options;
|
|
43
17
|
constructor(options) {
|
|
@@ -136,8 +110,9 @@ export class DevinAdapter extends LlmAdapter {
|
|
|
136
110
|
description: tool.description,
|
|
137
111
|
parameters: tool.parameters,
|
|
138
112
|
}));
|
|
139
|
-
|
|
140
|
-
|
|
113
|
+
// Event translation (blocks, usage, finish, unclosed-block safety) is
|
|
114
|
+
// shared with every dsh adapter in ns-bridge-core/dsh.
|
|
115
|
+
yield* streamToDsh(streamDevin({
|
|
141
116
|
model,
|
|
142
117
|
messages: projected.messages,
|
|
143
118
|
systemPrompt: system,
|
|
@@ -146,89 +121,7 @@ export class DevinAdapter extends LlmAdapter {
|
|
|
146
121
|
apiKey: credentials.access,
|
|
147
122
|
sessionId: options.sessionId,
|
|
148
123
|
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
|
-
arguments: JSON.stringify(event.arguments),
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
break;
|
|
205
|
-
case "usage":
|
|
206
|
-
yield { type: "usage", usage: toHarnessUsage(event.usage) };
|
|
207
|
-
break;
|
|
208
|
-
case "done":
|
|
209
|
-
yield {
|
|
210
|
-
type: "finish",
|
|
211
|
-
reason: event.stopReason === "toolUse"
|
|
212
|
-
? { kind: "tool-calls" }
|
|
213
|
-
: event.stopReason === "length"
|
|
214
|
-
? { kind: "max-tokens" }
|
|
215
|
-
: { kind: "stop" },
|
|
216
|
-
// The finish union carries no message field for a successful
|
|
217
|
-
// reason, so a terminal diagnostic rides the replay envelope —
|
|
218
|
-
// the harness stores it on the assembled message's model source.
|
|
219
|
-
...(event.errorMessage ? { replayState: { response: { errorMessage: event.errorMessage } } } : {}),
|
|
220
|
-
};
|
|
221
|
-
break;
|
|
222
|
-
// `start` needs no chunk. `reset` can arrive on a mid-stream-error
|
|
223
|
-
// retry; this adapter cannot un-deliver a block, so it ignores the
|
|
224
|
-
// marker and lets the retried attempt continue on fresh indexes.
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// A block the core opened but never closed would leave the assembler waiting
|
|
228
|
-
// for content that is not coming. Close them rather than trust it.
|
|
229
|
-
for (const index of openBlocks) {
|
|
230
|
-
yield { type: "block-end", index, block: { type: "text", text: "" } };
|
|
231
|
-
}
|
|
124
|
+
}));
|
|
232
125
|
}
|
|
233
126
|
}
|
|
234
127
|
//# sourceMappingURL=adapter.js.map
|
package/dist/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAGjD,OAAO,EAEL,UAAU,EACV,QAAQ,
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/messages.d.ts
CHANGED
|
@@ -1,26 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { type Message } 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
|
-
* Two structural differences drive the whole mapping. The Harness has no
|
|
18
|
-
* `toolResult` role — a result is a `tool-result` block inside a user message —
|
|
19
|
-
* so those blocks are lifted into their own neutral messages. And its system
|
|
20
|
-
* prompt travels as a `system`-role message rather than a request field, so
|
|
21
|
-
* system text is returned separately for the caller to pass as the system slot.
|
|
22
|
-
*/
|
|
23
|
-
export declare function toDevinMessages(messages: readonly Message[], context?: MessageProjectionContext): Promise<{
|
|
24
|
-
messages: DevinMessage[];
|
|
25
|
-
system?: string;
|
|
26
|
-
}>;
|
|
1
|
+
export { type MessageProjectionContext, toBridgeMessagesFromDsh as toDevinMessages, } from "ns-bridge-core/dsh";
|
package/dist/messages.js
CHANGED
|
@@ -1,95 +1,4 @@
|
|
|
1
1
|
// ABOUTME: Projects the Harness conversation vocabulary onto the neutral one devin-core reads.
|
|
2
|
-
// ABOUTME:
|
|
3
|
-
|
|
4
|
-
async function userContent(blocks, context) {
|
|
5
|
-
const out = [];
|
|
6
|
-
for (const block of blocks) {
|
|
7
|
-
if (block.type === "text")
|
|
8
|
-
out.push({ type: "text", text: block.text });
|
|
9
|
-
else if (block.type === "reasoning")
|
|
10
|
-
out.push({ type: "text", text: block.text });
|
|
11
|
-
else if (block.type === "file") {
|
|
12
|
-
// Files never go to a provider as bytes: the harness projects them to
|
|
13
|
-
// handle text, and a hand-built call that still carries a file block
|
|
14
|
-
// gets the same representation here.
|
|
15
|
-
out.push({
|
|
16
|
-
type: "text",
|
|
17
|
-
text: fileHandleText(block.attachment, context.attachments?.fileHostPath(block.attachment)),
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
else if (block.type === "image" && context.attachments) {
|
|
21
|
-
const stored = await context.attachments.readImage(block.attachment, context.signal);
|
|
22
|
-
out.push({
|
|
23
|
-
type: "image",
|
|
24
|
-
data: Buffer.from(stored.data).toString("base64"),
|
|
25
|
-
mimeType: block.attachment.mediaType,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return out;
|
|
30
|
-
}
|
|
31
|
-
function isToolResult(block) {
|
|
32
|
-
return block.type === "tool-result";
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Flatten one Harness history into the neutral shape.
|
|
36
|
-
*
|
|
37
|
-
* Two structural differences drive the whole mapping. The Harness has no
|
|
38
|
-
* `toolResult` role — a result is a `tool-result` block inside a user message —
|
|
39
|
-
* so those blocks are lifted into their own neutral messages. And its system
|
|
40
|
-
* prompt travels as a `system`-role message rather than a request field, so
|
|
41
|
-
* system text is returned separately for the caller to pass as the system slot.
|
|
42
|
-
*/
|
|
43
|
-
export async function toDevinMessages(messages, context = {}) {
|
|
44
|
-
const out = [];
|
|
45
|
-
const systemParts = [];
|
|
46
|
-
for (const message of messages) {
|
|
47
|
-
if (message.role === "system") {
|
|
48
|
-
for (const block of message.content)
|
|
49
|
-
if (block.type === "text")
|
|
50
|
-
systemParts.push(block.text);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (message.role === "assistant") {
|
|
54
|
-
const content = [];
|
|
55
|
-
for (const block of message.content) {
|
|
56
|
-
if (block.type === "text")
|
|
57
|
-
content.push({ type: "text", text: block.text });
|
|
58
|
-
else if (block.type === "reasoning")
|
|
59
|
-
content.push({ type: "thinking", thinking: block.text });
|
|
60
|
-
else if (block.type === "tool-call") {
|
|
61
|
-
let args;
|
|
62
|
-
try {
|
|
63
|
-
args = JSON.parse(block.arguments || "{}");
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
// A stored call whose arguments no longer parse is still part of
|
|
67
|
-
// the exchange the model must see; sending it with empty arguments
|
|
68
|
-
// keeps the call/result pairing, which dropping would break.
|
|
69
|
-
args = {};
|
|
70
|
-
}
|
|
71
|
-
content.push({ type: "toolCall", id: block.id, name: block.name, arguments: args });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
out.push({ role: "assistant", content });
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const results = message.content.filter(isToolResult);
|
|
78
|
-
const plain = message.content.filter((block) => !isToolResult(block));
|
|
79
|
-
if (plain.length > 0)
|
|
80
|
-
out.push({ role: "user", content: await userContent(plain, context) });
|
|
81
|
-
for (const result of results) {
|
|
82
|
-
out.push({
|
|
83
|
-
role: "toolResult",
|
|
84
|
-
toolCallId: result.toolCallId,
|
|
85
|
-
// The Harness correlates a result by id alone and carries no tool
|
|
86
|
-
// name on the block, so a placeholder is honest here.
|
|
87
|
-
toolName: "tool",
|
|
88
|
-
content: await userContent(result.content, context),
|
|
89
|
-
isError: result.isError === true,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return { messages: out, ...(systemParts.length > 0 ? { system: systemParts.join("\n\n") } : {}) };
|
|
94
|
-
}
|
|
2
|
+
// ABOUTME: The projection is shared by every dsh adapter and lives in ns-bridge-core/dsh.
|
|
3
|
+
export { toBridgeMessagesFromDsh as toDevinMessages, } from "ns-bridge-core/dsh";
|
|
95
4
|
//# sourceMappingURL=messages.js.map
|
package/dist/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,0FAA0F;AAE1F,OAAO,EAEL,uBAAuB,IAAI,eAAe,GAC3C,MAAM,oBAAoB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ns-dsh-llm-devin",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "DeepSeek Harness (dsh) LLM adapter adding Devin (Cognition Cascade) as a model provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/ngosangns/ns-
|
|
18
|
+
"url": "git+https://github.com/ngosangns/ns-bridge.git",
|
|
19
19
|
"directory": "packages/dsh-llm-devin"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
@@ -35,13 +35,14 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
38
|
-
"ns-devin-core": "0.1
|
|
38
|
+
"ns-devin-core": "0.2.1",
|
|
39
|
+
"ns-bridge-core": "0.1.0"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
42
|
-
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1 <0.
|
|
43
|
-
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1 <0.
|
|
44
|
-
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.
|
|
43
|
+
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1 <0.3.0",
|
|
44
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1 <0.3.0",
|
|
45
|
+
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.3.0"
|
|
45
46
|
},
|
|
46
47
|
"peerDependenciesMeta": {
|
|
47
48
|
"@deepseek-ai/dsh-attachment": {
|
|
@@ -52,10 +53,10 @@
|
|
|
52
53
|
}
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"@deepseek-ai/cordis": "4.0.
|
|
56
|
-
"@deepseek-ai/dsh-attachment": "0.
|
|
57
|
-
"@deepseek-ai/dsh-credentials": "0.
|
|
58
|
-
"@deepseek-ai/dsh-llm": "0.
|
|
56
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
57
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
59
60
|
"@types/node": "^22.20.4"
|
|
60
61
|
},
|
|
61
62
|
"scripts": {
|