@averyyy/pi-ai 0.85.1-piclient.4 → 0.85.1-piclient.6
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/dist/api/devin-catalog.d.ts +5 -0
- package/dist/api/devin-catalog.d.ts.map +1 -0
- package/dist/api/devin-catalog.js +114 -0
- package/dist/api/devin-catalog.js.map +1 -0
- package/dist/api/devin-context-map.d.ts +33 -0
- package/dist/api/devin-context-map.d.ts.map +1 -0
- package/dist/api/devin-context-map.js +80 -0
- package/dist/api/devin-context-map.js.map +1 -0
- package/dist/api/devin-jwt.d.ts +2 -0
- package/dist/api/devin-jwt.d.ts.map +1 -0
- package/dist/api/devin-jwt.js +23 -0
- package/dist/api/devin-jwt.js.map +1 -0
- package/dist/api/devin-metadata.d.ts +11 -0
- package/dist/api/devin-metadata.d.ts.map +1 -0
- package/dist/api/devin-metadata.js +25 -0
- package/dist/api/devin-metadata.js.map +1 -0
- package/dist/api/devin-stream.d.ts +34 -0
- package/dist/api/devin-stream.d.ts.map +1 -0
- package/dist/api/devin-stream.js +622 -0
- package/dist/api/devin-stream.js.map +1 -0
- package/dist/api/devin-thinking.d.ts +26 -0
- package/dist/api/devin-thinking.d.ts.map +1 -0
- package/dist/api/devin-thinking.js +54 -0
- package/dist/api/devin-thinking.js.map +1 -0
- package/dist/api/devin-wire.d.ts +16 -0
- package/dist/api/devin-wire.d.ts.map +1 -0
- package/dist/api/devin-wire.js +130 -0
- package/dist/api/devin-wire.js.map +1 -0
- package/dist/api/devin.d.ts +3 -0
- package/dist/api/devin.d.ts.map +1 -0
- package/dist/api/devin.js +3 -0
- package/dist/api/devin.js.map +1 -0
- package/dist/auth/oauth/devin-runtime.d.ts +2 -0
- package/dist/auth/oauth/devin-runtime.d.ts.map +1 -0
- package/dist/auth/oauth/devin-runtime.js +3 -0
- package/dist/auth/oauth/devin-runtime.js.map +1 -0
- package/dist/auth/oauth/devin.d.ts +5 -0
- package/dist/auth/oauth/devin.d.ts.map +1 -0
- package/dist/auth/oauth/devin.js +88 -0
- package/dist/auth/oauth/devin.js.map +1 -0
- package/dist/auth/oauth/load.d.ts +9 -0
- package/dist/auth/oauth/load.d.ts.map +1 -1
- package/dist/auth/oauth/load.js +11 -0
- package/dist/auth/oauth/load.js.map +1 -1
- package/dist/bun-oauth.d.ts.map +1 -1
- package/dist/bun-oauth.js +4 -0
- package/dist/bun-oauth.js.map +1 -1
- package/dist/compat.d.ts.map +1 -1
- package/dist/compat.js +2 -0
- package/dist/compat.js.map +1 -1
- package/dist/providers/all.d.ts.map +1 -1
- package/dist/providers/all.js +2 -0
- package/dist/providers/all.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/devin.d.ts +5 -0
- package/dist/providers/devin.d.ts.map +1 -0
- package/dist/providers/devin.js +38 -0
- package/dist/providers/devin.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Model } from "../types.ts";
|
|
2
|
+
/** Decode the published ClientModelConfig/ModelInfo wire fields, never model-name guesses. */
|
|
3
|
+
export declare function decodeDevinCatalog(payload: Buffer): Model<"devin">[];
|
|
4
|
+
export declare function discoverDevinModels(apiKey: string, signal: AbortSignal): Promise<Model<"devin">[]>;
|
|
5
|
+
//# sourceMappingURL=devin-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-catalog.d.ts","sourceRoot":"","sources":["../../src/api/devin-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,aAAa,CAAC;AAc/E,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAsEpE;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAoBxG","sourcesContent":["import type { Model, ModelThinkingLevel, ThinkingLevelMap } from \"../types.ts\";\nimport { buildMetadata, DEVIN_BASE_URL } from \"./devin-metadata.ts\";\nimport { encodeMessage, iterFields, type ProtoField } from \"./devin-wire.ts\";\n\nfunction bytes(fields: ProtoField[], number: number): Buffer {\n\tconst value = fields.find((field) => field.num === number)?.value;\n\treturn Buffer.isBuffer(value) ? value : Buffer.alloc(0);\n}\n\nfunction integer(fields: ProtoField[], number: number): number {\n\tconst value = fields.find((field) => field.num === number)?.value;\n\treturn typeof value === \"bigint\" ? Number(value) : 0;\n}\n\n/** Decode the published ClientModelConfig/ModelInfo wire fields, never model-name guesses. */\nexport function decodeDevinCatalog(payload: Buffer): Model<\"devin\">[] {\n\tconst models: Model<\"devin\">[] = [];\n\tconst seen = new Set<string>();\n\tfor (const field of iterFields(payload)) {\n\t\tif (field.num !== 1 || !Buffer.isBuffer(field.value)) continue;\n\t\tconst config = [...iterFields(field.value)];\n\t\tif (integer(config, 4)) continue;\n\t\tconst info = [...iterFields(bytes(config, 23))];\n\t\t// Router and internal slots require a separate assignment protocol.\n\t\tif (integer(info, 2) || integer(info, 25) || [3, 4, 6].includes(integer(info, 22))) continue;\n\t\tconst id = bytes(config, 22).toString(\"utf8\");\n\t\tconst contextWindow = integer(config, 18);\n\t\tconst maxTokens = integer(info, 13);\n\t\tconst features = [...iterFields(bytes(info, 6))];\n\t\tif (!id || !contextWindow || !maxTokens || !features.length)\n\t\t\tthrow new Error(\"Devin returned an incomplete model definition\");\n\t\tif (seen.has(id)) throw new Error(`Devin returned duplicate model ${id}`);\n\t\tseen.add(id);\n\t\tconst thinkingLevelMap: ThinkingLevelMap = {\n\t\t\toff: null,\n\t\t\tminimal: null,\n\t\t\tlow: null,\n\t\t\tmedium: null,\n\t\t\thigh: null,\n\t\t\txhigh: null,\n\t\t\tmax: null,\n\t\t};\n\t\tconst family = [...iterFields(bytes(config, 30))];\n\t\tfor (const entry of family.filter((value) => value.num === 2)) {\n\t\t\tif (!Buffer.isBuffer(entry.value)) continue;\n\t\t\tconst parts = [...iterFields(entry.value)];\n\t\t\tconst key = bytes(parts, 1).toString(\"utf8\").toLowerCase();\n\t\t\tif (key !== \"effort\" && key !== \"reasoning effort\") continue;\n\t\t\tconst value = [...iterFields(bytes(parts, 2))];\n\t\t\tconst label = bytes(value, 2).toString(\"utf8\").toLowerCase();\n\t\t\tconst level = label === \"none\" || label === \"no thinking\" ? \"off\" : label;\n\t\t\tif (Object.hasOwn(thinkingLevelMap, level)) thinkingLevelMap[level as ModelThinkingLevel] = id;\n\t\t}\n\t\tconst cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\t\tfor (const dimension of config.filter((value) => value.num === 32)) {\n\t\t\tif (!Buffer.isBuffer(dimension.value)) continue;\n\t\t\tconst values = [...iterFields(dimension.value)];\n\t\t\tif (![1, 2].includes(integer(values, 6))) continue;\n\t\t\tconst label = bytes(values, 1).toString(\"utf8\");\n\t\t\tconst rate = bytes(values, 2);\n\t\t\tconst denominator = bytes(values, 3).toString(\"utf8\");\n\t\t\tif (denominator !== \"1M tokens\" || rate.length !== 4)\n\t\t\t\tthrow new Error(`Unsupported Devin pricing dimension for ${id}`);\n\t\t\tconst value = Math.round(rate.readFloatLE() * 1e6) / 1e6;\n\t\t\tif (!Number.isFinite(value) || value < 0) throw new Error(\"Invalid Devin model price\");\n\t\t\tif (label === \"Input\") cost.input = value;\n\t\t\telse if (label === \"Output\") cost.output = value;\n\t\t\telse if (label === \"Cached input\") cost.cacheRead = value;\n\t\t}\n\t\tmodels.push({\n\t\t\tid,\n\t\t\tname: bytes(config, 1).toString(\"utf8\"),\n\t\t\tprovider: \"devin\",\n\t\t\tapi: \"devin\",\n\t\t\tbaseUrl: DEVIN_BASE_URL,\n\t\t\treasoning: !!integer(features, 15),\n\t\t\tthinkingLevelMap,\n\t\t\tinput: integer(features, 11) ? [\"text\", \"image\"] : [\"text\"],\n\t\t\tcontextWindow,\n\t\t\tmaxTokens,\n\t\t\tcost,\n\t\t});\n\t}\n\tif (!models.length) throw new Error(\"Devin returned no usable models; check the account and protocol version\");\n\treturn models.sort((a, b) => a.id.localeCompare(b.id));\n}\n\nexport async function discoverDevinModels(apiKey: string, signal: AbortSignal): Promise<Model<\"devin\">[]> {\n\tconst response = await fetch(`${DEVIN_BASE_URL}/exa.api_server_pb.ApiServerService/GetCliModelConfigs`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"content-type\": \"application/proto\", \"connect-protocol-version\": \"1\" },\n\t\tbody: new Uint8Array(\n\t\t\tencodeMessage(\n\t\t\t\t1,\n\t\t\t\tbuildMetadata({\n\t\t\t\t\tapiKey,\n\t\t\t\t\tdiscovery: true,\n\t\t\t\t\tsessionId: crypto.randomUUID(),\n\t\t\t\t\trequestId: BigInt(Date.now()),\n\t\t\t\t\ttriggerId: crypto.randomUUID(),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tsignal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),\n\t});\n\tif (!response.ok) throw new Error(`Devin model discovery failed (HTTP ${response.status})`);\n\treturn decodeDevinCatalog(Buffer.from(await response.arrayBuffer()));\n}\n"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { buildMetadata, DEVIN_BASE_URL } from "./devin-metadata.js";
|
|
2
|
+
import { encodeMessage, iterFields } from "./devin-wire.js";
|
|
3
|
+
function bytes(fields, number) {
|
|
4
|
+
const value = fields.find((field) => field.num === number)?.value;
|
|
5
|
+
return Buffer.isBuffer(value) ? value : Buffer.alloc(0);
|
|
6
|
+
}
|
|
7
|
+
function integer(fields, number) {
|
|
8
|
+
const value = fields.find((field) => field.num === number)?.value;
|
|
9
|
+
return typeof value === "bigint" ? Number(value) : 0;
|
|
10
|
+
}
|
|
11
|
+
/** Decode the published ClientModelConfig/ModelInfo wire fields, never model-name guesses. */
|
|
12
|
+
export function decodeDevinCatalog(payload) {
|
|
13
|
+
const models = [];
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
for (const field of iterFields(payload)) {
|
|
16
|
+
if (field.num !== 1 || !Buffer.isBuffer(field.value))
|
|
17
|
+
continue;
|
|
18
|
+
const config = [...iterFields(field.value)];
|
|
19
|
+
if (integer(config, 4))
|
|
20
|
+
continue;
|
|
21
|
+
const info = [...iterFields(bytes(config, 23))];
|
|
22
|
+
// Router and internal slots require a separate assignment protocol.
|
|
23
|
+
if (integer(info, 2) || integer(info, 25) || [3, 4, 6].includes(integer(info, 22)))
|
|
24
|
+
continue;
|
|
25
|
+
const id = bytes(config, 22).toString("utf8");
|
|
26
|
+
const contextWindow = integer(config, 18);
|
|
27
|
+
const maxTokens = integer(info, 13);
|
|
28
|
+
const features = [...iterFields(bytes(info, 6))];
|
|
29
|
+
if (!id || !contextWindow || !maxTokens || !features.length)
|
|
30
|
+
throw new Error("Devin returned an incomplete model definition");
|
|
31
|
+
if (seen.has(id))
|
|
32
|
+
throw new Error(`Devin returned duplicate model ${id}`);
|
|
33
|
+
seen.add(id);
|
|
34
|
+
const thinkingLevelMap = {
|
|
35
|
+
off: null,
|
|
36
|
+
minimal: null,
|
|
37
|
+
low: null,
|
|
38
|
+
medium: null,
|
|
39
|
+
high: null,
|
|
40
|
+
xhigh: null,
|
|
41
|
+
max: null,
|
|
42
|
+
};
|
|
43
|
+
const family = [...iterFields(bytes(config, 30))];
|
|
44
|
+
for (const entry of family.filter((value) => value.num === 2)) {
|
|
45
|
+
if (!Buffer.isBuffer(entry.value))
|
|
46
|
+
continue;
|
|
47
|
+
const parts = [...iterFields(entry.value)];
|
|
48
|
+
const key = bytes(parts, 1).toString("utf8").toLowerCase();
|
|
49
|
+
if (key !== "effort" && key !== "reasoning effort")
|
|
50
|
+
continue;
|
|
51
|
+
const value = [...iterFields(bytes(parts, 2))];
|
|
52
|
+
const label = bytes(value, 2).toString("utf8").toLowerCase();
|
|
53
|
+
const level = label === "none" || label === "no thinking" ? "off" : label;
|
|
54
|
+
if (Object.hasOwn(thinkingLevelMap, level))
|
|
55
|
+
thinkingLevelMap[level] = id;
|
|
56
|
+
}
|
|
57
|
+
const cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
58
|
+
for (const dimension of config.filter((value) => value.num === 32)) {
|
|
59
|
+
if (!Buffer.isBuffer(dimension.value))
|
|
60
|
+
continue;
|
|
61
|
+
const values = [...iterFields(dimension.value)];
|
|
62
|
+
if (![1, 2].includes(integer(values, 6)))
|
|
63
|
+
continue;
|
|
64
|
+
const label = bytes(values, 1).toString("utf8");
|
|
65
|
+
const rate = bytes(values, 2);
|
|
66
|
+
const denominator = bytes(values, 3).toString("utf8");
|
|
67
|
+
if (denominator !== "1M tokens" || rate.length !== 4)
|
|
68
|
+
throw new Error(`Unsupported Devin pricing dimension for ${id}`);
|
|
69
|
+
const value = Math.round(rate.readFloatLE() * 1e6) / 1e6;
|
|
70
|
+
if (!Number.isFinite(value) || value < 0)
|
|
71
|
+
throw new Error("Invalid Devin model price");
|
|
72
|
+
if (label === "Input")
|
|
73
|
+
cost.input = value;
|
|
74
|
+
else if (label === "Output")
|
|
75
|
+
cost.output = value;
|
|
76
|
+
else if (label === "Cached input")
|
|
77
|
+
cost.cacheRead = value;
|
|
78
|
+
}
|
|
79
|
+
models.push({
|
|
80
|
+
id,
|
|
81
|
+
name: bytes(config, 1).toString("utf8"),
|
|
82
|
+
provider: "devin",
|
|
83
|
+
api: "devin",
|
|
84
|
+
baseUrl: DEVIN_BASE_URL,
|
|
85
|
+
reasoning: !!integer(features, 15),
|
|
86
|
+
thinkingLevelMap,
|
|
87
|
+
input: integer(features, 11) ? ["text", "image"] : ["text"],
|
|
88
|
+
contextWindow,
|
|
89
|
+
maxTokens,
|
|
90
|
+
cost,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (!models.length)
|
|
94
|
+
throw new Error("Devin returned no usable models; check the account and protocol version");
|
|
95
|
+
return models.sort((a, b) => a.id.localeCompare(b.id));
|
|
96
|
+
}
|
|
97
|
+
export async function discoverDevinModels(apiKey, signal) {
|
|
98
|
+
const response = await fetch(`${DEVIN_BASE_URL}/exa.api_server_pb.ApiServerService/GetCliModelConfigs`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "content-type": "application/proto", "connect-protocol-version": "1" },
|
|
101
|
+
body: new Uint8Array(encodeMessage(1, buildMetadata({
|
|
102
|
+
apiKey,
|
|
103
|
+
discovery: true,
|
|
104
|
+
sessionId: crypto.randomUUID(),
|
|
105
|
+
requestId: BigInt(Date.now()),
|
|
106
|
+
triggerId: crypto.randomUUID(),
|
|
107
|
+
}))),
|
|
108
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),
|
|
109
|
+
});
|
|
110
|
+
if (!response.ok)
|
|
111
|
+
throw new Error(`Devin model discovery failed (HTTP ${response.status})`);
|
|
112
|
+
return decodeDevinCatalog(Buffer.from(await response.arrayBuffer()));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=devin-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-catalog.js","sourceRoot":"","sources":["../../src/api/devin-catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model, ModelThinkingLevel, ThinkingLevelMap } from \"../types.ts\";\nimport { buildMetadata, DEVIN_BASE_URL } from \"./devin-metadata.ts\";\nimport { encodeMessage, iterFields, type ProtoField } from \"./devin-wire.ts\";\n\nfunction bytes(fields: ProtoField[], number: number): Buffer {\n\tconst value = fields.find((field) => field.num === number)?.value;\n\treturn Buffer.isBuffer(value) ? value : Buffer.alloc(0);\n}\n\nfunction integer(fields: ProtoField[], number: number): number {\n\tconst value = fields.find((field) => field.num === number)?.value;\n\treturn typeof value === \"bigint\" ? Number(value) : 0;\n}\n\n/** Decode the published ClientModelConfig/ModelInfo wire fields, never model-name guesses. */\nexport function decodeDevinCatalog(payload: Buffer): Model<\"devin\">[] {\n\tconst models: Model<\"devin\">[] = [];\n\tconst seen = new Set<string>();\n\tfor (const field of iterFields(payload)) {\n\t\tif (field.num !== 1 || !Buffer.isBuffer(field.value)) continue;\n\t\tconst config = [...iterFields(field.value)];\n\t\tif (integer(config, 4)) continue;\n\t\tconst info = [...iterFields(bytes(config, 23))];\n\t\t// Router and internal slots require a separate assignment protocol.\n\t\tif (integer(info, 2) || integer(info, 25) || [3, 4, 6].includes(integer(info, 22))) continue;\n\t\tconst id = bytes(config, 22).toString(\"utf8\");\n\t\tconst contextWindow = integer(config, 18);\n\t\tconst maxTokens = integer(info, 13);\n\t\tconst features = [...iterFields(bytes(info, 6))];\n\t\tif (!id || !contextWindow || !maxTokens || !features.length)\n\t\t\tthrow new Error(\"Devin returned an incomplete model definition\");\n\t\tif (seen.has(id)) throw new Error(`Devin returned duplicate model ${id}`);\n\t\tseen.add(id);\n\t\tconst thinkingLevelMap: ThinkingLevelMap = {\n\t\t\toff: null,\n\t\t\tminimal: null,\n\t\t\tlow: null,\n\t\t\tmedium: null,\n\t\t\thigh: null,\n\t\t\txhigh: null,\n\t\t\tmax: null,\n\t\t};\n\t\tconst family = [...iterFields(bytes(config, 30))];\n\t\tfor (const entry of family.filter((value) => value.num === 2)) {\n\t\t\tif (!Buffer.isBuffer(entry.value)) continue;\n\t\t\tconst parts = [...iterFields(entry.value)];\n\t\t\tconst key = bytes(parts, 1).toString(\"utf8\").toLowerCase();\n\t\t\tif (key !== \"effort\" && key !== \"reasoning effort\") continue;\n\t\t\tconst value = [...iterFields(bytes(parts, 2))];\n\t\t\tconst label = bytes(value, 2).toString(\"utf8\").toLowerCase();\n\t\t\tconst level = label === \"none\" || label === \"no thinking\" ? \"off\" : label;\n\t\t\tif (Object.hasOwn(thinkingLevelMap, level)) thinkingLevelMap[level as ModelThinkingLevel] = id;\n\t\t}\n\t\tconst cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\t\tfor (const dimension of config.filter((value) => value.num === 32)) {\n\t\t\tif (!Buffer.isBuffer(dimension.value)) continue;\n\t\t\tconst values = [...iterFields(dimension.value)];\n\t\t\tif (![1, 2].includes(integer(values, 6))) continue;\n\t\t\tconst label = bytes(values, 1).toString(\"utf8\");\n\t\t\tconst rate = bytes(values, 2);\n\t\t\tconst denominator = bytes(values, 3).toString(\"utf8\");\n\t\t\tif (denominator !== \"1M tokens\" || rate.length !== 4)\n\t\t\t\tthrow new Error(`Unsupported Devin pricing dimension for ${id}`);\n\t\t\tconst value = Math.round(rate.readFloatLE() * 1e6) / 1e6;\n\t\t\tif (!Number.isFinite(value) || value < 0) throw new Error(\"Invalid Devin model price\");\n\t\t\tif (label === \"Input\") cost.input = value;\n\t\t\telse if (label === \"Output\") cost.output = value;\n\t\t\telse if (label === \"Cached input\") cost.cacheRead = value;\n\t\t}\n\t\tmodels.push({\n\t\t\tid,\n\t\t\tname: bytes(config, 1).toString(\"utf8\"),\n\t\t\tprovider: \"devin\",\n\t\t\tapi: \"devin\",\n\t\t\tbaseUrl: DEVIN_BASE_URL,\n\t\t\treasoning: !!integer(features, 15),\n\t\t\tthinkingLevelMap,\n\t\t\tinput: integer(features, 11) ? [\"text\", \"image\"] : [\"text\"],\n\t\t\tcontextWindow,\n\t\t\tmaxTokens,\n\t\t\tcost,\n\t\t});\n\t}\n\tif (!models.length) throw new Error(\"Devin returned no usable models; check the account and protocol version\");\n\treturn models.sort((a, b) => a.id.localeCompare(b.id));\n}\n\nexport async function discoverDevinModels(apiKey: string, signal: AbortSignal): Promise<Model<\"devin\">[]> {\n\tconst response = await fetch(`${DEVIN_BASE_URL}/exa.api_server_pb.ApiServerService/GetCliModelConfigs`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"content-type\": \"application/proto\", \"connect-protocol-version\": \"1\" },\n\t\tbody: new Uint8Array(\n\t\t\tencodeMessage(\n\t\t\t\t1,\n\t\t\t\tbuildMetadata({\n\t\t\t\t\tapiKey,\n\t\t\t\t\tdiscovery: true,\n\t\t\t\t\tsessionId: crypto.randomUUID(),\n\t\t\t\t\trequestId: BigInt(Date.now()),\n\t\t\t\t\ttriggerId: crypto.randomUUID(),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tsignal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),\n\t});\n\tif (!response.ok) throw new Error(`Devin model discovery failed (HTTP ${response.status})`);\n\treturn decodeDevinCatalog(Buffer.from(await response.arrayBuffer()));\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Context } from "../index.ts";
|
|
2
|
+
import { type ChatThinking } from "./devin-thinking.ts";
|
|
3
|
+
export interface ContentPart {
|
|
4
|
+
type: "text" | "image";
|
|
5
|
+
text?: string;
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
base64Data?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatHistoryItem {
|
|
10
|
+
role: "user" | "assistant" | "tool";
|
|
11
|
+
content: string | ContentPart[];
|
|
12
|
+
tool_call_id?: string;
|
|
13
|
+
tool_calls?: Array<{
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
arguments: string;
|
|
17
|
+
}>;
|
|
18
|
+
/** Prior reasoning, replayed so the server can verify and continue it. */
|
|
19
|
+
thinking?: ChatThinking;
|
|
20
|
+
}
|
|
21
|
+
export interface ToolDef {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
parameters: unknown;
|
|
25
|
+
}
|
|
26
|
+
export interface MappedChat {
|
|
27
|
+
/** Goes into GetChatMessageRequest.prompt, the server's system slot. */
|
|
28
|
+
systemPrompt?: string;
|
|
29
|
+
messages: ChatHistoryItem[];
|
|
30
|
+
tools: ToolDef[];
|
|
31
|
+
}
|
|
32
|
+
export declare function mapContextToChat(context: Context, modelId?: string): MappedChat;
|
|
33
|
+
//# sourceMappingURL=devin-context-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-context-map.d.ts","sourceRoot":"","sources":["../../src/api/devin-context-map.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,KAAK,YAAY,EAA2B,MAAM,qBAAqB,CAAC;AAEjF,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,OAAO,EAAE,CAAC;CACjB;AAgBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAgE/E","sourcesContent":["/*\nMIT License\n\nCopyright (c) 2026 Kashyab Ambarani\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\nimport type { Context, Message, Tool } from \"../index.ts\";\nimport { type ChatThinking, unpackThinkingSignature } from \"./devin-thinking.ts\";\n\nexport interface ContentPart {\n\ttype: \"text\" | \"image\";\n\ttext?: string;\n\tmimeType?: string;\n\tbase64Data?: string;\n}\n\nexport interface ChatHistoryItem {\n\trole: \"user\" | \"assistant\" | \"tool\";\n\tcontent: string | ContentPart[];\n\ttool_call_id?: string;\n\ttool_calls?: Array<{ id: string; name: string; arguments: string }>;\n\t/** Prior reasoning, replayed so the server can verify and continue it. */\n\tthinking?: ChatThinking;\n}\n\nexport interface ToolDef {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface MappedChat {\n\t/** Goes into GetChatMessageRequest.prompt, the server's system slot. */\n\tsystemPrompt?: string;\n\tmessages: ChatHistoryItem[];\n\ttools: ToolDef[];\n}\n\nfunction userContent(content: Message[\"content\"]): string | ContentPart[] {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: ContentPart[] = [];\n\tfor (const part of content) {\n\t\tif (!part || typeof part !== \"object\") continue;\n\t\tif (part.type === \"text\") parts.push({ type: \"text\", text: part.text });\n\t\tif (part.type === \"image\") {\n\t\t\tparts.push({ type: \"image\", mimeType: part.mimeType, base64Data: part.data });\n\t\t}\n\t}\n\treturn parts;\n}\n\nexport function mapContextToChat(context: Context, modelId?: string): MappedChat {\n\tconst messages: ChatHistoryItem[] = [];\n\n\tfor (const message of context.messages) {\n\t\tif (message.role === \"user\") {\n\t\t\tmessages.push({ role: \"user\", content: userContent(message.content) });\n\t\t\tcontinue;\n\t\t}\n\t\tif (message.role === \"assistant\") {\n\t\t\tconst texts: string[] = [];\n\t\t\tconst toolCalls: Array<{ id: string; name: string; arguments: string }> = [];\n\t\t\tlet thinking: ChatThinking | undefined;\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type === \"text\") texts.push(part.text);\n\t\t\t\tif (part.type === \"toolCall\") {\n\t\t\t\t\ttoolCalls.push({\n\t\t\t\t\t\tid: part.id,\n\t\t\t\t\t\tname: part.name,\n\t\t\t\t\t\targuments: JSON.stringify(part.arguments ?? {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tpart.type === \"thinking\" &&\n\t\t\t\t\tmessage.provider === \"devin\" &&\n\t\t\t\t\tmessage.api === \"devin\" &&\n\t\t\t\t\t(!modelId || message.model === modelId)\n\t\t\t\t) {\n\t\t\t\t\tconst decoded = unpackThinkingSignature(part.thinkingSignature);\n\t\t\t\t\t// One thinking slot per message on the wire, and an unsigned trace is\n\t\t\t\t\t// not replayable — keep the newest block that the server can verify.\n\t\t\t\t\tif (decoded.signature) {\n\t\t\t\t\t\tthinking = {\n\t\t\t\t\t\t\ttext: part.thinking,\n\t\t\t\t\t\t\tsignature: decoded.signature,\n\t\t\t\t\t\t\tsignatureType: decoded.signatureType,\n\t\t\t\t\t\t\tredacted: part.redacted,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmessages.push({\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: texts.join(\"\\n\"),\n\t\t\t\ttool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n\t\t\t\tthinking,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (message.role === \"toolResult\") {\n\t\t\tmessages.push({\n\t\t\t\trole: \"tool\",\n\t\t\t\tcontent: userContent(message.content),\n\t\t\t\ttool_call_id: message.toolCallId,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst tools: ToolDef[] = (context.tools ?? []).map((tool: Tool) => ({\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tparameters: tool.parameters,\n\t}));\n\n\treturn { systemPrompt: context.systemPrompt || undefined, messages, tools };\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { unpackThinkingSignature } from "./devin-thinking.js";
|
|
2
|
+
function userContent(content) {
|
|
3
|
+
if (typeof content === "string")
|
|
4
|
+
return content;
|
|
5
|
+
if (!Array.isArray(content))
|
|
6
|
+
return "";
|
|
7
|
+
const parts = [];
|
|
8
|
+
for (const part of content) {
|
|
9
|
+
if (!part || typeof part !== "object")
|
|
10
|
+
continue;
|
|
11
|
+
if (part.type === "text")
|
|
12
|
+
parts.push({ type: "text", text: part.text });
|
|
13
|
+
if (part.type === "image") {
|
|
14
|
+
parts.push({ type: "image", mimeType: part.mimeType, base64Data: part.data });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return parts;
|
|
18
|
+
}
|
|
19
|
+
export function mapContextToChat(context, modelId) {
|
|
20
|
+
const messages = [];
|
|
21
|
+
for (const message of context.messages) {
|
|
22
|
+
if (message.role === "user") {
|
|
23
|
+
messages.push({ role: "user", content: userContent(message.content) });
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (message.role === "assistant") {
|
|
27
|
+
const texts = [];
|
|
28
|
+
const toolCalls = [];
|
|
29
|
+
let thinking;
|
|
30
|
+
for (const part of message.content) {
|
|
31
|
+
if (part.type === "text")
|
|
32
|
+
texts.push(part.text);
|
|
33
|
+
if (part.type === "toolCall") {
|
|
34
|
+
toolCalls.push({
|
|
35
|
+
id: part.id,
|
|
36
|
+
name: part.name,
|
|
37
|
+
arguments: JSON.stringify(part.arguments ?? {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (part.type === "thinking" &&
|
|
41
|
+
message.provider === "devin" &&
|
|
42
|
+
message.api === "devin" &&
|
|
43
|
+
(!modelId || message.model === modelId)) {
|
|
44
|
+
const decoded = unpackThinkingSignature(part.thinkingSignature);
|
|
45
|
+
// One thinking slot per message on the wire, and an unsigned trace is
|
|
46
|
+
// not replayable — keep the newest block that the server can verify.
|
|
47
|
+
if (decoded.signature) {
|
|
48
|
+
thinking = {
|
|
49
|
+
text: part.thinking,
|
|
50
|
+
signature: decoded.signature,
|
|
51
|
+
signatureType: decoded.signatureType,
|
|
52
|
+
redacted: part.redacted,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
messages.push({
|
|
58
|
+
role: "assistant",
|
|
59
|
+
content: texts.join("\n"),
|
|
60
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
61
|
+
thinking,
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (message.role === "toolResult") {
|
|
66
|
+
messages.push({
|
|
67
|
+
role: "tool",
|
|
68
|
+
content: userContent(message.content),
|
|
69
|
+
tool_call_id: message.toolCallId,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const tools = (context.tools ?? []).map((tool) => ({
|
|
74
|
+
name: tool.name,
|
|
75
|
+
description: tool.description,
|
|
76
|
+
parameters: tool.parameters,
|
|
77
|
+
}));
|
|
78
|
+
return { systemPrompt: context.systemPrompt || undefined, messages, tools };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=devin-context-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-context-map.js","sourceRoot":"","sources":["../../src/api/devin-context-map.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AA+BjF,SAAS,WAAW,CAAC,OAA2B,EAA0B;IACzE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAS;QAChD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACxE,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/E,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAAE,OAAgB,EAAc;IAChF,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACvE,SAAS;QACV,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,MAAM,SAAS,GAA2D,EAAE,CAAC;YAC7E,IAAI,QAAkC,CAAC;YACvC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAChD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBAC9B,SAAS,CAAC,IAAI,CAAC;wBACd,EAAE,EAAE,IAAI,CAAC,EAAE;wBACX,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;qBAC/C,CAAC,CAAC;gBACJ,CAAC;gBACD,IACC,IAAI,CAAC,IAAI,KAAK,UAAU;oBACxB,OAAO,CAAC,QAAQ,KAAK,OAAO;oBAC5B,OAAO,CAAC,GAAG,KAAK,OAAO;oBACvB,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,EACtC,CAAC;oBACF,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBAChE,sEAAsE;oBACtE,uEAAqE;oBACrE,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;wBACvB,QAAQ,GAAG;4BACV,IAAI,EAAE,IAAI,CAAC,QAAQ;4BACnB,SAAS,EAAE,OAAO,CAAC,SAAS;4BAC5B,aAAa,EAAE,OAAO,CAAC,aAAa;4BACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;yBACvB,CAAC;oBACH,CAAC;gBACF,CAAC;YACF,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;gBACzB,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;gBACxD,QAAQ;aACR,CAAC,CAAC;YACH,SAAS;QACV,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC;gBACrC,YAAY,EAAE,OAAO,CAAC,UAAU;aAChC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,MAAM,KAAK,GAAc,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAU,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;KAC3B,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAAA,CAC5E","sourcesContent":["/*\nMIT License\n\nCopyright (c) 2026 Kashyab Ambarani\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\nimport type { Context, Message, Tool } from \"../index.ts\";\nimport { type ChatThinking, unpackThinkingSignature } from \"./devin-thinking.ts\";\n\nexport interface ContentPart {\n\ttype: \"text\" | \"image\";\n\ttext?: string;\n\tmimeType?: string;\n\tbase64Data?: string;\n}\n\nexport interface ChatHistoryItem {\n\trole: \"user\" | \"assistant\" | \"tool\";\n\tcontent: string | ContentPart[];\n\ttool_call_id?: string;\n\ttool_calls?: Array<{ id: string; name: string; arguments: string }>;\n\t/** Prior reasoning, replayed so the server can verify and continue it. */\n\tthinking?: ChatThinking;\n}\n\nexport interface ToolDef {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface MappedChat {\n\t/** Goes into GetChatMessageRequest.prompt, the server's system slot. */\n\tsystemPrompt?: string;\n\tmessages: ChatHistoryItem[];\n\ttools: ToolDef[];\n}\n\nfunction userContent(content: Message[\"content\"]): string | ContentPart[] {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: ContentPart[] = [];\n\tfor (const part of content) {\n\t\tif (!part || typeof part !== \"object\") continue;\n\t\tif (part.type === \"text\") parts.push({ type: \"text\", text: part.text });\n\t\tif (part.type === \"image\") {\n\t\t\tparts.push({ type: \"image\", mimeType: part.mimeType, base64Data: part.data });\n\t\t}\n\t}\n\treturn parts;\n}\n\nexport function mapContextToChat(context: Context, modelId?: string): MappedChat {\n\tconst messages: ChatHistoryItem[] = [];\n\n\tfor (const message of context.messages) {\n\t\tif (message.role === \"user\") {\n\t\t\tmessages.push({ role: \"user\", content: userContent(message.content) });\n\t\t\tcontinue;\n\t\t}\n\t\tif (message.role === \"assistant\") {\n\t\t\tconst texts: string[] = [];\n\t\t\tconst toolCalls: Array<{ id: string; name: string; arguments: string }> = [];\n\t\t\tlet thinking: ChatThinking | undefined;\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type === \"text\") texts.push(part.text);\n\t\t\t\tif (part.type === \"toolCall\") {\n\t\t\t\t\ttoolCalls.push({\n\t\t\t\t\t\tid: part.id,\n\t\t\t\t\t\tname: part.name,\n\t\t\t\t\t\targuments: JSON.stringify(part.arguments ?? {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tpart.type === \"thinking\" &&\n\t\t\t\t\tmessage.provider === \"devin\" &&\n\t\t\t\t\tmessage.api === \"devin\" &&\n\t\t\t\t\t(!modelId || message.model === modelId)\n\t\t\t\t) {\n\t\t\t\t\tconst decoded = unpackThinkingSignature(part.thinkingSignature);\n\t\t\t\t\t// One thinking slot per message on the wire, and an unsigned trace is\n\t\t\t\t\t// not replayable — keep the newest block that the server can verify.\n\t\t\t\t\tif (decoded.signature) {\n\t\t\t\t\t\tthinking = {\n\t\t\t\t\t\t\ttext: part.thinking,\n\t\t\t\t\t\t\tsignature: decoded.signature,\n\t\t\t\t\t\t\tsignatureType: decoded.signatureType,\n\t\t\t\t\t\t\tredacted: part.redacted,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmessages.push({\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: texts.join(\"\\n\"),\n\t\t\t\ttool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n\t\t\t\tthinking,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (message.role === \"toolResult\") {\n\t\t\tmessages.push({\n\t\t\t\trole: \"tool\",\n\t\t\t\tcontent: userContent(message.content),\n\t\t\t\ttool_call_id: message.toolCallId,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst tools: ToolDef[] = (context.tools ?? []).map((tool: Tool) => ({\n\t\tname: tool.name,\n\t\tdescription: tool.description,\n\t\tparameters: tool.parameters,\n\t}));\n\n\treturn { systemPrompt: context.systemPrompt || undefined, messages, tools };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-jwt.d.ts","sourceRoot":"","sources":["../../src/api/devin-jwt.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,EACpB,SAAS,eAAQ,GACf,OAAO,CAAC,MAAM,CAAC,CAyBjB","sourcesContent":["import { buildMetadata } from \"./devin-metadata.ts\";\nimport { encodeMessage, iterFields } from \"./devin-wire.ts\";\n\nexport async function mintUserJwt(\n\tapiKey: string,\n\thost: string,\n\tsignal?: AbortSignal,\n\tfetchImpl = fetch,\n): Promise<string> {\n\tconst response = await fetchImpl(`${host}/exa.auth_pb.AuthService/GetUserJwt`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"content-type\": \"application/proto\", \"connect-protocol-version\": \"1\" },\n\t\tbody: new Uint8Array(\n\t\t\tencodeMessage(\n\t\t\t\t1,\n\t\t\t\tbuildMetadata({\n\t\t\t\t\tapiKey,\n\t\t\t\t\tsessionId: crypto.randomUUID(),\n\t\t\t\t\trequestId: BigInt(Date.now()),\n\t\t\t\t\ttriggerId: crypto.randomUUID(),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tsignal,\n\t});\n\tif (!response.ok)\n\t\tthrow new Error(\n\t\t\t`Devin authentication failed (HTTP ${response.status}); run /login devin if the session was revoked`,\n\t\t);\n\tfor (const field of iterFields(Buffer.from(await response.arrayBuffer()))) {\n\t\tif (field.num === 1 && Buffer.isBuffer(field.value) && field.value.length) return field.value.toString(\"utf8\");\n\t}\n\tthrow new Error(\"Devin authentication returned no user JWT\");\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { buildMetadata } from "./devin-metadata.js";
|
|
2
|
+
import { encodeMessage, iterFields } from "./devin-wire.js";
|
|
3
|
+
export async function mintUserJwt(apiKey, host, signal, fetchImpl = fetch) {
|
|
4
|
+
const response = await fetchImpl(`${host}/exa.auth_pb.AuthService/GetUserJwt`, {
|
|
5
|
+
method: "POST",
|
|
6
|
+
headers: { "content-type": "application/proto", "connect-protocol-version": "1" },
|
|
7
|
+
body: new Uint8Array(encodeMessage(1, buildMetadata({
|
|
8
|
+
apiKey,
|
|
9
|
+
sessionId: crypto.randomUUID(),
|
|
10
|
+
requestId: BigInt(Date.now()),
|
|
11
|
+
triggerId: crypto.randomUUID(),
|
|
12
|
+
}))),
|
|
13
|
+
signal,
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok)
|
|
16
|
+
throw new Error(`Devin authentication failed (HTTP ${response.status}); run /login devin if the session was revoked`);
|
|
17
|
+
for (const field of iterFields(Buffer.from(await response.arrayBuffer()))) {
|
|
18
|
+
if (field.num === 1 && Buffer.isBuffer(field.value) && field.value.length)
|
|
19
|
+
return field.value.toString("utf8");
|
|
20
|
+
}
|
|
21
|
+
throw new Error("Devin authentication returned no user JWT");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=devin-jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-jwt.js","sourceRoot":"","sources":["../../src/api/devin-jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,MAAc,EACd,IAAY,EACZ,MAAoB,EACpB,SAAS,GAAG,KAAK,EACC;IAClB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,qCAAqC,EAAE;QAC9E,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,GAAG,EAAE;QACjF,IAAI,EAAE,IAAI,UAAU,CACnB,aAAa,CACZ,CAAC,EACD,aAAa,CAAC;YACb,MAAM;YACN,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE;YAC9B,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE;SAC9B,CAAC,CACF,CACD;QACD,MAAM;KACN,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE;QACf,MAAM,IAAI,KAAK,CACd,qCAAqC,QAAQ,CAAC,MAAM,gDAAgD,CACpG,CAAC;IACH,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;QAC3E,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;AAAA,CAC7D","sourcesContent":["import { buildMetadata } from \"./devin-metadata.ts\";\nimport { encodeMessage, iterFields } from \"./devin-wire.ts\";\n\nexport async function mintUserJwt(\n\tapiKey: string,\n\thost: string,\n\tsignal?: AbortSignal,\n\tfetchImpl = fetch,\n): Promise<string> {\n\tconst response = await fetchImpl(`${host}/exa.auth_pb.AuthService/GetUserJwt`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"content-type\": \"application/proto\", \"connect-protocol-version\": \"1\" },\n\t\tbody: new Uint8Array(\n\t\t\tencodeMessage(\n\t\t\t\t1,\n\t\t\t\tbuildMetadata({\n\t\t\t\t\tapiKey,\n\t\t\t\t\tsessionId: crypto.randomUUID(),\n\t\t\t\t\trequestId: BigInt(Date.now()),\n\t\t\t\t\ttriggerId: crypto.randomUUID(),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tsignal,\n\t});\n\tif (!response.ok)\n\t\tthrow new Error(\n\t\t\t`Devin authentication failed (HTTP ${response.status}); run /login devin if the session was revoked`,\n\t\t);\n\tfor (const field of iterFields(Buffer.from(await response.arrayBuffer()))) {\n\t\tif (field.num === 1 && Buffer.isBuffer(field.value) && field.value.length) return field.value.toString(\"utf8\");\n\t}\n\tthrow new Error(\"Devin authentication returned no user JWT\");\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const DEVIN_BASE_URL = "https://server.codeium.com";
|
|
2
|
+
/** Protocol client identity required by Devin Local; no installed CLI dependency. */
|
|
3
|
+
export declare function buildMetadata(input: {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
userJwt?: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
requestId: bigint;
|
|
8
|
+
triggerId: string;
|
|
9
|
+
discovery?: boolean;
|
|
10
|
+
}): Buffer;
|
|
11
|
+
//# sourceMappingURL=devin-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-metadata.d.ts","sourceRoot":"","sources":["../../src/api/devin-metadata.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,+BAA+B,CAAC;AAE3D,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,MAAM,CAoBT","sourcesContent":["import { encodeMessage, encodeString, encodeTimestampBody, encodeVarintField } from \"./devin-wire.ts\";\n\nexport const DEVIN_BASE_URL = \"https://server.codeium.com\";\n\n/** Protocol client identity required by Devin Local; no installed CLI dependency. */\nexport function buildMetadata(input: {\n\tapiKey: string;\n\tuserJwt?: string;\n\tsessionId: string;\n\trequestId: bigint;\n\ttriggerId: string;\n\tdiscovery?: boolean;\n}): Buffer {\n\tconst name = input.discovery ? \"chisel\" : \"devin-cli\";\n\tconst version = input.discovery ? \"0.0.0-dev\" : \"3000.6.2\";\n\tconst token = input.apiKey.startsWith(\"devin-session-token$\") ? input.apiKey : `devin-session-token$${input.apiKey}`;\n\treturn Buffer.concat([\n\t\tencodeString(1, name),\n\t\tencodeString(2, version),\n\t\tencodeString(3, token),\n\t\tencodeString(4, \"en\"),\n\t\tencodeString(5, process.platform === \"win32\" ? \"windows\" : process.platform),\n\t\tencodeString(7, version),\n\t\tencodeVarintField(9, input.requestId),\n\t\tencodeString(10, input.sessionId),\n\t\tencodeString(12, \"chisel\"),\n\t\tencodeMessage(16, encodeTimestampBody()),\n\t\t...(input.userJwt ? [encodeString(21, input.userJwt)] : []),\n\t\tencodeString(25, input.triggerId),\n\t\tencodeString(28, \"chisel\"),\n\t\t...[3, 4, 6, 7, 8].map((value) => encodeVarintField(30, value)),\n\t]);\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { encodeMessage, encodeString, encodeTimestampBody, encodeVarintField } from "./devin-wire.js";
|
|
2
|
+
export const DEVIN_BASE_URL = "https://server.codeium.com";
|
|
3
|
+
/** Protocol client identity required by Devin Local; no installed CLI dependency. */
|
|
4
|
+
export function buildMetadata(input) {
|
|
5
|
+
const name = input.discovery ? "chisel" : "devin-cli";
|
|
6
|
+
const version = input.discovery ? "0.0.0-dev" : "3000.6.2";
|
|
7
|
+
const token = input.apiKey.startsWith("devin-session-token$") ? input.apiKey : `devin-session-token$${input.apiKey}`;
|
|
8
|
+
return Buffer.concat([
|
|
9
|
+
encodeString(1, name),
|
|
10
|
+
encodeString(2, version),
|
|
11
|
+
encodeString(3, token),
|
|
12
|
+
encodeString(4, "en"),
|
|
13
|
+
encodeString(5, process.platform === "win32" ? "windows" : process.platform),
|
|
14
|
+
encodeString(7, version),
|
|
15
|
+
encodeVarintField(9, input.requestId),
|
|
16
|
+
encodeString(10, input.sessionId),
|
|
17
|
+
encodeString(12, "chisel"),
|
|
18
|
+
encodeMessage(16, encodeTimestampBody()),
|
|
19
|
+
...(input.userJwt ? [encodeString(21, input.userJwt)] : []),
|
|
20
|
+
encodeString(25, input.triggerId),
|
|
21
|
+
encodeString(28, "chisel"),
|
|
22
|
+
...[3, 4, 6, 7, 8].map((value) => encodeVarintField(30, value)),
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=devin-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-metadata.js","sourceRoot":"","sources":["../../src/api/devin-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEtG,MAAM,CAAC,MAAM,cAAc,GAAG,4BAA4B,CAAC;AAE3D,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAAC,KAO7B,EAAU;IACV,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC;IACtD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;IAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB,KAAK,CAAC,MAAM,EAAE,CAAC;IACrH,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC;QACrB,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;QACxB,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC;QACtB,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC;QACrB,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC5E,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;QACxB,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;QACrC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC;QACjC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC;QAC1B,aAAa,CAAC,EAAE,EAAE,mBAAmB,EAAE,CAAC;QACxC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC;QACjC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC;QAC1B,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;KAC/D,CAAC,CAAC;AAAA,CACH","sourcesContent":["import { encodeMessage, encodeString, encodeTimestampBody, encodeVarintField } from \"./devin-wire.ts\";\n\nexport const DEVIN_BASE_URL = \"https://server.codeium.com\";\n\n/** Protocol client identity required by Devin Local; no installed CLI dependency. */\nexport function buildMetadata(input: {\n\tapiKey: string;\n\tuserJwt?: string;\n\tsessionId: string;\n\trequestId: bigint;\n\ttriggerId: string;\n\tdiscovery?: boolean;\n}): Buffer {\n\tconst name = input.discovery ? \"chisel\" : \"devin-cli\";\n\tconst version = input.discovery ? \"0.0.0-dev\" : \"3000.6.2\";\n\tconst token = input.apiKey.startsWith(\"devin-session-token$\") ? input.apiKey : `devin-session-token$${input.apiKey}`;\n\treturn Buffer.concat([\n\t\tencodeString(1, name),\n\t\tencodeString(2, version),\n\t\tencodeString(3, token),\n\t\tencodeString(4, \"en\"),\n\t\tencodeString(5, process.platform === \"win32\" ? \"windows\" : process.platform),\n\t\tencodeString(7, version),\n\t\tencodeVarintField(9, input.requestId),\n\t\tencodeString(10, input.sessionId),\n\t\tencodeString(12, \"chisel\"),\n\t\tencodeMessage(16, encodeTimestampBody()),\n\t\t...(input.userJwt ? [encodeString(21, input.userJwt)] : []),\n\t\tencodeString(25, input.triggerId),\n\t\tencodeString(28, \"chisel\"),\n\t\t...[3, 4, 6, 7, 8].map((value) => encodeVarintField(30, value)),\n\t]);\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Api, type AssistantMessageEventStream, type Context, type Model, type SimpleStreamOptions } from "../index.ts";
|
|
2
|
+
export type CloudChatEvent = {
|
|
3
|
+
kind: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "reasoning";
|
|
7
|
+
text: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "reasoning_signature";
|
|
10
|
+
signature: string;
|
|
11
|
+
signatureType?: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "reasoning_redacted";
|
|
14
|
+
} | {
|
|
15
|
+
kind: "tool_call_start";
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "tool_call_args";
|
|
20
|
+
argsDelta: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "finish";
|
|
24
|
+
reason: "stop" | "tool_calls" | "length" | "content_filter";
|
|
25
|
+
} | {
|
|
26
|
+
kind: "usage";
|
|
27
|
+
promptTokens?: number;
|
|
28
|
+
completionTokens?: number;
|
|
29
|
+
totalTokens?: number;
|
|
30
|
+
cachedInputTokens?: number;
|
|
31
|
+
cacheCreationInputTokens?: number;
|
|
32
|
+
};
|
|
33
|
+
export declare function streamDevin(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
|
|
34
|
+
//# sourceMappingURL=devin-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-stream.d.ts","sourceRoot":"","sources":["../../src/api/devin-stream.ts"],"names":[],"mappings":"AA0BA,OAAO,EACN,KAAK,GAAG,EAER,KAAK,2BAA2B,EAChC,KAAK,OAAO,EAEZ,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,MAAM,aAAa,CAAC;AAqBrB,MAAM,MAAM,cAAc,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB,CAAA;CAAE,GAC/E;IACA,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAyWL,wBAAgB,WAAW,CAC1B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,2BAA2B,CA4N7B","sourcesContent":["/*\nMIT License\n\nCopyright (c) 2026 Kashyab Ambarani\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\nimport { randomUUID } from \"node:crypto\";\nimport * as zlib from \"node:zlib\";\nimport {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype AssistantMessageEventStream,\n\ttype Context,\n\tcreateAssistantMessageEventStream,\n\ttype Model,\n\ttype SimpleStreamOptions,\n} from \"../index.ts\";\nimport { type ChatHistoryItem, type ContentPart, mapContextToChat, type ToolDef } from \"./devin-context-map.ts\";\nimport { mintUserJwt } from \"./devin-jwt.ts\";\nimport { buildMetadata } from \"./devin-metadata.ts\";\n\nimport { type ChatThinking, packThinkingSignature, unpackThinkingSignature } from \"./devin-thinking.ts\";\nimport {\n\tencodeFixed64Field,\n\tencodeMessage,\n\tencodeString,\n\tencodeVarintField,\n\tframeConnectStream,\n\titerFields,\n} from \"./devin-wire.ts\";\n\nconst SOURCE_BY_ROLE: Record<string, number> = {\n\tuser: 1,\n\tassistant: 2,\n\ttool: 4,\n};\n\nexport type CloudChatEvent =\n\t| { kind: \"text\"; text: string }\n\t| { kind: \"reasoning\"; text: string }\n\t| { kind: \"reasoning_signature\"; signature: string; signatureType?: string }\n\t| { kind: \"reasoning_redacted\" }\n\t| { kind: \"tool_call_start\"; id: string; name: string }\n\t| { kind: \"tool_call_args\"; argsDelta: string; id?: string }\n\t| { kind: \"finish\"; reason: \"stop\" | \"tool_calls\" | \"length\" | \"content_filter\" }\n\t| {\n\t\t\tkind: \"usage\";\n\t\t\tpromptTokens?: number;\n\t\t\tcompletionTokens?: number;\n\t\t\ttotalTokens?: number;\n\t\t\tcachedInputTokens?: number;\n\t\t\tcacheCreationInputTokens?: number;\n\t };\n\nfunction normalizeContent(content: string | ContentPart[]): ContentPart[] {\n\tif (typeof content === \"string\") return [{ type: \"text\", text: content }];\n\treturn content;\n}\n\nfunction encodeImageData(img: { mimeType?: string; base64Data?: string }): Buffer {\n\treturn Buffer.concat([encodeString(1, img.base64Data ?? \"\"), encodeString(2, img.mimeType ?? \"image/png\")]);\n}\n\nfunction encodeChatToolCall(tc: { id: string; name: string; arguments: string }): Buffer {\n\treturn Buffer.concat([encodeString(1, tc.id), encodeString(2, tc.name), encodeString(3, tc.arguments)]);\n}\n\nfunction encodeChatMessagePrompt(\n\tcontent: ContentPart[],\n\tsource: number,\n\topts?: {\n\t\ttoolCallId?: string;\n\t\ttoolCalls?: Array<{ id: string; name: string; arguments: string }>;\n\t\tthinking?: ChatThinking;\n\t},\n): Buffer {\n\tconst text = content\n\t\t.filter((part) => part.type === \"text\")\n\t\t.map((part) => part.text ?? \"\")\n\t\t.join(\"\\n\");\n\tconst parts: Buffer[] = [encodeVarintField(2, source), encodeString(3, text), encodeVarintField(5, 1)];\n\tif (opts?.toolCallId) parts.push(encodeString(7, opts.toolCallId));\n\tfor (const tc of opts?.toolCalls ?? []) parts.push(encodeMessage(6, encodeChatToolCall(tc)));\n\tfor (const img of content.filter((part) => part.type === \"image\")) {\n\t\tparts.push(encodeMessage(10, encodeImageData(img)));\n\t}\n\t// 11 thinking / 12 signature / 13 thinking_redacted / 18 signature_type — the\n\t// same quartet the Devin CLI replays so the model keeps its own reasoning.\n\tif (opts?.thinking) {\n\t\tparts.push(encodeString(11, opts.thinking.text));\n\t\tparts.push(encodeString(12, opts.thinking.signature));\n\t\tif (opts.thinking.redacted) parts.push(encodeVarintField(13, 1));\n\t\tif (opts.thinking.signatureType) parts.push(encodeString(18, opts.thinking.signatureType));\n\t}\n\treturn Buffer.concat(parts);\n}\n\n/** Mirrors the Devin CLI: num_completions / max_tokens / max_newlines plus\n * temperature / top_k / top_p, and nothing else. */\nfunction encodeCompletionConfiguration(maxOutputTokens?: number, temperature?: number): Buffer {\n\treturn Buffer.concat([\n\t\tencodeVarintField(1, 1),\n\t\tencodeVarintField(2, maxOutputTokens ?? 128_000),\n\t\tencodeVarintField(3, 400),\n\t\tencodeFixed64Field(5, temperature ?? 1.0),\n\t\tencodeVarintField(7, 40),\n\t\tencodeFixed64Field(8, 0.95),\n\t]);\n}\n\n/** CortexTrajectoryReference: cascade trajectory, user-input step. */\nfunction encodeTrajectoryReference(trajectoryId: string): Buffer {\n\treturn Buffer.concat([encodeString(1, trajectoryId), encodeVarintField(3, 4), encodeVarintField(4, 14)]);\n}\n\nfunction encodeToolDef(tool: ToolDef): Buffer {\n\tconst description = tool.description;\n\treturn Buffer.concat([\n\t\tencodeString(1, tool.name),\n\t\tencodeString(2, description),\n\t\tencodeString(3, JSON.stringify(tool.parameters ?? {})),\n\t]);\n}\n\nfunction buildGetChatMessageRequest(args: {\n\tapiKey: string;\n\tuserJwt: string;\n\tmodelUid: string;\n\tsystemPrompt?: string;\n\tmessages: ChatHistoryItem[];\n\ttools?: ToolDef[];\n\tcascadeId: string;\n\ttrajectoryId: string;\n\tsessionId: string;\n\trequestId: bigint;\n\ttriggerId: string;\n\tmaxOutputTokens?: number;\n\ttemperature?: number;\n}): Buffer {\n\tconst metadata = buildMetadata({\n\t\tapiKey: args.apiKey,\n\t\tuserJwt: args.userJwt,\n\t\tsessionId: args.sessionId,\n\t\trequestId: args.requestId,\n\t\ttriggerId: args.triggerId,\n\t});\n\tconst prompts = args.messages.map((message) =>\n\t\tencodeMessage(\n\t\t\t3,\n\t\t\tencodeChatMessagePrompt(normalizeContent(message.content), SOURCE_BY_ROLE[message.role], {\n\t\t\t\ttoolCallId: message.role === \"tool\" ? message.tool_call_id : undefined,\n\t\t\t\ttoolCalls: message.role === \"assistant\" ? message.tool_calls : undefined,\n\t\t\t\tthinking: message.role === \"assistant\" ? message.thinking : undefined,\n\t\t\t}),\n\t\t),\n\t);\n\treturn Buffer.concat([\n\t\tencodeMessage(1, metadata),\n\t\t// 2 prompt — the server's system slot, same place the Devin CLI puts its own\n\t\t// system prompt. Collapsing it into the first user turn is not equivalent.\n\t\t...(args.systemPrompt ? [encodeString(2, args.systemPrompt)] : []),\n\t\t...prompts,\n\t\tencodeVarintField(7, 5),\n\t\tencodeMessage(8, encodeCompletionConfiguration(args.maxOutputTokens, args.temperature)),\n\t\t...(args.tools ?? []).map((tool) => encodeMessage(10, encodeToolDef(tool))),\n\t\tencodeMessage(15, encodeTrajectoryReference(args.trajectoryId)),\n\t\tencodeString(16, args.cascadeId),\n\t\tencodeVarintField(20, 1),\n\t\tencodeString(21, args.modelUid),\n\t]);\n}\n\nfunction* decodeChatFrame(proto: Buffer): Generator<CloudChatEvent> {\n\tlet signature: string | undefined;\n\tlet signatureType: string | undefined;\n\tfor (const field of iterFields(proto)) {\n\t\tif (field.num === 3 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tconst text = field.value.toString(\"utf8\");\n\t\t\tif (text) yield { kind: \"text\", text };\n\t\t} else if (field.num === 9 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tconst text = field.value.toString(\"utf8\");\n\t\t\tif (text) yield { kind: \"reasoning\", text };\n\t\t} else if (field.num === 11 && field.wire === 0) {\n\t\t\t// GetChatMessageResponse.thinking_redacted\n\t\t\tif (Number(field.value) !== 0) yield { kind: \"reasoning_redacted\" };\n\t\t} else if (field.num === 10 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tconst text = field.value.toString(\"utf8\");\n\t\t\tif (text) signature = text;\n\t\t} else if (field.num === 21 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tconst text = field.value.toString(\"utf8\");\n\t\t\tif (text) signatureType = text;\n\t\t} else if (field.num === 6 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tlet id: string | undefined;\n\t\t\tlet name: string | undefined;\n\t\t\tlet argsDelta: string | undefined;\n\t\t\tfor (const inner of iterFields(field.value)) {\n\t\t\t\tif (inner.wire === 2 && Buffer.isBuffer(inner.value)) {\n\t\t\t\t\tconst text = inner.value.toString(\"utf8\");\n\t\t\t\t\tif (inner.num === 1) id = text;\n\t\t\t\t\telse if (inner.num === 2) name = text;\n\t\t\t\t\telse if (inner.num === 3) argsDelta = text;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (id !== undefined && name !== undefined) yield { kind: \"tool_call_start\", id, name };\n\t\t\tif (argsDelta !== undefined) yield { kind: \"tool_call_args\", argsDelta, ...(id ? { id } : {}) };\n\t\t} else if (field.num === 5 && field.wire === 0) {\n\t\t\tconst value = Number(field.value);\n\t\t\tlet reason: Extract<CloudChatEvent, { kind: \"finish\" }>[\"reason\"] = \"stop\";\n\t\t\tif (value === 10) reason = \"tool_calls\";\n\t\t\telse if (value === 11) reason = \"content_filter\";\n\t\t\telse if ([1, 3, 5, 9].includes(value)) reason = \"length\";\n\t\t\telse if (![0, 2, 4, 6, 8, 12].includes(value)) throw new Error(`Devin returned failure stop reason ${value}`);\n\t\t\tyield { kind: \"finish\", reason };\n\t\t} else if (field.num === 28 && field.wire === 2 && Buffer.isBuffer(field.value)) {\n\t\t\tconst usage = decodeUsage(field.value);\n\t\t\tif (usage) yield usage;\n\t\t}\n\t}\n\t// The signature trails the thinking text, so it is yielded once the frame is\n\t// fully read and gets attached to the block that is already closed.\n\tif (signature !== undefined || signatureType !== undefined)\n\t\tyield { kind: \"reasoning_signature\", signature: signature ?? \"\", signatureType };\n}\n\nfunction decodeUsage(buf: Buffer): CloudChatEvent | null {\n\tlet promptTokens: number | undefined;\n\tlet completionTokens: number | undefined;\n\tlet cachedInputTokens: number | undefined;\n\tlet cacheCreationInputTokens: number | undefined;\n\tfor (const field of iterFields(buf)) {\n\t\tif (field.num !== 2 || field.wire !== 2 || !Buffer.isBuffer(field.value)) continue;\n\t\tlet metric: string | undefined;\n\t\tlet value: number | undefined;\n\t\tfor (const inner of iterFields(field.value)) {\n\t\t\tif (inner.num === 5 && inner.wire === 2 && Buffer.isBuffer(inner.value)) {\n\t\t\t\tmetric = inner.value.toString(\"utf8\");\n\t\t\t} else if (inner.num === 4 && inner.wire === 2 && Buffer.isBuffer(inner.value)) {\n\t\t\t\tfor (const dim of iterFields(inner.value)) {\n\t\t\t\t\tif (dim.num === 2 && dim.wire === 5 && Buffer.isBuffer(dim.value)) {\n\t\t\t\t\t\tvalue = dim.value.readFloatLE(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!metric || value === undefined || !Number.isFinite(value)) continue;\n\t\tconst n = Math.round(value);\n\t\tif (metric === \"input_tokens\") promptTokens = n;\n\t\telse if (metric === \"output_tokens\") completionTokens = n;\n\t\telse if (metric === \"cache_read_input_tokens\") cachedInputTokens = n;\n\t\telse if (metric === \"cache_creation_input_tokens\") cacheCreationInputTokens = n;\n\t}\n\tif (promptTokens === undefined && completionTokens === undefined) return null;\n\treturn {\n\t\tkind: \"usage\",\n\t\tpromptTokens,\n\t\tcompletionTokens,\n\t\ttotalTokens: (promptTokens ?? 0) + (completionTokens ?? 0),\n\t\tcachedInputTokens,\n\t\tcacheCreationInputTokens,\n\t};\n}\n\nasync function* streamChatEvents(args: {\n\tapiKey: string;\n\thost: string;\n\tmodelUid: string;\n\tsystemPrompt?: string;\n\tmessages: ChatHistoryItem[];\n\ttools?: ToolDef[];\n\tmaxOutputTokens?: number;\n\tsignal?: AbortSignal;\n\tsessionId?: string;\n\tfetchImpl: typeof fetch;\n\ttemperature?: number;\n}): AsyncGenerator<CloudChatEvent> {\n\tconst host = args.host.replace(/\\/$/, \"\");\n\tconst userJwt = await mintUserJwt(args.apiKey, host, args.signal, args.fetchImpl);\n\tconst sessionId = args.sessionId ?? randomUUID();\n\tconst ids = { sessionId, cascadeId: sessionId, trajectoryId: sessionId };\n\tconst proto = buildGetChatMessageRequest({\n\t\tapiKey: args.apiKey,\n\t\tuserJwt,\n\t\tmodelUid: args.modelUid,\n\t\tsystemPrompt: args.systemPrompt,\n\t\tmessages: args.messages,\n\t\ttools: args.tools,\n\t\tcascadeId: ids.cascadeId,\n\t\ttrajectoryId: ids.trajectoryId,\n\t\tsessionId: ids.sessionId,\n\t\trequestId: BigInt(Date.now()),\n\t\ttriggerId: randomUUID(),\n\t\tmaxOutputTokens: args.maxOutputTokens,\n\t\ttemperature: args.temperature,\n\t});\n\n\tconst resp = await args.fetchImpl(`${host}/exa.api_server_pb.ApiServerService/GetChatMessage`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/connect+proto\",\n\t\t\t\"Connect-Protocol-Version\": \"1\",\n\t\t\t\"Connect-Content-Encoding\": \"gzip\",\n\t\t\t\"Connect-Accept-Encoding\": \"gzip\",\n\t\t},\n\t\tbody: new Uint8Array(frameConnectStream(proto, true)),\n\t\tsignal: args.signal,\n\t});\n\tif (!resp.ok) {\n\t\tthrow new Error(`Devin GetChatMessage failed (HTTP ${resp.status})`);\n\t}\n\tif (!resp.body) throw new Error(\"GetChatMessage returned an empty body\");\n\n\tconst reader = resp.body.getReader();\n\t// reader.closed rejects with the stream's storedError when the socket dies\n\t// mid-response; nothing awaits it -> unhandledRejection crashes the process.\n\tvoid reader.closed.catch(() => {});\n\tconst queue: Buffer[] = [];\n\tlet queued = 0;\n\tlet sawEos = false;\n\tlet trailerError: string | null = null;\n\n\tconst peek = (n: number): Buffer | null => {\n\t\tif (queued < n) return null;\n\t\tif (queue.length === 1 && queue[0].length >= n) return queue[0].subarray(0, n);\n\t\tconst parts: Buffer[] = [];\n\t\tlet remaining = n;\n\t\tfor (const chunk of queue) {\n\t\t\tif (remaining <= 0) break;\n\t\t\tif (chunk.length <= remaining) {\n\t\t\t\tparts.push(chunk);\n\t\t\t\tremaining -= chunk.length;\n\t\t\t} else {\n\t\t\t\tparts.push(chunk.subarray(0, remaining));\n\t\t\t\tremaining = 0;\n\t\t\t}\n\t\t}\n\t\treturn Buffer.concat(parts, n);\n\t};\n\n\tconst drop = (n: number): void => {\n\t\tqueued -= n;\n\t\tlet remaining = n;\n\t\twhile (remaining > 0 && queue.length > 0) {\n\t\t\tconst head = queue[0];\n\t\t\tif (head.length <= remaining) {\n\t\t\t\tqueue.shift();\n\t\t\t\tremaining -= head.length;\n\t\t\t} else {\n\t\t\t\tqueue[0] = head.subarray(remaining);\n\t\t\t\tremaining = 0;\n\t\t\t}\n\t\t}\n\t};\n\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { value, done } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tif (value) {\n\t\t\t\tqueue.push(Buffer.from(value));\n\t\t\t\tqueued += value.length;\n\t\t\t}\n\t\t\twhile (queued >= 5) {\n\t\t\t\tconst header = peek(5);\n\t\t\t\tif (!header) break;\n\t\t\t\tconst flags = header[0];\n\t\t\t\tconst len = header.readUInt32BE(1);\n\t\t\t\tif (flags & ~3) throw new Error(\"Invalid Devin Connect frame flags\");\n\t\t\t\tif (len > 16 * 1024 * 1024) throw new Error(\"Devin Connect frame exceeds 16 MiB\");\n\t\t\t\tif (queued < 5 + len) break;\n\t\t\t\tdrop(5);\n\t\t\t\tconst raw = peek(len) ?? Buffer.alloc(0);\n\t\t\t\tdrop(len);\n\t\t\t\tlet payload = raw;\n\t\t\t\tif (flags & 0x01) payload = zlib.gunzipSync(raw, { maxOutputLength: 16 * 1024 * 1024 });\n\t\t\t\tif (sawEos) throw new Error(\"Devin returned data after its EOS trailer\");\n\t\t\t\tif (flags & 0x02) {\n\t\t\t\t\tsawEos = true;\n\t\t\t\t\tconst parsed = JSON.parse(payload.toString(\"utf8\")) as { error?: { code?: string; message?: string } };\n\t\t\t\t\tif (parsed.error) {\n\t\t\t\t\t\tconst detail =\n\t\t\t\t\t\t\ttypeof parsed.error.message === \"string\"\n\t\t\t\t\t\t\t\t? parsed.error.message\n\t\t\t\t\t\t\t\t\t\t.replaceAll(args.apiKey, \"[redacted]\")\n\t\t\t\t\t\t\t\t\t\t.replaceAll(userJwt, \"[redacted]\")\n\t\t\t\t\t\t\t\t\t\t.slice(0, 500)\n\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\ttrailerError = `Devin stream failed (${parsed.error.code ?? \"unknown\"})${detail ? `: ${detail}` : \"\"}`;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tyield* decodeChatFrame(payload);\n\t\t\t}\n\t\t}\n\t} finally {\n\t\ttry {\n\t\t\treader.releaseLock();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t\ttry {\n\t\t\t// await: on an errored stream cancel() returns a rejected promise;\n\t\t\t// `void`-ing it escapes the try/catch as an unhandled rejection.\n\t\t\tawait resp.body?.cancel();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tif (trailerError) throw new Error(trailerError);\n\tif (queued) throw new Error(\"Truncated Devin Connect frame\");\n\tif (!sawEos) throw new Error(\"Devin stream ended without an EOS trailer\");\n}\n\nexport function streamDevin(\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst stream = createAssistantMessageEventStream();\n\n\tvoid (async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet textOpen = false;\n\t\tlet thinkingOpen = false;\n\t\tlet thinkingIndex = -1;\n\t\tlet toolId = \"\";\n\t\tconst toolStates = new Map<string, { index: number; json: string }>();\n\n\t\tconst closeText = () => {\n\t\t\tif (!textOpen) return;\n\t\t\tconst idx = output.content.length - 1;\n\t\t\tconst block = output.content[idx];\n\t\t\tif (block.type === \"text\") {\n\t\t\t\tstream.push({ type: \"text_end\", contentIndex: idx, content: block.text, partial: output });\n\t\t\t}\n\t\t\ttextOpen = false;\n\t\t};\n\t\tconst closeThinking = () => {\n\t\t\tif (!thinkingOpen) return;\n\t\t\tconst idx = output.content.length - 1;\n\t\t\tconst block = output.content[idx];\n\t\t\tif (block.type === \"thinking\") {\n\t\t\t\tstream.push({ type: \"thinking_end\", contentIndex: idx, content: block.thinking, partial: output });\n\t\t\t}\n\t\t\tthinkingOpen = false;\n\t\t};\n\t\tconst closeTool = () => {\n\t\t\tfor (const state of toolStates.values()) {\n\t\t\t\tconst block = output.content[state.index];\n\t\t\t\tif (block.type === \"toolCall\") {\n\t\t\t\t\tconst parsed: unknown = JSON.parse(state.json);\n\t\t\t\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\t\t\t\t\tthrow new Error(\"Invalid Devin tool arguments\");\n\t\t\t\t\tblock.arguments = parsed as Record<string, unknown>;\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\tcontentIndex: state.index,\n\t\t\t\t\t\ttoolCall: block,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\ttoolStates.clear();\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (process.env.PI_SERVER_MODE === \"true\")\n\t\t\t\tthrow new Error(\"Devin inference must use pi-server in PI_SERVER_MODE\");\n\t\t\tif (!apiKey) throw new Error(\"No Devin credentials. Run /login devin.\");\n\t\t\tconst host = model.baseUrl.replace(/\\/$/, \"\");\n\t\t\tif (options?.reasoning && model.thinkingLevelMap && model.thinkingLevelMap[options.reasoning] !== model.id)\n\t\t\t\tthrow new Error(\"Select the Devin model variant for the requested reasoning effort\");\n\t\t\tif (options?.onPayload) throw new Error(\"Devin does not support onPayload overrides\");\n\t\t\tconst modelUid = model.id;\n\t\t\tconst mapped = mapContextToChat(context, model.id);\n\t\t\tstream.push({ type: \"start\", partial: output });\n\n\t\t\tfor await (const event of streamChatEvents({\n\t\t\t\tapiKey,\n\t\t\t\thost,\n\t\t\t\tmodelUid,\n\t\t\t\tsystemPrompt: mapped.systemPrompt,\n\t\t\t\tmessages: mapped.messages,\n\t\t\t\ttools: mapped.tools.length > 0 ? mapped.tools : undefined,\n\t\t\t\tmaxOutputTokens: options?.maxTokens ?? model.maxTokens,\n\t\t\t\tsignal: options?.signal\n\t\t\t\t\t? AbortSignal.any([options.signal, AbortSignal.timeout(options.timeoutMs ?? 600_000)])\n\t\t\t\t\t: AbortSignal.timeout(options?.timeoutMs ?? 600_000),\n\t\t\t\tsessionId: options?.sessionId,\n\t\t\t\tfetchImpl: options?.fetch ?? fetch,\n\t\t\t\ttemperature: options?.temperature,\n\t\t\t})) {\n\t\t\t\tif (event.kind === \"text\") {\n\t\t\t\t\tcloseThinking();\n\t\t\t\t\tif (!textOpen) {\n\t\t\t\t\t\toutput.content.push({ type: \"text\", text: \"\" });\n\t\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t\ttextOpen = true;\n\t\t\t\t\t}\n\t\t\t\t\tconst idx = output.content.length - 1;\n\t\t\t\t\tconst block = output.content[idx];\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\tblock.text += event.text;\n\t\t\t\t\t\tstream.push({ type: \"text_delta\", contentIndex: idx, delta: event.text, partial: output });\n\t\t\t\t\t}\n\t\t\t\t} else if (event.kind === \"reasoning\") {\n\t\t\t\t\tcloseText();\n\t\t\t\t\tif (!thinkingOpen) {\n\t\t\t\t\t\toutput.content.push({ type: \"thinking\", thinking: \"\" });\n\t\t\t\t\t\tthinkingIndex = output.content.length - 1;\n\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: output.content.length - 1, partial: output });\n\t\t\t\t\t\tthinkingOpen = true;\n\t\t\t\t\t}\n\t\t\t\t\tconst idx = output.content.length - 1;\n\t\t\t\t\tconst block = output.content[idx];\n\t\t\t\t\tif (block.type === \"thinking\") {\n\t\t\t\t\t\tblock.thinking += event.text;\n\t\t\t\t\t\tstream.push({ type: \"thinking_delta\", contentIndex: idx, delta: event.text, partial: output });\n\t\t\t\t\t}\n\t\t\t\t} else if (event.kind === \"reasoning_signature\") {\n\t\t\t\t\tif (thinkingIndex < 0) {\n\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\toutput.content.push({ type: \"thinking\", thinking: \"\" });\n\t\t\t\t\t\tthinkingIndex = output.content.length - 1;\n\t\t\t\t\t\tthinkingOpen = true;\n\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: thinkingIndex, partial: output });\n\t\t\t\t\t}\n\t\t\t\t\tconst block = output.content[thinkingIndex];\n\t\t\t\t\tif (block?.type === \"thinking\") {\n\t\t\t\t\t\tconst previous = unpackThinkingSignature(block.thinkingSignature);\n\t\t\t\t\t\tblock.thinkingSignature = packThinkingSignature(\n\t\t\t\t\t\t\t(previous.signature ?? \"\") + event.signature,\n\t\t\t\t\t\t\tevent.signatureType ?? previous.signatureType,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else if (event.kind === \"reasoning_redacted\") {\n\t\t\t\t\tif (thinkingIndex < 0) {\n\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\toutput.content.push({ type: \"thinking\", thinking: \"\" });\n\t\t\t\t\t\tthinkingIndex = output.content.length - 1;\n\t\t\t\t\t\tthinkingOpen = true;\n\t\t\t\t\t\tstream.push({ type: \"thinking_start\", contentIndex: thinkingIndex, partial: output });\n\t\t\t\t\t}\n\t\t\t\t\tconst block = thinkingIndex >= 0 ? output.content[thinkingIndex] : undefined;\n\t\t\t\t\tif (block?.type === \"thinking\") block.redacted = true;\n\t\t\t\t} else if (event.kind === \"tool_call_start\") {\n\t\t\t\t\tcloseText();\n\t\t\t\t\tcloseThinking();\n\t\t\t\t\ttoolId = event.id;\n\t\t\t\t\tconst existing = toolStates.get(toolId);\n\t\t\t\t\tif (existing) {\n\t\t\t\t\t\tconst block = output.content[existing.index];\n\t\t\t\t\t\tif (block.type !== \"toolCall\" || block.name !== event.name)\n\t\t\t\t\t\t\tthrow new Error(\"Devin changed a tool call's name\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!toolId || !event.name) throw new Error(\"Devin returned an empty tool identity\");\n\t\t\t\t\toutput.content.push({ type: \"toolCall\", id: event.id, name: event.name, arguments: {} });\n\t\t\t\t\tconst toolIndex = output.content.length - 1;\n\t\t\t\t\ttoolStates.set(toolId, { index: toolIndex, json: \"\" });\n\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: toolIndex, partial: output });\n\t\t\t\t} else if (event.kind === \"tool_call_args\") {\n\t\t\t\t\tconst state = toolStates.get(event.id ?? toolId);\n\t\t\t\t\tif (!state) throw new Error(\"Devin tool argument delta has no matching call\");\n\t\t\t\t\tstate.json += event.argsDelta;\n\t\t\t\t\tconst block = output.content[state.index];\n\t\t\t\t\tif (block.type === \"toolCall\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tblock.arguments = JSON.parse(state.json);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// incomplete json\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\tcontentIndex: state.index,\n\t\t\t\t\t\tdelta: event.argsDelta,\n\t\t\t\t\t\tpartial: output,\n\t\t\t\t\t});\n\t\t\t\t} else if (event.kind === \"finish\") {\n\t\t\t\t\tif (event.reason === \"content_filter\")\n\t\t\t\t\t\tthrow new Error(\"Devin response was blocked by content filtering\");\n\t\t\t\t\tcloseText();\n\t\t\t\t\tcloseThinking();\n\t\t\t\t\toutput.stopReason =\n\t\t\t\t\t\tevent.reason === \"tool_calls\" ? \"toolUse\" : event.reason === \"length\" ? \"length\" : \"stop\";\n\t\t\t\t} else if (event.kind === \"usage\") {\n\t\t\t\t\toutput.usage.input = event.promptTokens ?? 0;\n\t\t\t\t\toutput.usage.output = event.completionTokens ?? 0;\n\t\t\t\t\toutput.usage.cacheRead = event.cachedInputTokens ?? 0;\n\t\t\t\t\toutput.usage.cacheWrite = event.cacheCreationInputTokens ?? 0;\n\t\t\t\t\toutput.usage.totalTokens = event.totalTokens ?? output.usage.input + output.usage.output;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcloseText();\n\t\t\tcloseThinking();\n\t\t\tcloseTool();\n\t\t\toptions?.signal?.throwIfAborted();\n\t\t\tif (!output.content.length) throw new Error(\"Devin returned an empty completion\");\n\t\t\tfor (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\"] as const) {\n\t\t\t\toutput.usage.cost[key] = (output.usage[key] * model.cost[key]) / 1_000_000;\n\t\t\t}\n\t\t\toutput.usage.cost.total =\n\t\t\t\toutput.usage.cost.input +\n\t\t\t\toutput.usage.cost.output +\n\t\t\t\toutput.usage.cost.cacheRead +\n\t\t\t\toutput.usage.cost.cacheWrite;\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason as \"stop\" | \"length\" | \"toolUse\", message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason as \"aborted\" | \"error\", error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n"]}
|