@obversa/engine-devin-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonny Neill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @obversa/engine-devin-cli
2
+
3
+ `@obversa/engine-devin-cli` runs Obversa engine requests through a fresh Devin
4
+ CLI process.
5
+
6
+ ## Requirements
7
+
8
+ - Node.js 22.12 or later
9
+ - The Devin CLI, installed and signed in with your own account
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @obversa/engine-devin-cli
15
+ ```
16
+
17
+ ## Use
18
+
19
+ ```ts
20
+ import { devin } from '@obversa/engine-devin-cli';
21
+
22
+ const seat = devin('swe-2-max');
23
+ ```
24
+
25
+ Without a model, `devin()` runs the default model from your Devin settings.
26
+
27
+ Devin runs with your own environment, Devin login and Devin settings. A
28
+ read-only step runs with `--permission-mode auto`, and a step that may write
29
+ runs with `--permission-mode accept-edits`. Devin has no mode without a
30
+ folder and no flag for a list of named tools. The plugin passes
31
+ `--respect-workspace-trust false`, because print mode cannot show Devin's
32
+ folder trust prompt.
33
+
34
+ See [Devin CLI Engine](https://docs.obversa.ai/packages/engine-devin-cli).
package/dist/atif.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Read the conversation file `devin --export` writes. Devin 3000.11 writes it
3
+ * in the ATIF v1 shape: ordered steps from the system, the user and the agent,
4
+ * with each agent step carrying its text, its tool calls, the results of those
5
+ * calls and the model that produced it.
6
+ */
7
+ import type { EngineStreamEvent, UsageReceipt } from '@obversa/api';
8
+ export interface DevinConversation {
9
+ /** Every non-empty agent message, in order. */
10
+ readonly messages: readonly string[];
11
+ /** True when the conversation ends on an agent message with no tool calls. */
12
+ readonly answered: boolean;
13
+ /** The model named on the last agent step, when Devin names one. */
14
+ readonly model: string | undefined;
15
+ readonly toolEvents: readonly Extract<EngineStreamEvent, {
16
+ type: 'tool';
17
+ }>[];
18
+ readonly usage: UsageReceipt;
19
+ }
20
+ export declare function readDevinExport(value: unknown): DevinConversation;
@@ -0,0 +1,42 @@
1
+ import { type AgentRequest, type AgentResult, type Engine, type EngineEventSink, type EngineSelectionRecord } from '@obversa/api';
2
+ export interface DevinCliEngineOptions {
3
+ /** Model for requests that name none. Without one, Devin uses the person's default. */
4
+ readonly defaultModel?: string;
5
+ /** Absolute path or bare command name; `devin` by default. */
6
+ readonly cliBinary?: string;
7
+ }
8
+ export interface DevinSeat {
9
+ readonly engine: DevinCliEngine;
10
+ readonly identity: {
11
+ readonly adapter: 'devin-cli';
12
+ readonly provider: 'cognition';
13
+ readonly modelFamily: string;
14
+ readonly model: string;
15
+ readonly tools: readonly string[];
16
+ };
17
+ }
18
+ /**
19
+ * Create the Devin seat used by declarative team workflows. Without a model,
20
+ * Devin runs the default model from the person's own Devin settings and the
21
+ * seat records the model as `default`.
22
+ */
23
+ export declare function devin(model?: string): DevinSeat;
24
+ /**
25
+ * Build the `devin` argv for one attempt. Exported so the flag wiring is
26
+ * testable without spawning a process.
27
+ */
28
+ export declare function buildDevinArgs(req: AgentRequest, opts: DevinCliEngineOptions, files: {
29
+ readonly promptFile: string;
30
+ readonly exportFile: string;
31
+ }): string[];
32
+ export declare class DevinCliEngine implements Engine {
33
+ private readonly opts;
34
+ readonly name = "devin-cli";
35
+ private executable;
36
+ private version;
37
+ constructor(opts?: DevinCliEngineOptions);
38
+ private commandExecutable;
39
+ private observeVersion;
40
+ admit(request: Omit<AgentRequest, 'prompt'>, signal: AbortSignal, expectedSelection?: EngineSelectionRecord): Promise<EngineSelectionRecord>;
41
+ run(req: AgentRequest, onEvent: EngineEventSink, signal: AbortSignal): Promise<AgentResult>;
42
+ }
@@ -0,0 +1 @@
1
+ export { DevinCliEngine, devin, buildDevinArgs, type DevinSeat, type DevinCliEngineOptions, } from './devin-cli.js';
package/dist/index.js ADDED
@@ -0,0 +1,413 @@
1
+ import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'fs';
2
+ import { tmpdir } from 'os';
3
+ import { isAbsolute, basename, join } from 'path';
4
+ import { isDeepStrictEqual } from 'util';
5
+ import { modelIdentity, assertReadAccess, EngineError, engineSelection, classifyEngineFailure, EngineIncompleteResultError, validateAgentResult } from '@obversa/api';
6
+ import { resolveCommandExecutable, runOwnedCommand, DEFAULT_OWNED_COMMAND_LIMITS, ownedCommandIdentity, attemptEnvironment, scrubCapture, OwnedCommandError } from '@obversa/core/command';
7
+
8
+ // src/devin-cli.ts
9
+
10
+ // src/atif.ts
11
+ function record(value, what) {
12
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
13
+ throw new Error(`Devin export ${what} is not an object`);
14
+ }
15
+ return value;
16
+ }
17
+ function messageText(value) {
18
+ if (typeof value === "string") return value;
19
+ if (!Array.isArray(value)) return "";
20
+ return value.map((part) => part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" ? part.text : "").join("");
21
+ }
22
+ function tokenCount(value) {
23
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
24
+ }
25
+ function usage(value) {
26
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
27
+ return { kind: "unknown" };
28
+ }
29
+ const totals = value;
30
+ const input = totals.total_prompt_tokens;
31
+ const output = totals.total_completion_tokens;
32
+ const cached = totals.total_cached_tokens;
33
+ if (!tokenCount(input) || !tokenCount(output) || cached != null && (!tokenCount(cached) || cached > input)) {
34
+ return { kind: "unknown" };
35
+ }
36
+ return {
37
+ kind: "reported",
38
+ inputTokens: input,
39
+ outputTokens: output,
40
+ ...cached == null ? {} : { cacheReadInputTokens: cached }
41
+ };
42
+ }
43
+ function readDevinExport(value) {
44
+ const root = record(value, "root");
45
+ if (typeof root.schema_version !== "string" || !root.schema_version.startsWith("ATIF-v1.")) {
46
+ throw new Error("Devin export is not in the ATIF v1 shape");
47
+ }
48
+ if (!Array.isArray(root.steps)) throw new Error("Devin export has no steps");
49
+ const messages = [];
50
+ const toolEvents = [];
51
+ let answered = false;
52
+ let model;
53
+ for (const value2 of root.steps) {
54
+ const step = record(value2, "step");
55
+ if (step.source !== "agent") {
56
+ answered = false;
57
+ continue;
58
+ }
59
+ if (typeof step.model_name === "string" && step.model_name.length > 0) {
60
+ model = step.model_name;
61
+ }
62
+ const calls = /* @__PURE__ */ new Map();
63
+ if (Array.isArray(step.tool_calls)) {
64
+ for (const value3 of step.tool_calls) {
65
+ const call = record(value3, "tool call");
66
+ if (typeof call.tool_call_id !== "string" || typeof call.function_name !== "string") {
67
+ throw new Error("Devin export has a tool call without an id or a name");
68
+ }
69
+ calls.set(call.tool_call_id, call.function_name);
70
+ toolEvents.push({ type: "tool", name: call.function_name, phase: "use" });
71
+ }
72
+ }
73
+ const results = step.observation === void 0 || step.observation === null ? [] : record(step.observation, "observation").results;
74
+ if (Array.isArray(results)) {
75
+ for (const value3 of results) {
76
+ const id = record(value3, "tool result").source_call_id;
77
+ const name = typeof id === "string" ? calls.get(id) : void 0;
78
+ if (name !== void 0) toolEvents.push({ type: "tool", name, phase: "result" });
79
+ }
80
+ }
81
+ const text = messageText(step.message);
82
+ if (text.length > 0) messages.push(text);
83
+ answered = text.length > 0 && calls.size === 0;
84
+ }
85
+ return { messages, answered, model, toolEvents, usage: usage(root.final_metrics) };
86
+ }
87
+
88
+ // src/devin-cli.ts
89
+ var ADAPTER = "devin-cli";
90
+ var PROVIDER = "cognition";
91
+ var DEFAULT_MODEL = "default";
92
+ var DIAGNOSTIC_MAX = 700;
93
+ function devin(model) {
94
+ if (model !== void 0 && !model.trim()) throw new TypeError("devin model must not be empty");
95
+ return {
96
+ engine: new DevinCliEngine(model === void 0 ? {} : { defaultModel: model }),
97
+ identity: {
98
+ adapter: ADAPTER,
99
+ provider: PROVIDER,
100
+ modelFamily: modelIdentity(model ?? DEFAULT_MODEL).modelFamily,
101
+ model: model ?? DEFAULT_MODEL,
102
+ tools: ["read", "edit", "exec"]
103
+ }
104
+ };
105
+ }
106
+ function modelFor(req, opts) {
107
+ const model = req.model ?? opts.defaultModel;
108
+ return model === void 0 || model === DEFAULT_MODEL ? void 0 : model;
109
+ }
110
+ function permissionMode(req) {
111
+ return req.workspaceMode === "write" ? "accept-edits" : "auto";
112
+ }
113
+ function buildDevinArgs(req, opts, files) {
114
+ try {
115
+ assertReadAccess(req);
116
+ if (req.tools?.length === 0) {
117
+ throw new TypeError("devin cannot turn its tools off; choose an engine that supports tools: []");
118
+ }
119
+ if (req.workspaceMode === "none") {
120
+ throw new TypeError("devin has no mode without workspace access; choose read or write");
121
+ }
122
+ } catch (cause) {
123
+ throw new EngineError({
124
+ kind: "invalid-config",
125
+ message: cause instanceof Error ? cause.message : "invalid Devin workspace configuration",
126
+ cause
127
+ });
128
+ }
129
+ const model = modelFor(req, opts);
130
+ return [
131
+ "-p",
132
+ "--prompt-file",
133
+ files.promptFile,
134
+ "--export",
135
+ files.exportFile,
136
+ "--permission-mode",
137
+ permissionMode(req),
138
+ // Print mode cannot show Devin's folder trust prompt; without this flag
139
+ // every folder the person has not opened in Devin before refuses to run.
140
+ "--respect-workspace-trust",
141
+ "false",
142
+ ...model === void 0 ? [] : ["--model", model]
143
+ ];
144
+ }
145
+ function requestedSelection(req, opts) {
146
+ const model = modelFor(req, opts);
147
+ return engineSelection({
148
+ adapter: ADAPTER,
149
+ provider: PROVIDER,
150
+ modelFamily: model === void 0 ? null : modelIdentity(model).modelFamily,
151
+ model: model ?? null,
152
+ capabilities: req.tools ?? []
153
+ });
154
+ }
155
+ function assertDevinConfiguration(req, opts) {
156
+ buildDevinArgs(req, opts, { promptFile: "prompt", exportFile: "export" });
157
+ try {
158
+ const env = attemptEnvironment(req) ?? {};
159
+ const limits = {
160
+ timeoutMs: req.timeoutMs ?? DEFAULT_OWNED_COMMAND_LIMITS.timeoutMs,
161
+ teardownGraceMs: req.timeoutGraceMs ?? DEFAULT_OWNED_COMMAND_LIMITS.teardownGraceMs,
162
+ maxOutputBytes: req.maxOutputBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxOutputBytes,
163
+ maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes
164
+ };
165
+ if (!isAbsolute(req.cwd ?? process.cwd()) || Object.values(env).some((value) => typeof value !== "string") || !Number.isSafeInteger(limits.timeoutMs) || limits.timeoutMs < 1 || !Number.isSafeInteger(limits.teardownGraceMs) || limits.teardownGraceMs < 0 || limits.timeoutMs + limits.teardownGraceMs > 2147483647 || !Number.isSafeInteger(limits.maxOutputBytes) || limits.maxOutputBytes < 0 || !Number.isSafeInteger(limits.maxMemoryBytes) || limits.maxMemoryBytes < 1) {
166
+ throw new Error("invalid Devin command request");
167
+ }
168
+ ownedCommandIdentity({
169
+ adapter: ADAPTER,
170
+ runId: req.attempt?.runId,
171
+ leafId: req.attempt?.leafId,
172
+ attemptId: req.attempt?.attemptId
173
+ });
174
+ } catch {
175
+ throw new EngineError({ kind: "invalid-config", message: "invalid Devin command configuration" });
176
+ }
177
+ }
178
+ function devinCommandError(error, executable) {
179
+ if (!(error instanceof OwnedCommandError)) return error;
180
+ if (error.code === "INVALID_EXECUTABLE") {
181
+ return new EngineError({ kind: "missing-cli", message: "Devin executable is not runnable" });
182
+ }
183
+ if (error.code === "INVALID_COMMAND") {
184
+ return new EngineError({ kind: "invalid-config", message: "invalid Devin command request" });
185
+ }
186
+ if (error.code === "SPAWN_FAILED") {
187
+ if (executable !== void 0) {
188
+ try {
189
+ resolveCommandExecutable(executable);
190
+ } catch {
191
+ return new EngineError({ kind: "missing-cli", message: "Devin executable is not runnable" });
192
+ }
193
+ }
194
+ return new EngineError({ kind: "unknown", message: "Devin command could not start" });
195
+ }
196
+ return error;
197
+ }
198
+ function readExport(path) {
199
+ let text;
200
+ try {
201
+ text = readFileSync(path, "utf8");
202
+ } catch {
203
+ return void 0;
204
+ }
205
+ try {
206
+ return readDevinExport(JSON.parse(text));
207
+ } catch {
208
+ return void 0;
209
+ }
210
+ }
211
+ var DevinCliEngine = class {
212
+ constructor(opts = {}) {
213
+ this.opts = opts;
214
+ }
215
+ opts;
216
+ name = ADAPTER;
217
+ executable;
218
+ version;
219
+ commandExecutable(expected) {
220
+ const configured = this.opts.cliBinary ?? "devin";
221
+ if (configured.length === 0 || !isAbsolute(configured) && basename(configured) !== configured) {
222
+ throw new EngineError({ kind: "invalid-config", message: "Devin command must be absolute or a bare name" });
223
+ }
224
+ if (expected && (expected.executable === null || isAbsolute(configured) && configured !== expected.executable || this.executable !== void 0 && this.executable !== expected.executable)) {
225
+ throw new EngineError({ kind: "invalid-config", message: "Devin executable selection changed" });
226
+ }
227
+ try {
228
+ this.executable ??= resolveCommandExecutable(expected?.executable ?? configured);
229
+ return resolveCommandExecutable(this.executable);
230
+ } catch (error) {
231
+ throw devinCommandError(error, this.executable);
232
+ }
233
+ }
234
+ async observeVersion(req, executable, signal) {
235
+ try {
236
+ const result = await runOwnedCommand({
237
+ executable,
238
+ args: ["--version"],
239
+ stdin: "",
240
+ cwd: req.cwd ?? process.cwd(),
241
+ env: attemptEnvironment(req) ?? {},
242
+ ...ownedCommandIdentity({
243
+ adapter: ADAPTER,
244
+ runId: req.attempt?.runId,
245
+ leafId: req.attempt?.leafId,
246
+ attemptId: req.attempt?.attemptId
247
+ }),
248
+ ...DEFAULT_OWNED_COMMAND_LIMITS,
249
+ timeoutMs: Math.min(req.timeoutMs ?? 5e3, 5e3),
250
+ teardownGraceMs: Math.min(req.timeoutGraceMs ?? 1e3, 1e3),
251
+ maxOutputBytes: Math.min(req.maxOutputBytes ?? 4096, 4096),
252
+ maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes
253
+ }, signal);
254
+ if (result.aborted || signal.aborted) {
255
+ throw new EngineError({ kind: "aborted", message: "Devin version observation aborted" });
256
+ }
257
+ if (result.timedOut) {
258
+ throw new EngineError({ kind: "timeout", message: "Devin version observation timed out" });
259
+ }
260
+ if (result.exitCode !== 0) {
261
+ throw new EngineError({ kind: "unknown", message: "Devin version command failed" });
262
+ }
263
+ const matched = /^devin (\d+\.\d+\.\d+) \([0-9a-f]+\)$/.exec(new TextDecoder().decode(result.stdout).trim());
264
+ if (!matched) {
265
+ throw new EngineError({ kind: "invalid-config", message: "Devin version output is not recognized" });
266
+ }
267
+ return matched[1];
268
+ } catch (error) {
269
+ const mapped = devinCommandError(error, executable);
270
+ if (mapped instanceof EngineError) throw mapped;
271
+ throw new EngineError({ kind: "unknown", message: "Devin version observation failed" });
272
+ }
273
+ }
274
+ async admit(request, signal, expectedSelection) {
275
+ if (signal.aborted) throw new EngineError({ kind: "aborted", message: "Devin admission aborted" });
276
+ const req = { ...request, prompt: "" };
277
+ assertDevinConfiguration(req, this.opts);
278
+ let expected;
279
+ let proposed;
280
+ try {
281
+ expected = expectedSelection === void 0 ? void 0 : engineSelection(expectedSelection);
282
+ proposed = requestedSelection(req, this.opts);
283
+ } catch {
284
+ throw new EngineError({ kind: "invalid-config", message: "invalid Devin selection" });
285
+ }
286
+ const executable = this.commandExecutable(expected);
287
+ let observation = this.version;
288
+ if (observation === void 0) {
289
+ observation = this.observeVersion(req, executable, signal);
290
+ this.version = observation;
291
+ }
292
+ let adapterVersion;
293
+ try {
294
+ adapterVersion = await observation;
295
+ } catch (error) {
296
+ if (this.version === observation) this.version = void 0;
297
+ throw error;
298
+ }
299
+ if (signal.aborted) throw new EngineError({ kind: "aborted", message: "Devin admission aborted" });
300
+ const selected = engineSelection({ ...proposed, executable, adapterVersion });
301
+ if (expected && !isDeepStrictEqual(selected, expected)) {
302
+ throw new EngineError({ kind: "invalid-config", message: "Devin selection changed" });
303
+ }
304
+ return selected;
305
+ }
306
+ async run(req, onEvent, signal) {
307
+ if (signal.aborted) throw new EngineError({ kind: "aborted", message: "devin run aborted" });
308
+ const { prompt: _prompt, ...admissionRequest } = req;
309
+ const requested = await this.admit(admissionRequest, signal);
310
+ const executable = requested.executable;
311
+ const dir = mkdtempSync(join(tmpdir(), "obversa-devin-"));
312
+ const promptFile = join(dir, "prompt.md");
313
+ const exportFile = join(dir, "conversation.json");
314
+ const env = attemptEnvironment(req);
315
+ try {
316
+ writeFileSync(
317
+ promptFile,
318
+ req.system ? `${req.system}
319
+
320
+ ---
321
+
322
+ ${req.prompt}` : req.prompt,
323
+ { encoding: "utf8", mode: 384 }
324
+ );
325
+ const sub = await runOwnedCommand({
326
+ executable,
327
+ args: buildDevinArgs(req, this.opts, { promptFile, exportFile }),
328
+ cwd: req.cwd ?? process.cwd(),
329
+ env: env ?? {},
330
+ stdin: "",
331
+ ...ownedCommandIdentity({
332
+ adapter: ADAPTER,
333
+ runId: req.attempt?.runId,
334
+ leafId: req.attempt?.leafId,
335
+ attemptId: req.attempt?.attemptId
336
+ }),
337
+ ...DEFAULT_OWNED_COMMAND_LIMITS,
338
+ timeoutMs: req.timeoutMs ?? DEFAULT_OWNED_COMMAND_LIMITS.timeoutMs,
339
+ teardownGraceMs: req.timeoutGraceMs ?? DEFAULT_OWNED_COMMAND_LIMITS.teardownGraceMs,
340
+ maxOutputBytes: req.maxOutputBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxOutputBytes,
341
+ maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes
342
+ }, signal).catch((error) => {
343
+ throw devinCommandError(error, executable);
344
+ });
345
+ const conversation = readExport(exportFile);
346
+ const answered = conversation?.answered === true;
347
+ const aborted = sub.aborted || signal.aborted;
348
+ if (aborted && !answered) throw new EngineError({ kind: "aborted", message: "devin run aborted" });
349
+ const diagnostic = scrubCapture(
350
+ [new TextDecoder().decode(sub.stderr), new TextDecoder().decode(sub.stdout)].filter((text) => text.length > 0).join("\n"),
351
+ env,
352
+ DIAGNOSTIC_MAX
353
+ ).trim();
354
+ const failed = aborted || sub.timedOut || sub.exitCode !== 0;
355
+ if (failed && !answered) {
356
+ throw new EngineError({
357
+ kind: sub.timedOut ? "timeout" : classifyEngineFailure(new Error(diagnostic)),
358
+ message: `devin exited ${sub.exitCode ?? "?"}${diagnostic ? `: ${diagnostic}` : ""}`
359
+ });
360
+ }
361
+ if (conversation === void 0) {
362
+ throw new EngineError({
363
+ kind: "unknown",
364
+ message: `devin wrote no readable conversation export${diagnostic ? `: ${diagnostic}` : ""}`
365
+ });
366
+ }
367
+ let effective = requested;
368
+ if (conversation.model !== void 0) {
369
+ try {
370
+ effective = engineSelection({
371
+ ...requested,
372
+ model: conversation.model,
373
+ modelFamily: modelIdentity(conversation.model).modelFamily
374
+ });
375
+ } catch {
376
+ }
377
+ }
378
+ const parts = conversation.messages.map((text, index) => ({
379
+ kind: "assistant",
380
+ text,
381
+ final: answered && index === conversation.messages.length - 1
382
+ }));
383
+ for (const text of conversation.messages) onEvent({ type: "text", delta: text });
384
+ for (const event of conversation.toolEvents) onEvent(event);
385
+ if (!answered) {
386
+ throw new EngineIncompleteResultError(
387
+ `devin ended without a final answer under permission mode ${permissionMode(req)}${diagnostic ? `: ${diagnostic}` : ""}`,
388
+ { parts, usage: conversation.usage, requested, effective }
389
+ );
390
+ }
391
+ onEvent({ type: "usage", usage: conversation.usage, model: effective.model ?? "devin" });
392
+ return validateAgentResult({
393
+ parts,
394
+ usage: conversation.usage,
395
+ requested,
396
+ effective,
397
+ ...failed ? {
398
+ transportFailure: {
399
+ kind: aborted ? "aborted" : sub.timedOut ? "timeout" : "unknown",
400
+ message: `devin completed but exited ${sub.exitCode ?? "?"} during teardown${diagnostic ? `: ${diagnostic}` : ""}`,
401
+ exitCode: sub.exitCode ?? null
402
+ }
403
+ } : {}
404
+ });
405
+ } finally {
406
+ rmSync(dir, { recursive: true, force: true });
407
+ }
408
+ }
409
+ };
410
+
411
+ export { DevinCliEngine, buildDevinArgs, devin };
412
+ //# sourceMappingURL=index.js.map
413
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/atif.ts","../src/devin-cli.ts"],"names":["value"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Read the conversation file `devin --export` writes. Devin 3000.11 writes it\n * in the ATIF v1 shape: ordered steps from the system, the user and the agent,\n * with each agent step carrying its text, its tool calls, the results of those\n * calls and the model that produced it.\n */\nimport type { EngineStreamEvent, UsageReceipt } from '@obversa/api';\n\nexport interface DevinConversation {\n /** Every non-empty agent message, in order. */\n readonly messages: readonly string[];\n /** True when the conversation ends on an agent message with no tool calls. */\n readonly answered: boolean;\n /** The model named on the last agent step, when Devin names one. */\n readonly model: string | undefined;\n readonly toolEvents: readonly Extract<EngineStreamEvent, { type: 'tool' }>[];\n readonly usage: UsageReceipt;\n}\n\nfunction record(value: unknown, what: string): Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Devin export ${what} is not an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction messageText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (!Array.isArray(value)) return '';\n return value\n .map((part) => (part !== null && typeof part === 'object'\n && (part as Record<string, unknown>).type === 'text'\n && typeof (part as Record<string, unknown>).text === 'string'\n ? (part as Record<string, string>).text\n : ''))\n .join('');\n}\n\nfunction tokenCount(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction usage(value: unknown): UsageReceipt {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return { kind: 'unknown' };\n }\n const totals = value as Record<string, unknown>;\n const input = totals.total_prompt_tokens;\n const output = totals.total_completion_tokens;\n const cached = totals.total_cached_tokens;\n if (!tokenCount(input) || !tokenCount(output)\n || (cached != null && (!tokenCount(cached) || cached > input))) {\n return { kind: 'unknown' };\n }\n return {\n kind: 'reported',\n inputTokens: input,\n outputTokens: output,\n ...(cached == null ? {} : { cacheReadInputTokens: cached }),\n };\n}\n\nexport function readDevinExport(value: unknown): DevinConversation {\n const root = record(value, 'root');\n if (typeof root.schema_version !== 'string' || !root.schema_version.startsWith('ATIF-v1.')) {\n throw new Error('Devin export is not in the ATIF v1 shape');\n }\n if (!Array.isArray(root.steps)) throw new Error('Devin export has no steps');\n\n const messages: string[] = [];\n const toolEvents: Extract<EngineStreamEvent, { type: 'tool' }>[] = [];\n let answered = false;\n let model: string | undefined;\n for (const value of root.steps) {\n const step = record(value, 'step');\n if (step.source !== 'agent') {\n answered = false;\n continue;\n }\n if (typeof step.model_name === 'string' && step.model_name.length > 0) {\n model = step.model_name;\n }\n const calls = new Map<string, string>();\n if (Array.isArray(step.tool_calls)) {\n for (const value of step.tool_calls) {\n const call = record(value, 'tool call');\n if (typeof call.tool_call_id !== 'string' || typeof call.function_name !== 'string') {\n throw new Error('Devin export has a tool call without an id or a name');\n }\n calls.set(call.tool_call_id, call.function_name);\n toolEvents.push({ type: 'tool', name: call.function_name, phase: 'use' });\n }\n }\n const results = step.observation === undefined || step.observation === null\n ? []\n : record(step.observation, 'observation').results;\n if (Array.isArray(results)) {\n for (const value of results) {\n const id = record(value, 'tool result').source_call_id;\n const name = typeof id === 'string' ? calls.get(id) : undefined;\n if (name !== undefined) toolEvents.push({ type: 'tool', name, phase: 'result' });\n }\n }\n const text = messageText(step.message);\n if (text.length > 0) messages.push(text);\n answered = text.length > 0 && calls.size === 0;\n }\n return { messages, answered, model, toolEvents, usage: usage(root.final_metrics) };\n}\n","/**\n * Engine plugin: the `devin` CLI as a non-interactive subprocess. One fresh\n * `devin -p` process runs each attempt, with the person's own environment,\n * Devin login and Devin settings. The plugin adds only what the step needs:\n * Devin's permission mode for the workspace mode, the model when one is named,\n * and a conversation export it reads the answer from.\n */\nimport { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, isAbsolute, join } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport {\n EngineError,\n EngineIncompleteResultError,\n assertReadAccess,\n classifyEngineFailure,\n engineSelection,\n modelIdentity,\n validateAgentResult,\n type AgentRequest,\n type AgentResult,\n type AgentResultPart,\n type Engine,\n type EngineEventSink,\n type EngineSelectionRecord,\n} from '@obversa/api';\nimport {\n attemptEnvironment,\n scrubCapture,\n DEFAULT_OWNED_COMMAND_LIMITS,\n OwnedCommandError,\n ownedCommandIdentity,\n resolveCommandExecutable,\n runOwnedCommand,\n} from '@obversa/core/command';\nimport { readDevinExport, type DevinConversation } from './atif.js';\n\nconst ADAPTER = 'devin-cli';\nconst PROVIDER = 'cognition';\n/** The model a seat records when it leaves the choice to the person's Devin settings. */\nconst DEFAULT_MODEL = 'default';\nconst DIAGNOSTIC_MAX = 700;\n\nexport interface DevinCliEngineOptions {\n /** Model for requests that name none. Without one, Devin uses the person's default. */\n readonly defaultModel?: string;\n /** Absolute path or bare command name; `devin` by default. */\n readonly cliBinary?: string;\n}\n\nexport interface DevinSeat {\n readonly engine: DevinCliEngine;\n readonly identity: {\n readonly adapter: 'devin-cli';\n readonly provider: 'cognition';\n readonly modelFamily: string;\n readonly model: string;\n readonly tools: readonly string[];\n };\n}\n\n/**\n * Create the Devin seat used by declarative team workflows. Without a model,\n * Devin runs the default model from the person's own Devin settings and the\n * seat records the model as `default`.\n */\nexport function devin(model?: string): DevinSeat {\n if (model !== undefined && !model.trim()) throw new TypeError('devin model must not be empty');\n return {\n engine: new DevinCliEngine(model === undefined ? {} : { defaultModel: model }),\n identity: {\n adapter: ADAPTER,\n provider: PROVIDER,\n modelFamily: modelIdentity(model ?? DEFAULT_MODEL).modelFamily,\n model: model ?? DEFAULT_MODEL,\n tools: ['read', 'edit', 'exec'],\n },\n };\n}\n\nfunction modelFor(req: AgentRequest, opts: DevinCliEngineOptions): string | undefined {\n const model = req.model ?? opts.defaultModel;\n return model === undefined || model === DEFAULT_MODEL ? undefined : model;\n}\n\n/** Devin's permission mode for a workspace mode: read-only tools, or those plus workspace edits. */\nfunction permissionMode(req: AgentRequest): 'auto' | 'accept-edits' {\n return req.workspaceMode === 'write' ? 'accept-edits' : 'auto';\n}\n\n/**\n * Build the `devin` argv for one attempt. Exported so the flag wiring is\n * testable without spawning a process.\n */\nexport function buildDevinArgs(\n req: AgentRequest,\n opts: DevinCliEngineOptions,\n files: { readonly promptFile: string; readonly exportFile: string },\n): string[] {\n try {\n assertReadAccess(req);\n if (req.tools?.length === 0) {\n throw new TypeError('devin cannot turn its tools off; choose an engine that supports tools: []');\n }\n if (req.workspaceMode === 'none') {\n throw new TypeError('devin has no mode without workspace access; choose read or write');\n }\n } catch (cause) {\n throw new EngineError({\n kind: 'invalid-config',\n message: cause instanceof Error ? cause.message : 'invalid Devin workspace configuration',\n cause,\n });\n }\n const model = modelFor(req, opts);\n return [\n '-p',\n '--prompt-file', files.promptFile,\n '--export', files.exportFile,\n '--permission-mode', permissionMode(req),\n // Print mode cannot show Devin's folder trust prompt; without this flag\n // every folder the person has not opened in Devin before refuses to run.\n '--respect-workspace-trust', 'false',\n ...(model === undefined ? [] : ['--model', model]),\n ];\n}\n\nfunction requestedSelection(req: AgentRequest, opts: DevinCliEngineOptions): EngineSelectionRecord {\n const model = modelFor(req, opts);\n return engineSelection({\n adapter: ADAPTER,\n provider: PROVIDER,\n modelFamily: model === undefined ? null : modelIdentity(model).modelFamily,\n model: model ?? null,\n capabilities: req.tools ?? [],\n });\n}\n\nfunction assertDevinConfiguration(req: AgentRequest, opts: DevinCliEngineOptions): void {\n buildDevinArgs(req, opts, { promptFile: 'prompt', exportFile: 'export' });\n try {\n const env = attemptEnvironment(req) ?? {};\n const limits = {\n timeoutMs: req.timeoutMs ?? DEFAULT_OWNED_COMMAND_LIMITS.timeoutMs,\n teardownGraceMs: req.timeoutGraceMs ?? DEFAULT_OWNED_COMMAND_LIMITS.teardownGraceMs,\n maxOutputBytes: req.maxOutputBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxOutputBytes,\n maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes,\n };\n if (!isAbsolute(req.cwd ?? process.cwd())\n || Object.values(env).some((value) => typeof value !== 'string')\n || !Number.isSafeInteger(limits.timeoutMs) || limits.timeoutMs < 1\n || !Number.isSafeInteger(limits.teardownGraceMs) || limits.teardownGraceMs < 0\n || limits.timeoutMs + limits.teardownGraceMs > 2_147_483_647\n || !Number.isSafeInteger(limits.maxOutputBytes) || limits.maxOutputBytes < 0\n || !Number.isSafeInteger(limits.maxMemoryBytes) || limits.maxMemoryBytes < 1) {\n throw new Error('invalid Devin command request');\n }\n ownedCommandIdentity({ adapter: ADAPTER, runId: req.attempt?.runId,\n leafId: req.attempt?.leafId, attemptId: req.attempt?.attemptId });\n } catch {\n throw new EngineError({ kind: 'invalid-config', message: 'invalid Devin command configuration' });\n }\n}\n\nfunction devinCommandError(error: unknown, executable?: string): unknown {\n if (!(error instanceof OwnedCommandError)) return error;\n if (error.code === 'INVALID_EXECUTABLE') {\n return new EngineError({ kind: 'missing-cli', message: 'Devin executable is not runnable' });\n }\n if (error.code === 'INVALID_COMMAND') {\n return new EngineError({ kind: 'invalid-config', message: 'invalid Devin command request' });\n }\n if (error.code === 'SPAWN_FAILED') {\n if (executable !== undefined) {\n try { resolveCommandExecutable(executable); }\n catch { return new EngineError({ kind: 'missing-cli', message: 'Devin executable is not runnable' }); }\n }\n return new EngineError({ kind: 'unknown', message: 'Devin command could not start' });\n }\n return error;\n}\n\nfunction readExport(path: string): DevinConversation | undefined {\n let text: string;\n try {\n text = readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n try {\n return readDevinExport(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\nexport class DevinCliEngine implements Engine {\n readonly name = ADAPTER;\n private executable: string | undefined;\n private version: Promise<string> | undefined;\n constructor(private readonly opts: DevinCliEngineOptions = {}) {}\n\n private commandExecutable(expected?: EngineSelectionRecord): string {\n const configured = this.opts.cliBinary ?? 'devin';\n if (configured.length === 0 || (!isAbsolute(configured) && basename(configured) !== configured)) {\n throw new EngineError({ kind: 'invalid-config', message: 'Devin command must be absolute or a bare name' });\n }\n if (expected && (expected.executable === null\n || (isAbsolute(configured) && configured !== expected.executable)\n || (this.executable !== undefined && this.executable !== expected.executable))) {\n throw new EngineError({ kind: 'invalid-config', message: 'Devin executable selection changed' });\n }\n try {\n this.executable ??= resolveCommandExecutable(expected?.executable ?? configured);\n return resolveCommandExecutable(this.executable);\n } catch (error) { throw devinCommandError(error, this.executable); }\n }\n\n private async observeVersion(req: AgentRequest, executable: string, signal: AbortSignal): Promise<string> {\n try {\n const result = await runOwnedCommand({\n executable, args: ['--version'], stdin: '',\n cwd: req.cwd ?? process.cwd(), env: attemptEnvironment(req) ?? {},\n ...ownedCommandIdentity({ adapter: ADAPTER, runId: req.attempt?.runId,\n leafId: req.attempt?.leafId, attemptId: req.attempt?.attemptId }),\n ...DEFAULT_OWNED_COMMAND_LIMITS,\n timeoutMs: Math.min(req.timeoutMs ?? 5_000, 5_000),\n teardownGraceMs: Math.min(req.timeoutGraceMs ?? 1_000, 1_000),\n maxOutputBytes: Math.min(req.maxOutputBytes ?? 4_096, 4_096),\n maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes,\n }, signal);\n if (result.aborted || signal.aborted) {\n throw new EngineError({ kind: 'aborted', message: 'Devin version observation aborted' });\n }\n if (result.timedOut) {\n throw new EngineError({ kind: 'timeout', message: 'Devin version observation timed out' });\n }\n if (result.exitCode !== 0) {\n throw new EngineError({ kind: 'unknown', message: 'Devin version command failed' });\n }\n const matched = /^devin (\\d+\\.\\d+\\.\\d+) \\([0-9a-f]+\\)$/.exec(new TextDecoder().decode(result.stdout).trim());\n if (!matched) {\n throw new EngineError({ kind: 'invalid-config', message: 'Devin version output is not recognized' });\n }\n return matched[1]!;\n } catch (error) {\n const mapped = devinCommandError(error, executable);\n if (mapped instanceof EngineError) throw mapped;\n throw new EngineError({ kind: 'unknown', message: 'Devin version observation failed' });\n }\n }\n\n async admit(\n request: Omit<AgentRequest, 'prompt'>,\n signal: AbortSignal,\n expectedSelection?: EngineSelectionRecord,\n ): Promise<EngineSelectionRecord> {\n if (signal.aborted) throw new EngineError({ kind: 'aborted', message: 'Devin admission aborted' });\n const req: AgentRequest = { ...request, prompt: '' };\n assertDevinConfiguration(req, this.opts);\n let expected: EngineSelectionRecord | undefined;\n let proposed: EngineSelectionRecord;\n try {\n expected = expectedSelection === undefined ? undefined : engineSelection(expectedSelection);\n proposed = requestedSelection(req, this.opts);\n } catch {\n throw new EngineError({ kind: 'invalid-config', message: 'invalid Devin selection' });\n }\n const executable = this.commandExecutable(expected);\n let observation = this.version;\n if (observation === undefined) {\n observation = this.observeVersion(req, executable, signal);\n this.version = observation;\n }\n let adapterVersion: string;\n try { adapterVersion = await observation; }\n catch (error) {\n if (this.version === observation) this.version = undefined;\n throw error;\n }\n if (signal.aborted) throw new EngineError({ kind: 'aborted', message: 'Devin admission aborted' });\n const selected = engineSelection({ ...proposed, executable, adapterVersion });\n if (expected && !isDeepStrictEqual(selected, expected)) {\n throw new EngineError({ kind: 'invalid-config', message: 'Devin selection changed' });\n }\n return selected;\n }\n\n async run(\n req: AgentRequest,\n onEvent: EngineEventSink,\n signal: AbortSignal,\n ): Promise<AgentResult> {\n if (signal.aborted) throw new EngineError({ kind: 'aborted', message: 'devin run aborted' });\n const { prompt: _prompt, ...admissionRequest } = req;\n const requested = await this.admit(admissionRequest, signal);\n const executable = requested.executable!;\n const dir = mkdtempSync(join(tmpdir(), 'obversa-devin-'));\n const promptFile = join(dir, 'prompt.md');\n const exportFile = join(dir, 'conversation.json');\n const env = attemptEnvironment(req);\n try {\n // Devin has no system prompt flag, so system text leads the prompt.\n writeFileSync(\n promptFile,\n req.system ? `${req.system}\\n\\n---\\n\\n${req.prompt}` : req.prompt,\n { encoding: 'utf8', mode: 0o600 },\n );\n const sub = await runOwnedCommand({\n executable,\n args: buildDevinArgs(req, this.opts, { promptFile, exportFile }),\n cwd: req.cwd ?? process.cwd(),\n env: env ?? {},\n stdin: '',\n ...ownedCommandIdentity({ adapter: ADAPTER, runId: req.attempt?.runId,\n leafId: req.attempt?.leafId, attemptId: req.attempt?.attemptId }),\n ...DEFAULT_OWNED_COMMAND_LIMITS,\n timeoutMs: req.timeoutMs ?? DEFAULT_OWNED_COMMAND_LIMITS.timeoutMs,\n teardownGraceMs: req.timeoutGraceMs ?? DEFAULT_OWNED_COMMAND_LIMITS.teardownGraceMs,\n maxOutputBytes: req.maxOutputBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxOutputBytes,\n maxMemoryBytes: req.maxMemoryBytes ?? DEFAULT_OWNED_COMMAND_LIMITS.maxMemoryBytes,\n }, signal).catch((error: unknown) => { throw devinCommandError(error, executable); });\n\n const conversation = readExport(exportFile);\n const answered = conversation?.answered === true;\n const aborted = sub.aborted || signal.aborted;\n if (aborted && !answered) throw new EngineError({ kind: 'aborted', message: 'devin run aborted' });\n const diagnostic = scrubCapture(\n [new TextDecoder().decode(sub.stderr), new TextDecoder().decode(sub.stdout)]\n .filter((text) => text.length > 0).join('\\n'),\n env,\n DIAGNOSTIC_MAX,\n ).trim();\n const failed = aborted || sub.timedOut || sub.exitCode !== 0;\n if (failed && !answered) {\n throw new EngineError({\n kind: sub.timedOut ? 'timeout' : classifyEngineFailure(new Error(diagnostic)),\n message: `devin exited ${sub.exitCode ?? '?'}${diagnostic ? `: ${diagnostic}` : ''}`,\n });\n }\n if (conversation === undefined) {\n throw new EngineError({\n kind: 'unknown',\n message: `devin wrote no readable conversation export${diagnostic ? `: ${diagnostic}` : ''}`,\n });\n }\n\n let effective = requested;\n if (conversation.model !== undefined) {\n try {\n effective = engineSelection({\n ...requested,\n model: conversation.model,\n modelFamily: modelIdentity(conversation.model).modelFamily,\n });\n } catch {\n // A model name Devin reports but no family can be read from keeps the requested record.\n }\n }\n const parts: AgentResultPart[] = conversation.messages.map((text, index) => ({\n kind: 'assistant',\n text,\n final: answered && index === conversation.messages.length - 1,\n }));\n for (const text of conversation.messages) onEvent({ type: 'text', delta: text });\n for (const event of conversation.toolEvents) onEvent(event);\n if (!answered) {\n // A tool the permission mode does not approve is refused, and Devin\n // can then end with no answer and exit 0.\n throw new EngineIncompleteResultError(\n `devin ended without a final answer under permission mode ${permissionMode(req)}${\n diagnostic ? `: ${diagnostic}` : ''\n }`,\n { parts, usage: conversation.usage, requested, effective },\n );\n }\n onEvent({ type: 'usage', usage: conversation.usage, model: effective.model ?? 'devin' });\n return validateAgentResult({\n parts,\n usage: conversation.usage,\n requested,\n effective,\n ...(failed\n ? {\n transportFailure: {\n kind: aborted ? 'aborted' as const : sub.timedOut ? 'timeout' as const : 'unknown' as const,\n message: `devin completed but exited ${sub.exitCode ?? '?'} during teardown${\n diagnostic ? `: ${diagnostic}` : ''\n }`,\n exitCode: sub.exitCode ?? null,\n },\n }\n : {}),\n });\n } finally {\n rmSync(dir, { recursive: true, force: true });\n }\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@obversa/engine-devin-cli",
3
+ "version": "0.1.0",
4
+ "description": "Run engine requests through a fresh Devin CLI process.",
5
+ "author": "Jonny Neill <jonnyneill@hotmail.com>",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "sideEffects": false,
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "engines": {
13
+ "node": ">=22.12.0"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "dependencies": {
29
+ "@obversa/api": "^0.2.3",
30
+ "@obversa/core": "^0.2.3"
31
+ },
32
+ "keywords": [
33
+ "agent",
34
+ "graph",
35
+ "workflow",
36
+ "runtime",
37
+ "obversa"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/jonny981/obversa.git"
42
+ },
43
+ "homepage": "https://docs.obversa.ai",
44
+ "bugs": {
45
+ "url": "https://github.com/jonny981/obversa/issues"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup && tsc -p tsconfig.build.json",
49
+ "test": "vitest run",
50
+ "typecheck": "tsc --noEmit -p tsconfig.json",
51
+ "typecheck:ts6": "tsc6 --noEmit -p tsconfig.json"
52
+ }
53
+ }