@ai-devkit/agent-manager 0.38.0 → 0.40.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.
Files changed (126) hide show
  1. package/dist/__tests__/capacity/devin.test.js +239 -0
  2. package/dist/__tests__/capacity/devin.test.js.map +1 -0
  3. package/dist/__tests__/harnesses/devin/DevinAdapter.test.js +597 -0
  4. package/dist/__tests__/harnesses/devin/DevinAdapter.test.js.map +1 -0
  5. package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js +91 -0
  6. package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js.map +1 -0
  7. package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js +192 -0
  8. package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js.map +1 -0
  9. package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js +407 -0
  10. package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js.map +1 -0
  11. package/dist/__tests__/harnesses/devin/fixtures.js +95 -0
  12. package/dist/__tests__/harnesses/devin/fixtures.js.map +1 -0
  13. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js +64 -1
  14. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -1
  15. package/dist/__tests__/terminal/GhosttyBackend.test.js +334 -0
  16. package/dist/__tests__/terminal/GhosttyBackend.test.js.map +1 -0
  17. package/dist/adapters/AgentAdapter.d.ts +1 -1
  18. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  19. package/dist/adapters/AgentAdapter.js +2 -1
  20. package/dist/adapters/AgentAdapter.js.map +1 -1
  21. package/dist/capacity/index.d.ts +3 -0
  22. package/dist/capacity/index.d.ts.map +1 -1
  23. package/dist/capacity/index.js +8 -0
  24. package/dist/capacity/index.js.map +1 -1
  25. package/dist/capacity/providers/devin.d.ts +13 -0
  26. package/dist/capacity/providers/devin.d.ts.map +1 -0
  27. package/dist/capacity/providers/devin.js +92 -0
  28. package/dist/capacity/providers/devin.js.map +1 -0
  29. package/dist/capacity/sources/devin.d.ts +12 -0
  30. package/dist/capacity/sources/devin.d.ts.map +1 -0
  31. package/dist/capacity/sources/devin.js +29 -0
  32. package/dist/capacity/sources/devin.js.map +1 -0
  33. package/dist/harnesses/devin/DevinAdapter.d.ts +39 -0
  34. package/dist/harnesses/devin/DevinAdapter.d.ts.map +1 -0
  35. package/dist/harnesses/devin/DevinAdapter.js +177 -0
  36. package/dist/harnesses/devin/DevinAdapter.js.map +1 -0
  37. package/dist/harnesses/devin/DevinAgentMapper.d.ts +11 -0
  38. package/dist/harnesses/devin/DevinAgentMapper.d.ts.map +1 -0
  39. package/dist/harnesses/devin/DevinAgentMapper.js +35 -0
  40. package/dist/harnesses/devin/DevinAgentMapper.js.map +1 -0
  41. package/dist/harnesses/devin/DevinSessionLocator.d.ts +46 -0
  42. package/dist/harnesses/devin/DevinSessionLocator.d.ts.map +1 -0
  43. package/dist/harnesses/devin/DevinSessionLocator.js +176 -0
  44. package/dist/harnesses/devin/DevinSessionLocator.js.map +1 -0
  45. package/dist/harnesses/devin/DevinSessionParser.d.ts +29 -0
  46. package/dist/harnesses/devin/DevinSessionParser.d.ts.map +1 -0
  47. package/dist/harnesses/devin/DevinSessionParser.js +166 -0
  48. package/dist/harnesses/devin/DevinSessionParser.js.map +1 -0
  49. package/dist/harnesses/devin/credentials.d.ts +10 -0
  50. package/dist/harnesses/devin/credentials.d.ts.map +1 -0
  51. package/dist/harnesses/devin/credentials.js +66 -0
  52. package/dist/harnesses/devin/credentials.js.map +1 -0
  53. package/dist/harnesses/devin/readiness.d.ts +3 -0
  54. package/dist/harnesses/devin/readiness.d.ts.map +1 -0
  55. package/dist/harnesses/devin/readiness.js +39 -0
  56. package/dist/harnesses/devin/readiness.js.map +1 -0
  57. package/dist/harnesses/index.d.ts.map +1 -1
  58. package/dist/harnesses/index.js +3 -1
  59. package/dist/harnesses/index.js.map +1 -1
  60. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -1
  61. package/dist/harnesses/readiness/AgentReadiness.js +3 -1
  62. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -1
  63. package/dist/harnesses/readiness/types.d.ts +1 -1
  64. package/dist/harnesses/readiness/types.d.ts.map +1 -1
  65. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -1
  66. package/dist/harnesses/runtimeProfiles.js +2 -1
  67. package/dist/harnesses/runtimeProfiles.js.map +1 -1
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +1 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  73. package/dist/terminal/TtyWriter.js +4 -3
  74. package/dist/terminal/TtyWriter.js.map +1 -1
  75. package/dist/terminal/backends/ghostty.d.ts +3 -0
  76. package/dist/terminal/backends/ghostty.d.ts.map +1 -0
  77. package/dist/terminal/backends/ghostty.js +161 -0
  78. package/dist/terminal/backends/ghostty.js.map +1 -0
  79. package/dist/terminal/backends/index.d.ts.map +1 -1
  80. package/dist/terminal/backends/index.js +2 -0
  81. package/dist/terminal/backends/index.js.map +1 -1
  82. package/dist/terminal/backends/shared.d.ts +2 -0
  83. package/dist/terminal/backends/shared.d.ts.map +1 -1
  84. package/dist/terminal/backends/shared.js +4 -1
  85. package/dist/terminal/backends/shared.js.map +1 -1
  86. package/dist/terminal/types.d.ts +1 -0
  87. package/dist/terminal/types.d.ts.map +1 -1
  88. package/dist/terminal/types.js +1 -0
  89. package/dist/terminal/types.js.map +1 -1
  90. package/dist/utils/process.d.ts +2 -0
  91. package/dist/utils/process.d.ts.map +1 -1
  92. package/dist/utils/process.js +3 -0
  93. package/dist/utils/process.js.map +1 -1
  94. package/package.json +1 -1
  95. package/src/__tests__/capacity/devin.test.ts +210 -0
  96. package/src/__tests__/capacity/fixtures/devin-config.json +6 -0
  97. package/src/__tests__/capacity/fixtures/devin-credentials.toml +4 -0
  98. package/src/__tests__/capacity/fixtures/devin-quota-nonplan.json +5 -0
  99. package/src/__tests__/capacity/fixtures/devin-quota.json +10 -0
  100. package/src/__tests__/harnesses/devin/DevinAdapter.test.ts +292 -0
  101. package/src/__tests__/harnesses/devin/DevinAgentMapper.test.ts +88 -0
  102. package/src/__tests__/harnesses/devin/DevinSessionLocator.test.ts +139 -0
  103. package/src/__tests__/harnesses/devin/DevinSessionParser.test.ts +218 -0
  104. package/src/__tests__/harnesses/devin/fixtures.ts +144 -0
  105. package/src/__tests__/harnesses/readiness/AgentReadiness.test.ts +41 -0
  106. package/src/__tests__/terminal/GhosttyBackend.test.ts +271 -0
  107. package/src/adapters/AgentAdapter.ts +1 -0
  108. package/src/capacity/index.ts +12 -0
  109. package/src/capacity/providers/devin.ts +121 -0
  110. package/src/capacity/sources/devin.ts +49 -0
  111. package/src/harnesses/devin/DevinAdapter.ts +224 -0
  112. package/src/harnesses/devin/DevinAgentMapper.ts +43 -0
  113. package/src/harnesses/devin/DevinSessionLocator.ts +230 -0
  114. package/src/harnesses/devin/DevinSessionParser.ts +236 -0
  115. package/src/harnesses/devin/credentials.ts +88 -0
  116. package/src/harnesses/devin/readiness.ts +36 -0
  117. package/src/harnesses/index.ts +2 -0
  118. package/src/harnesses/readiness/AgentReadiness.ts +2 -0
  119. package/src/harnesses/runtimeProfiles.ts +1 -0
  120. package/src/index.ts +1 -0
  121. package/src/terminal/TtyWriter.ts +5 -5
  122. package/src/terminal/backends/ghostty.ts +180 -0
  123. package/src/terminal/backends/index.ts +2 -0
  124. package/src/terminal/backends/shared.ts +6 -1
  125. package/src/terminal/types.ts +1 -0
  126. package/src/utils/process.ts +5 -0
@@ -0,0 +1,176 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import Database from "better-sqlite3";
4
+ import { DevinSessionParser } from "./DevinSessionParser.js";
5
+ import { homeDir } from "../shared.js";
6
+ const SESSION_REF_SEP = "::";
7
+ const LOCK_FILE_SUFFIX = ".lock";
8
+ export function encodeDevinSessionRef(dbPath, sessionId) {
9
+ return `${dbPath}${SESSION_REF_SEP}${sessionId}`;
10
+ }
11
+ /** Inverse of {@link encodeDevinSessionRef}; null when the ref has no separator. */ export function decodeDevinSessionRef(ref) {
12
+ const idx = ref.lastIndexOf(SESSION_REF_SEP);
13
+ return idx === -1 ? null : ref.slice(idx + SESSION_REF_SEP.length);
14
+ }
15
+ function toDevinSession(row) {
16
+ return {
17
+ sessionId: row.id,
18
+ directory: row.working_directory,
19
+ title: row.title,
20
+ timeCreated: row.created_at * 1000,
21
+ lastActivityAt: row.last_activity_at * 1000
22
+ };
23
+ }
24
+ export class DevinSessionLocator {
25
+ parser;
26
+ dbPath;
27
+ locksDir;
28
+ db = null;
29
+ constructor(dbPath = DevinSessionLocator.resolveDbPath(), locksDir = DevinSessionLocator.defaultLocksDir(dbPath), parser = new DevinSessionParser()){
30
+ this.parser = parser;
31
+ this.dbPath = dbPath;
32
+ this.locksDir = locksDir;
33
+ }
34
+ static resolveDbPath() {
35
+ const xdg = process.env.XDG_DATA_HOME;
36
+ const base = xdg || path.join(homeDir(), ".local", "share");
37
+ return path.join(base, "devin", "cli", "sessions.db");
38
+ }
39
+ static defaultLocksDir(dbPath) {
40
+ return path.join(path.dirname(dbPath), "session_locks");
41
+ }
42
+ openDb() {
43
+ if (this.db) return this.db;
44
+ if (!fs.existsSync(this.dbPath)) return null;
45
+ try {
46
+ this.db = new Database(this.dbPath, {
47
+ readonly: true
48
+ });
49
+ return this.db;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ close() {
55
+ if (this.db) {
56
+ try {
57
+ this.db.close();
58
+ } catch {
59
+ /* ignore */ }
60
+ this.db = null;
61
+ }
62
+ }
63
+ /**
64
+ * Read `session_locks/*.lock` entries. Each file name is a session slug and
65
+ * its body is the PID of the `devin acp` backend holding the session.
66
+ * Malformed files are skipped; callers validate liveness against the
67
+ * process snapshot.
68
+ */ listActiveLocks() {
69
+ let entries;
70
+ try {
71
+ entries = fs.readdirSync(this.locksDir);
72
+ } catch {
73
+ return [];
74
+ }
75
+ const locks = [];
76
+ for (const entry of entries){
77
+ if (!entry.endsWith(LOCK_FILE_SUFFIX)) continue;
78
+ const sessionId = entry.slice(0, -LOCK_FILE_SUFFIX.length);
79
+ if (!sessionId) continue;
80
+ try {
81
+ const raw = fs.readFileSync(path.join(this.locksDir, entry), "utf-8").trim();
82
+ const pid = parseInt(raw, 10);
83
+ if (Number.isFinite(pid) && pid > 0) locks.push({
84
+ sessionId,
85
+ pid
86
+ });
87
+ } catch {
88
+ /* skip unreadable lock */ }
89
+ }
90
+ return locks;
91
+ }
92
+ findSessionById(db, sessionId) {
93
+ try {
94
+ const row = db.prepare(`
95
+ SELECT id, working_directory, title, created_at, last_activity_at
96
+ FROM sessions
97
+ WHERE id = ? AND hidden = 0
98
+ `).get(sessionId);
99
+ return row ? toDevinSession(row) : null;
100
+ } catch {
101
+ return null;
102
+ }
103
+ }
104
+ findSessionForDirectory(db, directory) {
105
+ try {
106
+ const row = db.prepare(`
107
+ SELECT id, working_directory, title, created_at, last_activity_at
108
+ FROM sessions
109
+ WHERE working_directory = ? AND hidden = 0
110
+ ORDER BY last_activity_at DESC
111
+ LIMIT 1
112
+ `).get(directory);
113
+ return row ? toDevinSession(row) : null;
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+ listSessions(opts) {
119
+ const db = this.openDb();
120
+ if (!db) return [];
121
+ try {
122
+ const rows = db.prepare(`
123
+ SELECT id, working_directory, title, created_at, last_activity_at
124
+ FROM sessions
125
+ WHERE hidden = 0
126
+ ORDER BY last_activity_at DESC
127
+ `).all();
128
+ const summaries = [];
129
+ for (const row of rows){
130
+ if (opts?.cwd !== undefined && row.working_directory !== opts.cwd) continue;
131
+ summaries.push(this.toSessionSummary(db, row));
132
+ }
133
+ return summaries;
134
+ } catch {
135
+ this.close();
136
+ return [];
137
+ }
138
+ }
139
+ findSessionsById(sessionId) {
140
+ const db = this.openDb();
141
+ if (!db) return [];
142
+ try {
143
+ const row = db.prepare(`
144
+ SELECT id, working_directory, title, created_at, last_activity_at
145
+ FROM sessions
146
+ WHERE id = ? AND hidden = 0
147
+ `).get(sessionId);
148
+ return row ? [
149
+ this.toSessionSummary(db, row)
150
+ ] : [];
151
+ } catch {
152
+ this.close();
153
+ return [];
154
+ }
155
+ }
156
+ toSessionSummary(db, row) {
157
+ const session = toDevinSession(row);
158
+ return {
159
+ type: "devin",
160
+ sessionId: session.sessionId,
161
+ cwd: session.directory,
162
+ firstUserMessage: this.parser.getFirstUserMessage(db, session.sessionId),
163
+ lastActive: new Date(session.lastActivityAt),
164
+ startedAt: new Date(session.timeCreated),
165
+ sessionFilePath: encodeDevinSessionRef(this.dbPath, session.sessionId)
166
+ };
167
+ }
168
+ get dbFilePath() {
169
+ return this.dbPath;
170
+ }
171
+ get sessionLocksDir() {
172
+ return this.locksDir;
173
+ }
174
+ }
175
+
176
+ //# sourceMappingURL=DevinSessionLocator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/devin/DevinSessionLocator.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport * as path from \"path\";\nimport Database from \"better-sqlite3\";\nimport type { ListSessionsOptions, SessionSummary } from \"../../adapters/AgentAdapter.js\";\nimport { DevinSessionParser } from \"./DevinSessionParser.js\";\nimport { homeDir } from \"../shared.js\";\n\nconst SESSION_REF_SEP = \"::\";\nconst LOCK_FILE_SUFFIX = \".lock\";\n\nexport function encodeDevinSessionRef(dbPath: string, sessionId: string): string {\n return `${dbPath}${SESSION_REF_SEP}${sessionId}`;\n}\n\n/** Inverse of {@link encodeDevinSessionRef}; null when the ref has no separator. */\nexport function decodeDevinSessionRef(ref: string): string | null {\n const idx = ref.lastIndexOf(SESSION_REF_SEP);\n return idx === -1 ? null : ref.slice(idx + SESSION_REF_SEP.length);\n}\n\nexport interface DevinSession {\n sessionId: string;\n directory: string;\n title: string | null;\n /** Epoch milliseconds. */\n timeCreated: number;\n /** Epoch milliseconds. */\n lastActivityAt: number;\n}\n\n/** A live `session_locks/<slug>.lock` entry: the session and its holder PID. */\nexport interface DevinSessionLock {\n sessionId: string;\n pid: number;\n}\n\ninterface DevinSessionRow {\n id: string;\n working_directory: string;\n title: string | null;\n created_at: number;\n last_activity_at: number;\n}\n\nfunction toDevinSession(row: DevinSessionRow): DevinSession {\n return {\n sessionId: row.id,\n directory: row.working_directory,\n title: row.title,\n timeCreated: row.created_at * 1000,\n lastActivityAt: row.last_activity_at * 1000,\n };\n}\n\nexport class DevinSessionLocator {\n private readonly dbPath: string;\n private readonly locksDir: string;\n private db: Database.Database | null = null;\n\n constructor(\n dbPath = DevinSessionLocator.resolveDbPath(),\n locksDir = DevinSessionLocator.defaultLocksDir(dbPath),\n private readonly parser: DevinSessionParser = new DevinSessionParser(),\n ) {\n this.dbPath = dbPath;\n this.locksDir = locksDir;\n }\n\n static resolveDbPath(): string {\n const xdg = process.env.XDG_DATA_HOME;\n const base = xdg || path.join(homeDir(), \".local\", \"share\");\n return path.join(base, \"devin\", \"cli\", \"sessions.db\");\n }\n\n static defaultLocksDir(dbPath: string): string {\n return path.join(path.dirname(dbPath), \"session_locks\");\n }\n\n openDb(): Database.Database | null {\n if (this.db) return this.db;\n if (!fs.existsSync(this.dbPath)) return null;\n\n try {\n this.db = new Database(this.dbPath, { readonly: true });\n return this.db;\n } catch {\n return null;\n }\n }\n\n close(): void {\n if (this.db) {\n try {\n this.db.close();\n } catch {\n /* ignore */\n }\n this.db = null;\n }\n }\n\n /**\n * Read `session_locks/*.lock` entries. Each file name is a session slug and\n * its body is the PID of the `devin acp` backend holding the session.\n * Malformed files are skipped; callers validate liveness against the\n * process snapshot.\n */\n listActiveLocks(): DevinSessionLock[] {\n let entries: string[];\n try {\n entries = fs.readdirSync(this.locksDir);\n } catch {\n return [];\n }\n\n const locks: DevinSessionLock[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(LOCK_FILE_SUFFIX)) continue;\n const sessionId = entry.slice(0, -LOCK_FILE_SUFFIX.length);\n if (!sessionId) continue;\n\n try {\n const raw = fs.readFileSync(path.join(this.locksDir, entry), \"utf-8\").trim();\n const pid = parseInt(raw, 10);\n if (Number.isFinite(pid) && pid > 0) locks.push({ sessionId, pid });\n } catch {\n /* skip unreadable lock */\n }\n }\n return locks;\n }\n\n findSessionById(db: Database.Database, sessionId: string): DevinSession | null {\n try {\n const row = db\n .prepare<[string], DevinSessionRow>(`\n SELECT id, working_directory, title, created_at, last_activity_at\n FROM sessions\n WHERE id = ? AND hidden = 0\n `)\n .get(sessionId);\n return row ? toDevinSession(row) : null;\n } catch {\n return null;\n }\n }\n\n findSessionForDirectory(db: Database.Database, directory: string): DevinSession | null {\n try {\n const row = db\n .prepare<[string], DevinSessionRow>(`\n SELECT id, working_directory, title, created_at, last_activity_at\n FROM sessions\n WHERE working_directory = ? AND hidden = 0\n ORDER BY last_activity_at DESC\n LIMIT 1\n `)\n .get(directory);\n return row ? toDevinSession(row) : null;\n } catch {\n return null;\n }\n }\n\n listSessions(opts?: ListSessionsOptions): SessionSummary[] {\n const db = this.openDb();\n if (!db) return [];\n\n try {\n const rows = db\n .prepare<[], DevinSessionRow>(`\n SELECT id, working_directory, title, created_at, last_activity_at\n FROM sessions\n WHERE hidden = 0\n ORDER BY last_activity_at DESC\n `)\n .all();\n\n const summaries: SessionSummary[] = [];\n for (const row of rows) {\n if (opts?.cwd !== undefined && row.working_directory !== opts.cwd) continue;\n summaries.push(this.toSessionSummary(db, row));\n }\n return summaries;\n } catch {\n this.close();\n return [];\n }\n }\n\n findSessionsById(sessionId: string): SessionSummary[] {\n const db = this.openDb();\n if (!db) return [];\n\n try {\n const row = db\n .prepare<[string], DevinSessionRow>(`\n SELECT id, working_directory, title, created_at, last_activity_at\n FROM sessions\n WHERE id = ? AND hidden = 0\n `)\n .get(sessionId);\n return row ? [this.toSessionSummary(db, row)] : [];\n } catch {\n this.close();\n return [];\n }\n }\n\n private toSessionSummary(db: Database.Database, row: DevinSessionRow): SessionSummary {\n const session = toDevinSession(row);\n return {\n type: \"devin\",\n sessionId: session.sessionId,\n cwd: session.directory,\n firstUserMessage: this.parser.getFirstUserMessage(db, session.sessionId),\n lastActive: new Date(session.lastActivityAt),\n startedAt: new Date(session.timeCreated),\n sessionFilePath: encodeDevinSessionRef(this.dbPath, session.sessionId),\n };\n }\n\n get dbFilePath(): string {\n return this.dbPath;\n }\n\n get sessionLocksDir(): string {\n return this.locksDir;\n }\n}\n"],"names":["fs","path","Database","DevinSessionParser","homeDir","SESSION_REF_SEP","LOCK_FILE_SUFFIX","encodeDevinSessionRef","dbPath","sessionId","decodeDevinSessionRef","ref","idx","lastIndexOf","slice","length","toDevinSession","row","id","directory","working_directory","title","timeCreated","created_at","lastActivityAt","last_activity_at","DevinSessionLocator","locksDir","db","resolveDbPath","defaultLocksDir","parser","xdg","process","env","XDG_DATA_HOME","base","join","dirname","openDb","existsSync","readonly","close","listActiveLocks","entries","readdirSync","locks","entry","endsWith","raw","readFileSync","trim","pid","parseInt","Number","isFinite","push","findSessionById","prepare","get","findSessionForDirectory","listSessions","opts","rows","all","summaries","cwd","undefined","toSessionSummary","findSessionsById","session","type","firstUserMessage","getFirstUserMessage","lastActive","Date","startedAt","sessionFilePath","dbFilePath","sessionLocksDir"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { ConversationMessage, ConversationOptions } from "../../adapters/AgentAdapter.js";
2
+ import type Database from "better-sqlite3";
3
+ export interface DevinSessionStats {
4
+ lastRole: string | null;
5
+ /** Epoch milliseconds of the newest message node. */
6
+ lastTimeUpdated: number;
7
+ summary: string;
8
+ }
9
+ export declare class DevinSessionParser {
10
+ getConversation(db: Database.Database, sessionId: string, options?: ConversationOptions): ConversationMessage[];
11
+ getSessionStats(db: Database.Database, sessionId: string): DevinSessionStats;
12
+ /**
13
+ * Most recent typed prompt for the "working on" summary, matching the
14
+ * last-user-message convention of the other adapters.
15
+ */
16
+ getLastUserPrompt(db: Database.Database, sessionId: string): string;
17
+ /**
18
+ * First typed prompt for session-list identity (`firstUserMessage`).
19
+ */
20
+ getFirstUserMessage(db: Database.Database, sessionId: string): string;
21
+ /**
22
+ * Typed prompt in the given direction. `prompt_history` is authoritative;
23
+ * shell `!` commands and `/` slash commands are skipped. Falls back to a
24
+ * bounded `is_user_input` node scan in the same direction.
25
+ */
26
+ private userPrompt;
27
+ private parseChatMessage;
28
+ }
29
+ //# sourceMappingURL=DevinSessionParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevinSessionParser.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/DevinSessionParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG/F,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AA2BD,qBAAa,kBAAkB;IAC7B,eAAe,CACb,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,mBAAmB,EAAE;IAyFxB,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB;IA8B5E;;;OAGG;IACH,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAInE;;OAEG;IACH,mBAAmB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAIrE;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA+ClB,OAAO,CAAC,gBAAgB;CAOzB"}
@@ -0,0 +1,166 @@
1
+ import { sliceTail } from "../../utils/jsonlTail.js";
2
+ import { SUMMARY_MAX_LENGTH, truncate } from "../shared.js";
3
+ const EMPTY_STATS = {
4
+ lastRole: null,
5
+ lastTimeUpdated: 0,
6
+ summary: ""
7
+ };
8
+ /** Rows fetched per requested tail message; revisions/tools eat into the window. */ const TAIL_OVERFETCH = 8;
9
+ const TOOL_CONTENT_MAX = 500;
10
+ export class DevinSessionParser {
11
+ getConversation(db, sessionId, options) {
12
+ const verbose = options?.verbose ?? false;
13
+ const tail = options?.tail && Number.isInteger(options.tail) && options.tail > 0 ? options.tail : undefined;
14
+ try {
15
+ // LIMIT -1 = unbounded in SQLite; one query serves the full and tail paths.
16
+ const limit = tail ? tail * TAIL_OVERFETCH : -1;
17
+ const rows = db.prepare(`
18
+ SELECT node_id, chat_message
19
+ FROM message_nodes
20
+ WHERE session_id = ?
21
+ ORDER BY node_id DESC
22
+ LIMIT ?
23
+ `).all(sessionId, limit).reverse();
24
+ const messages = [];
25
+ const latestNodeByMessageId = new Map();
26
+ const parsed = [];
27
+ for (const row of rows){
28
+ const message = this.parseChatMessage(row.chat_message);
29
+ if (!message) continue;
30
+ parsed.push({
31
+ nodeId: row.node_id,
32
+ message
33
+ });
34
+ if (message.message_id) {
35
+ latestNodeByMessageId.set(message.message_id, Math.max(latestNodeByMessageId.get(message.message_id) ?? 0, row.node_id));
36
+ }
37
+ }
38
+ for (const { nodeId, message } of parsed){
39
+ if (message.message_id && latestNodeByMessageId.get(message.message_id) !== nodeId) {
40
+ continue;
41
+ }
42
+ const role = message.role;
43
+ if (role === "system") continue;
44
+ const content = typeof message.content === "string" ? message.content : "";
45
+ if (role === "user") {
46
+ if (message.metadata?.is_user_input === false || !content.trim()) continue;
47
+ messages.push({
48
+ role: "user",
49
+ content
50
+ });
51
+ continue;
52
+ }
53
+ if (role === "assistant") {
54
+ const thinking = typeof message.thinking === "string" ? message.thinking : message.thinking?.thinking;
55
+ if (verbose && thinking) {
56
+ messages.push({
57
+ role: "assistant",
58
+ content: `[thinking] ${thinking}`
59
+ });
60
+ }
61
+ if (content.trim()) messages.push({
62
+ role: "assistant",
63
+ content
64
+ });
65
+ if (verbose) {
66
+ for (const call of message.tool_calls ?? []){
67
+ const name = call.function?.name ?? call.name ?? "tool";
68
+ messages.push({
69
+ role: "assistant",
70
+ content: `[tool: ${name}]`
71
+ });
72
+ }
73
+ }
74
+ continue;
75
+ }
76
+ if (role === "tool" && verbose) {
77
+ const label = message.name ? `[tool: ${message.name}]` : "[tool]";
78
+ messages.push({
79
+ role: "assistant",
80
+ content: `${label} ${truncate(content.trim(), TOOL_CONTENT_MAX)}`.trim()
81
+ });
82
+ }
83
+ }
84
+ return sliceTail(messages, tail);
85
+ } catch {
86
+ return [];
87
+ }
88
+ }
89
+ getSessionStats(db, sessionId) {
90
+ try {
91
+ const frontier = db.prepare(`
92
+ SELECT chat_message
93
+ FROM message_nodes
94
+ WHERE session_id = ?
95
+ ORDER BY node_id DESC
96
+ LIMIT 1
97
+ `).get(sessionId);
98
+ const heartbeat = db.prepare(`
99
+ SELECT MAX(created_at) AS maxCreated
100
+ FROM message_nodes
101
+ WHERE session_id = ?
102
+ `).get(sessionId);
103
+ return {
104
+ lastRole: this.parseChatMessage(frontier?.chat_message ?? "")?.role ?? null,
105
+ lastTimeUpdated: (heartbeat?.maxCreated ?? 0) * 1000,
106
+ summary: this.getLastUserPrompt(db, sessionId)
107
+ };
108
+ } catch {
109
+ return EMPTY_STATS;
110
+ }
111
+ }
112
+ /**
113
+ * Most recent typed prompt for the "working on" summary, matching the
114
+ * last-user-message convention of the other adapters.
115
+ */ getLastUserPrompt(db, sessionId) {
116
+ return this.userPrompt(db, sessionId, "DESC");
117
+ }
118
+ /**
119
+ * First typed prompt for session-list identity (`firstUserMessage`).
120
+ */ getFirstUserMessage(db, sessionId) {
121
+ return this.userPrompt(db, sessionId, "ASC");
122
+ }
123
+ /**
124
+ * Typed prompt in the given direction. `prompt_history` is authoritative;
125
+ * shell `!` commands and `/` slash commands are skipped. Falls back to a
126
+ * bounded `is_user_input` node scan in the same direction.
127
+ */ userPrompt(db, sessionId, direction) {
128
+ try {
129
+ const prompt = db.prepare(`
130
+ SELECT content
131
+ FROM prompt_history
132
+ WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%'
133
+ ORDER BY timestamp ${direction}
134
+ LIMIT 1
135
+ `).get(sessionId);
136
+ if (prompt?.content?.trim()) return truncate(prompt.content.trim(), SUMMARY_MAX_LENGTH);
137
+ } catch {
138
+ /* fall through to message_nodes */ }
139
+ try {
140
+ const rows = db.prepare(`
141
+ SELECT chat_message
142
+ FROM message_nodes
143
+ WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user'
144
+ ORDER BY node_id ${direction}
145
+ LIMIT 8
146
+ `).all(sessionId);
147
+ for (const row of rows){
148
+ const message = this.parseChatMessage(row.chat_message);
149
+ if (message?.role === "user" && message.metadata?.is_user_input !== false && typeof message.content === "string" && message.content.trim()) {
150
+ return truncate(message.content.trim(), SUMMARY_MAX_LENGTH);
151
+ }
152
+ }
153
+ } catch {
154
+ /* ignore */ }
155
+ return "";
156
+ }
157
+ parseChatMessage(raw) {
158
+ try {
159
+ return JSON.parse(raw);
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+ }
165
+
166
+ //# sourceMappingURL=DevinSessionParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/devin/DevinSessionParser.ts"],"sourcesContent":["import type { ConversationMessage, ConversationOptions } from \"../../adapters/AgentAdapter.js\";\nimport { sliceTail } from \"../../utils/jsonlTail.js\";\nimport { SUMMARY_MAX_LENGTH, truncate } from \"../shared.js\";\nimport type Database from \"better-sqlite3\";\n\nexport interface DevinSessionStats {\n lastRole: string | null;\n /** Epoch milliseconds of the newest message node. */\n lastTimeUpdated: number;\n summary: string;\n}\n\ninterface DevinChatMessage {\n message_id?: string;\n role?: string;\n content?: unknown;\n tool_calls?: { name?: string; function?: { name?: string } }[];\n thinking?: { thinking?: string } | string;\n name?: string;\n metadata?: { is_user_input?: boolean };\n}\n\ninterface MessageNodeRow {\n node_id: number;\n chat_message: string;\n}\n\nconst EMPTY_STATS: DevinSessionStats = {\n lastRole: null,\n lastTimeUpdated: 0,\n summary: \"\",\n};\n\n/** Rows fetched per requested tail message; revisions/tools eat into the window. */\nconst TAIL_OVERFETCH = 8;\nconst TOOL_CONTENT_MAX = 500;\n\nexport class DevinSessionParser {\n getConversation(\n db: Database.Database,\n sessionId: string,\n options?: ConversationOptions,\n ): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n const tail =\n options?.tail && Number.isInteger(options.tail) && options.tail > 0\n ? options.tail\n : undefined;\n\n try {\n // LIMIT -1 = unbounded in SQLite; one query serves the full and tail paths.\n const limit = tail ? tail * TAIL_OVERFETCH : -1;\n const rows = db\n .prepare<[string, number], MessageNodeRow>(`\n SELECT node_id, chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT ?\n `)\n .all(sessionId, limit)\n .reverse();\n\n const messages: ConversationMessage[] = [];\n const latestNodeByMessageId = new Map<string, number>();\n const parsed: { nodeId: number; message: DevinChatMessage }[] = [];\n\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (!message) continue;\n parsed.push({ nodeId: row.node_id, message });\n if (message.message_id) {\n latestNodeByMessageId.set(\n message.message_id,\n Math.max(latestNodeByMessageId.get(message.message_id) ?? 0, row.node_id),\n );\n }\n }\n\n for (const { nodeId, message } of parsed) {\n if (\n message.message_id &&\n latestNodeByMessageId.get(message.message_id) !== nodeId\n ) {\n continue;\n }\n\n const role = message.role;\n if (role === \"system\") continue;\n\n const content = typeof message.content === \"string\" ? message.content : \"\";\n\n if (role === \"user\") {\n if (message.metadata?.is_user_input === false || !content.trim()) continue;\n messages.push({ role: \"user\", content });\n continue;\n }\n\n if (role === \"assistant\") {\n const thinking =\n typeof message.thinking === \"string\"\n ? message.thinking\n : message.thinking?.thinking;\n if (verbose && thinking) {\n messages.push({ role: \"assistant\", content: `[thinking] ${thinking}` });\n }\n if (content.trim()) messages.push({ role: \"assistant\", content });\n if (verbose) {\n for (const call of message.tool_calls ?? []) {\n const name = call.function?.name ?? call.name ?? \"tool\";\n messages.push({ role: \"assistant\", content: `[tool: ${name}]` });\n }\n }\n continue;\n }\n\n if (role === \"tool\" && verbose) {\n const label = message.name ? `[tool: ${message.name}]` : \"[tool]\";\n messages.push({\n role: \"assistant\",\n content: `${label} ${truncate(content.trim(), TOOL_CONTENT_MAX)}`.trim(),\n });\n }\n }\n\n return sliceTail(messages, tail);\n } catch {\n return [];\n }\n }\n\n getSessionStats(db: Database.Database, sessionId: string): DevinSessionStats {\n try {\n const frontier = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ?\n ORDER BY node_id DESC\n LIMIT 1\n `)\n .get(sessionId);\n\n const heartbeat = db\n .prepare<[string], { maxCreated: number | null }>(`\n SELECT MAX(created_at) AS maxCreated\n FROM message_nodes\n WHERE session_id = ?\n `)\n .get(sessionId);\n\n return {\n lastRole: this.parseChatMessage(frontier?.chat_message ?? \"\")?.role ?? null,\n lastTimeUpdated: (heartbeat?.maxCreated ?? 0) * 1000,\n summary: this.getLastUserPrompt(db, sessionId),\n };\n } catch {\n return EMPTY_STATS;\n }\n }\n\n /**\n * Most recent typed prompt for the \"working on\" summary, matching the\n * last-user-message convention of the other adapters.\n */\n getLastUserPrompt(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"DESC\");\n }\n\n /**\n * First typed prompt for session-list identity (`firstUserMessage`).\n */\n getFirstUserMessage(db: Database.Database, sessionId: string): string {\n return this.userPrompt(db, sessionId, \"ASC\");\n }\n\n /**\n * Typed prompt in the given direction. `prompt_history` is authoritative;\n * shell `!` commands and `/` slash commands are skipped. Falls back to a\n * bounded `is_user_input` node scan in the same direction.\n */\n private userPrompt(\n db: Database.Database,\n sessionId: string,\n direction: \"ASC\" | \"DESC\",\n ): string {\n try {\n const prompt = db\n .prepare<[string], { content: string }>(`\n SELECT content\n FROM prompt_history\n WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%'\n ORDER BY timestamp ${direction}\n LIMIT 1\n `)\n .get(sessionId);\n if (prompt?.content?.trim()) return truncate(prompt.content.trim(), SUMMARY_MAX_LENGTH);\n } catch {\n /* fall through to message_nodes */\n }\n\n try {\n const rows = db\n .prepare<[string], { chat_message: string }>(`\n SELECT chat_message\n FROM message_nodes\n WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user'\n ORDER BY node_id ${direction}\n LIMIT 8\n `)\n .all(sessionId);\n for (const row of rows) {\n const message = this.parseChatMessage(row.chat_message);\n if (\n message?.role === \"user\" &&\n message.metadata?.is_user_input !== false &&\n typeof message.content === \"string\" &&\n message.content.trim()\n ) {\n return truncate(message.content.trim(), SUMMARY_MAX_LENGTH);\n }\n }\n } catch {\n /* ignore */\n }\n return \"\";\n }\n\n private parseChatMessage(raw: string): DevinChatMessage | null {\n try {\n return JSON.parse(raw) as DevinChatMessage;\n } catch {\n return null;\n }\n }\n}\n"],"names":["sliceTail","SUMMARY_MAX_LENGTH","truncate","EMPTY_STATS","lastRole","lastTimeUpdated","summary","TAIL_OVERFETCH","TOOL_CONTENT_MAX","DevinSessionParser","getConversation","db","sessionId","options","verbose","tail","Number","isInteger","undefined","limit","rows","prepare","all","reverse","messages","latestNodeByMessageId","Map","parsed","row","message","parseChatMessage","chat_message","push","nodeId","node_id","message_id","set","Math","max","get","role","content","metadata","is_user_input","trim","thinking","call","tool_calls","name","function","label","getSessionStats","frontier","heartbeat","maxCreated","getLastUserPrompt","userPrompt","getFirstUserMessage","direction","prompt","raw","JSON","parse"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type DevinCredential = {
2
+ key: string;
3
+ orgId: string;
4
+ };
5
+ export type DevinCredentialOptions = {
6
+ env?: NodeJS.ProcessEnv;
7
+ readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
8
+ };
9
+ export declare function resolveDevinCredential(options?: DevinCredentialOptions): Promise<DevinCredential>;
10
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/credentials.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE,CAAC;AAkEF,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,CAK1B"}
@@ -0,0 +1,66 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const DEVIN_KEY_MISSING = "Devin API key not found; set DEVIN_API_KEY or log in with the Devin CLI";
5
+ const DEVIN_ORG_MISSING = "Devin organization id not found; set DEVIN_ORG_ID or log in with the Devin CLI";
6
+ function record(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
8
+ }
9
+ function nonEmptyText(value) {
10
+ return typeof value === "string" && value.trim() ? value.trim() : null;
11
+ }
12
+ async function readText(path, options) {
13
+ try {
14
+ return await (options.readFile ?? readFile)(path, "utf8");
15
+ } catch (error) {
16
+ if (record(error)?.code === "ENOENT") return null;
17
+ throw error;
18
+ }
19
+ }
20
+ function parseFlatToml(contents) {
21
+ const entries = {};
22
+ for (const line of contents.split("\n")){
23
+ const trimmed = line.trim();
24
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("[")) continue;
25
+ const match = /^([A-Za-z0-9_]+)\s*=\s*"(.*)"\s*$/.exec(trimmed);
26
+ if (!match) return null;
27
+ entries[match[1]] = match[2];
28
+ }
29
+ return entries;
30
+ }
31
+ async function resolveKey(home, options) {
32
+ const envKey = nonEmptyText(options.env?.DEVIN_API_KEY ?? process.env.DEVIN_API_KEY);
33
+ if (envKey) return envKey;
34
+ const contents = await readText(join(home, ".local", "share", "devin", "credentials.toml"), options);
35
+ if (contents === null) throw new Error(DEVIN_KEY_MISSING);
36
+ const parsed = parseFlatToml(contents);
37
+ if (parsed === null) throw new Error("Devin credentials file is malformed");
38
+ const key = nonEmptyText(parsed.windsurf_api_key);
39
+ if (!key) throw new Error(DEVIN_KEY_MISSING);
40
+ return key;
41
+ }
42
+ async function resolveOrgId(home, options) {
43
+ const envOrg = nonEmptyText(options.env?.DEVIN_ORG_ID ?? process.env.DEVIN_ORG_ID);
44
+ if (envOrg) return envOrg;
45
+ const contents = await readText(join(home, ".config", "devin", "config.json"), options);
46
+ if (contents === null) throw new Error(DEVIN_ORG_MISSING);
47
+ const parsed = record(JSON.parse(contents));
48
+ if (!parsed) throw new Error("Devin config file is malformed");
49
+ const orgId = nonEmptyText(record(parsed.devin)?.org_id);
50
+ if (!orgId) throw new Error(DEVIN_ORG_MISSING);
51
+ return orgId;
52
+ }
53
+ export async function resolveDevinCredential(options = {}) {
54
+ const env = options.env ?? process.env;
55
+ const home = env.HOME || homedir();
56
+ const [key, orgId] = await Promise.all([
57
+ resolveKey(home, options),
58
+ resolveOrgId(home, options)
59
+ ]);
60
+ return {
61
+ key,
62
+ orgId
63
+ };
64
+ }
65
+
66
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/devin/credentials.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type DevinCredential = {\n key: string;\n orgId: string;\n};\n\nexport type DevinCredentialOptions = {\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n};\n\nconst DEVIN_KEY_MISSING = \"Devin API key not found; set DEVIN_API_KEY or log in with the Devin CLI\";\nconst DEVIN_ORG_MISSING =\n \"Devin organization id not found; set DEVIN_ORG_ID or log in with the Devin CLI\";\n\nfunction record(value: unknown): UnknownRecord | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as UnknownRecord)\n : null;\n}\n\nfunction nonEmptyText(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nasync function readText(path: string, options: DevinCredentialOptions): Promise<string | null> {\n try {\n return await (options.readFile ?? readFile)(path, \"utf8\");\n } catch (error) {\n if (record(error)?.code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nfunction parseFlatToml(contents: string): UnknownRecord | null {\n const entries: UnknownRecord = {};\n for (const line of contents.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\") || trimmed.startsWith(\"[\")) continue;\n const match = /^([A-Za-z0-9_]+)\\s*=\\s*\"(.*)\"\\s*$/.exec(trimmed);\n if (!match) return null;\n entries[match[1]] = match[2];\n }\n return entries;\n}\n\nasync function resolveKey(home: string, options: DevinCredentialOptions): Promise<string> {\n const envKey = nonEmptyText(options.env?.DEVIN_API_KEY ?? process.env.DEVIN_API_KEY);\n if (envKey) return envKey;\n\n const contents = await readText(\n join(home, \".local\", \"share\", \"devin\", \"credentials.toml\"),\n options,\n );\n if (contents === null) throw new Error(DEVIN_KEY_MISSING);\n const parsed = parseFlatToml(contents);\n if (parsed === null) throw new Error(\"Devin credentials file is malformed\");\n const key = nonEmptyText(parsed.windsurf_api_key);\n if (!key) throw new Error(DEVIN_KEY_MISSING);\n return key;\n}\n\nasync function resolveOrgId(home: string, options: DevinCredentialOptions): Promise<string> {\n const envOrg = nonEmptyText(options.env?.DEVIN_ORG_ID ?? process.env.DEVIN_ORG_ID);\n if (envOrg) return envOrg;\n\n const contents = await readText(join(home, \".config\", \"devin\", \"config.json\"), options);\n if (contents === null) throw new Error(DEVIN_ORG_MISSING);\n const parsed = record(JSON.parse(contents));\n if (!parsed) throw new Error(\"Devin config file is malformed\");\n const orgId = nonEmptyText(record(parsed.devin)?.org_id);\n if (!orgId) throw new Error(DEVIN_ORG_MISSING);\n return orgId;\n}\n\nexport async function resolveDevinCredential(\n options: DevinCredentialOptions = {},\n): Promise<DevinCredential> {\n const env = options.env ?? process.env;\n const home = env.HOME || homedir();\n const [key, orgId] = await Promise.all([resolveKey(home, options), resolveOrgId(home, options)]);\n return { key, orgId };\n}\n"],"names":["readFile","homedir","join","DEVIN_KEY_MISSING","DEVIN_ORG_MISSING","record","value","Array","isArray","nonEmptyText","trim","readText","path","options","error","code","parseFlatToml","contents","entries","line","split","trimmed","startsWith","match","exec","resolveKey","home","envKey","env","DEVIN_API_KEY","process","Error","parsed","key","windsurf_api_key","resolveOrgId","envOrg","DEVIN_ORG_ID","JSON","parse","orgId","devin","org_id","resolveDevinCredential","HOME","Promise","all"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,mBAAmB;AAC5C,SAASC,OAAO,QAAQ,UAAU;AAClC,SAASC,IAAI,QAAQ,YAAY;AAcjC,MAAMC,oBAAoB;AAC1B,MAAMC,oBACJ;AAEF,SAASC,OAAOC,KAAc;IAC5B,OAAOA,UAAU,QAAQ,OAAOA,UAAU,YAAY,CAACC,MAAMC,OAAO,CAACF,SAChEA,QACD;AACN;AAEA,SAASG,aAAaH,KAAc;IAClC,OAAO,OAAOA,UAAU,YAAYA,MAAMI,IAAI,KAAKJ,MAAMI,IAAI,KAAK;AACpE;AAEA,eAAeC,SAASC,IAAY,EAAEC,OAA+B;IACnE,IAAI;QACF,OAAO,MAAM,AAACA,CAAAA,QAAQb,QAAQ,IAAIA,QAAO,EAAGY,MAAM;IACpD,EAAE,OAAOE,OAAO;QACd,IAAIT,OAAOS,QAAQC,SAAS,UAAU,OAAO;QAC7C,MAAMD;IACR;AACF;AAEA,SAASE,cAAcC,QAAgB;IACrC,MAAMC,UAAyB,CAAC;IAChC,KAAK,MAAMC,QAAQF,SAASG,KAAK,CAAC,MAAO;QACvC,MAAMC,UAAUF,KAAKT,IAAI;QACzB,IAAI,CAACW,WAAWA,QAAQC,UAAU,CAAC,QAAQD,QAAQC,UAAU,CAAC,MAAM;QACpE,MAAMC,QAAQ,oCAAoCC,IAAI,CAACH;QACvD,IAAI,CAACE,OAAO,OAAO;QACnBL,OAAO,CAACK,KAAK,CAAC,EAAE,CAAC,GAAGA,KAAK,CAAC,EAAE;IAC9B;IACA,OAAOL;AACT;AAEA,eAAeO,WAAWC,IAAY,EAAEb,OAA+B;IACrE,MAAMc,SAASlB,aAAaI,QAAQe,GAAG,EAAEC,iBAAiBC,QAAQF,GAAG,CAACC,aAAa;IACnF,IAAIF,QAAQ,OAAOA;IAEnB,MAAMV,WAAW,MAAMN,SACrBT,KAAKwB,MAAM,UAAU,SAAS,SAAS,qBACvCb;IAEF,IAAII,aAAa,MAAM,MAAM,IAAIc,MAAM5B;IACvC,MAAM6B,SAAShB,cAAcC;IAC7B,IAAIe,WAAW,MAAM,MAAM,IAAID,MAAM;IACrC,MAAME,MAAMxB,aAAauB,OAAOE,gBAAgB;IAChD,IAAI,CAACD,KAAK,MAAM,IAAIF,MAAM5B;IAC1B,OAAO8B;AACT;AAEA,eAAeE,aAAaT,IAAY,EAAEb,OAA+B;IACvE,MAAMuB,SAAS3B,aAAaI,QAAQe,GAAG,EAAES,gBAAgBP,QAAQF,GAAG,CAACS,YAAY;IACjF,IAAID,QAAQ,OAAOA;IAEnB,MAAMnB,WAAW,MAAMN,SAAST,KAAKwB,MAAM,WAAW,SAAS,gBAAgBb;IAC/E,IAAII,aAAa,MAAM,MAAM,IAAIc,MAAM3B;IACvC,MAAM4B,SAAS3B,OAAOiC,KAAKC,KAAK,CAACtB;IACjC,IAAI,CAACe,QAAQ,MAAM,IAAID,MAAM;IAC7B,MAAMS,QAAQ/B,aAAaJ,OAAO2B,OAAOS,KAAK,GAAGC;IACjD,IAAI,CAACF,OAAO,MAAM,IAAIT,MAAM3B;IAC5B,OAAOoC;AACT;AAEA,OAAO,eAAeG,uBACpB9B,UAAkC,CAAC,CAAC;IAEpC,MAAMe,MAAMf,QAAQe,GAAG,IAAIE,QAAQF,GAAG;IACtC,MAAMF,OAAOE,IAAIgB,IAAI,IAAI3C;IACzB,MAAM,CAACgC,KAAKO,MAAM,GAAG,MAAMK,QAAQC,GAAG,CAAC;QAACrB,WAAWC,MAAMb;QAAUsB,aAAaT,MAAMb;KAAS;IAC/F,OAAO;QAAEoB;QAAKO;IAAM;AACtB"}
@@ -0,0 +1,3 @@
1
+ import type { HarnessReadinessProfile } from "../readiness/types.js";
2
+ export declare const devinReadiness: HarnessReadinessProfile;
3
+ //# sourceMappingURL=readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EAExB,MAAM,uBAAuB,CAAC;AA4B/B,eAAO,MAAM,cAAc,EAAE,uBAG5B,CAAC"}
@@ -0,0 +1,39 @@
1
+ const LOGGED_IN_PATTERN = /^Logged in/m;
2
+ async function authCheck(runtime) {
3
+ try {
4
+ const result = await runtime.runCommand("devin", [
5
+ "auth",
6
+ "status"
7
+ ]);
8
+ const authenticated = LOGGED_IN_PATTERN.test(result.stdout);
9
+ return {
10
+ state: authenticated ? "authenticated" : "unauthenticated",
11
+ source: "devin auth status",
12
+ provider: null,
13
+ availableProviders: authenticated ? [
14
+ "devin"
15
+ ] : [],
16
+ status: authenticated ? "pass" : "fail",
17
+ errors: authenticated ? [] : [
18
+ "Devin is not logged in"
19
+ ]
20
+ };
21
+ } catch {
22
+ return {
23
+ state: "unknown",
24
+ source: "devin auth status",
25
+ provider: null,
26
+ availableProviders: [],
27
+ status: "warn",
28
+ errors: [
29
+ "Devin authentication probe failed"
30
+ ]
31
+ };
32
+ }
33
+ }
34
+ export const devinReadiness = {
35
+ configDir: ".config/devin",
36
+ auth: authCheck
37
+ };
38
+
39
+ //# sourceMappingURL=readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/devin/readiness.ts"],"sourcesContent":["import type {\n AuthReadinessCheck,\n HarnessReadinessProfile,\n ReadinessRuntime,\n} from \"../readiness/types.js\";\n\nconst LOGGED_IN_PATTERN = /^Logged in/m;\n\nasync function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {\n try {\n const result = await runtime.runCommand(\"devin\", [\"auth\", \"status\"]);\n const authenticated = LOGGED_IN_PATTERN.test(result.stdout);\n return {\n state: authenticated ? \"authenticated\" : \"unauthenticated\",\n source: \"devin auth status\",\n provider: null,\n availableProviders: authenticated ? [\"devin\"] : [],\n status: authenticated ? \"pass\" : \"fail\",\n errors: authenticated ? [] : [\"Devin is not logged in\"],\n };\n } catch {\n return {\n state: \"unknown\",\n source: \"devin auth status\",\n provider: null,\n availableProviders: [],\n status: \"warn\",\n errors: [\"Devin authentication probe failed\"],\n };\n }\n}\n\nexport const devinReadiness: HarnessReadinessProfile = {\n configDir: \".config/devin\",\n auth: authCheck,\n};\n"],"names":["LOGGED_IN_PATTERN","authCheck","runtime","result","runCommand","authenticated","test","stdout","state","source","provider","availableProviders","status","errors","devinReadiness","configDir","auth"],"mappings":"AAMA,MAAMA,oBAAoB;AAE1B,eAAeC,UAAUC,OAAyB;IAChD,IAAI;QACF,MAAMC,SAAS,MAAMD,QAAQE,UAAU,CAAC,SAAS;YAAC;YAAQ;SAAS;QACnE,MAAMC,gBAAgBL,kBAAkBM,IAAI,CAACH,OAAOI,MAAM;QAC1D,OAAO;YACLC,OAAOH,gBAAgB,kBAAkB;YACzCI,QAAQ;YACRC,UAAU;YACVC,oBAAoBN,gBAAgB;gBAAC;aAAQ,GAAG,EAAE;YAClDO,QAAQP,gBAAgB,SAAS;YACjCQ,QAAQR,gBAAgB,EAAE,GAAG;gBAAC;aAAyB;QACzD;IACF,EAAE,OAAM;QACN,OAAO;YACLG,OAAO;YACPC,QAAQ;YACRC,UAAU;YACVC,oBAAoB,EAAE;YACtBC,QAAQ;YACRC,QAAQ;gBAAC;aAAoC;QAC/C;IACF;AACF;AAEA,OAAO,MAAMC,iBAA0C;IACrDC,WAAW;IACXC,MAAMf;AACR,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAWhE;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,YAAY,EAAE,CAYtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harnesses/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAYhE;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,YAAY,EAAE,CAatD"}
@@ -7,6 +7,7 @@ import { GrokCliAdapter } from "./grok/GrokCliAdapter.js";
7
7
  import { KiroAdapter } from "./kiro/KiroAdapter.js";
8
8
  import { OpenCodeAdapter } from "./opencode/OpenCodeAdapter.js";
9
9
  import { PiAdapter } from "./pi/PiAdapter.js";
10
+ import { DevinAdapter } from "./devin/DevinAdapter.js";
10
11
  /**
11
12
  * One instance of every built-in harness adapter, in registration order
12
13
  * (which is also the order agents are listed in). Register a new harness here.
@@ -20,7 +21,8 @@ import { PiAdapter } from "./pi/PiAdapter.js";
20
21
  new KiroAdapter(),
21
22
  new AntigravityCliAdapter(),
22
23
  new OpenCodeAdapter(),
23
- new PiAdapter()
24
+ new PiAdapter(),
25
+ new DevinAdapter()
24
26
  ];
25
27
  }
26
28
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/harnesses/index.ts"],"sourcesContent":["import type { AgentAdapter } from \"../adapters/AgentAdapter.js\";\nimport { AntigravityCliAdapter } from \"./antigravity/AntigravityCliAdapter.js\";\nimport { ClaudeCodeAdapter } from \"./claude/ClaudeCodeAdapter.js\";\nimport { CodexAdapter } from \"./codex/CodexAdapter.js\";\nimport { CopilotAdapter } from \"./copilot/CopilotAdapter.js\";\nimport { GeminiCliAdapter } from \"./gemini/GeminiCliAdapter.js\";\nimport { GrokCliAdapter } from \"./grok/GrokCliAdapter.js\";\nimport { KiroAdapter } from \"./kiro/KiroAdapter.js\";\nimport { OpenCodeAdapter } from \"./opencode/OpenCodeAdapter.js\";\nimport { PiAdapter } from \"./pi/PiAdapter.js\";\n\n/**\n * One instance of every built-in harness adapter, in registration order\n * (which is also the order agents are listed in). Register a new harness here.\n */\nexport function createBuiltinAdapters(): AgentAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new CodexAdapter(),\n new CopilotAdapter(),\n new GeminiCliAdapter(),\n new GrokCliAdapter(),\n new KiroAdapter(),\n new AntigravityCliAdapter(),\n new OpenCodeAdapter(),\n new PiAdapter(),\n ];\n}\n"],"names":["AntigravityCliAdapter","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","KiroAdapter","OpenCodeAdapter","PiAdapter","createBuiltinAdapters"],"mappings":"AACA,SAASA,qBAAqB,QAAQ,yCAAyC;AAC/E,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,cAAc,QAAQ,8BAA8B;AAC7D,SAASC,gBAAgB,QAAQ,+BAA+B;AAChE,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,oBAAoB;AAE9C;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACL,IAAIR;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIN;QACJ,IAAIO;QACJ,IAAIC;KACL;AACH"}
1
+ {"version":3,"sources":["../../src/harnesses/index.ts"],"sourcesContent":["import type { AgentAdapter } from \"../adapters/AgentAdapter.js\";\nimport { AntigravityCliAdapter } from \"./antigravity/AntigravityCliAdapter.js\";\nimport { ClaudeCodeAdapter } from \"./claude/ClaudeCodeAdapter.js\";\nimport { CodexAdapter } from \"./codex/CodexAdapter.js\";\nimport { CopilotAdapter } from \"./copilot/CopilotAdapter.js\";\nimport { GeminiCliAdapter } from \"./gemini/GeminiCliAdapter.js\";\nimport { GrokCliAdapter } from \"./grok/GrokCliAdapter.js\";\nimport { KiroAdapter } from \"./kiro/KiroAdapter.js\";\nimport { OpenCodeAdapter } from \"./opencode/OpenCodeAdapter.js\";\nimport { PiAdapter } from \"./pi/PiAdapter.js\";\nimport { DevinAdapter } from \"./devin/DevinAdapter.js\";\n\n/**\n * One instance of every built-in harness adapter, in registration order\n * (which is also the order agents are listed in). Register a new harness here.\n */\nexport function createBuiltinAdapters(): AgentAdapter[] {\n return [\n new ClaudeCodeAdapter(),\n new CodexAdapter(),\n new CopilotAdapter(),\n new GeminiCliAdapter(),\n new GrokCliAdapter(),\n new KiroAdapter(),\n new AntigravityCliAdapter(),\n new OpenCodeAdapter(),\n new PiAdapter(),\n new DevinAdapter(),\n ];\n}\n"],"names":["AntigravityCliAdapter","ClaudeCodeAdapter","CodexAdapter","CopilotAdapter","GeminiCliAdapter","GrokCliAdapter","KiroAdapter","OpenCodeAdapter","PiAdapter","DevinAdapter","createBuiltinAdapters"],"mappings":"AACA,SAASA,qBAAqB,QAAQ,yCAAyC;AAC/E,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,cAAc,QAAQ,8BAA8B;AAC7D,SAASC,gBAAgB,QAAQ,+BAA+B;AAChE,SAASC,cAAc,QAAQ,2BAA2B;AAC1D,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,eAAe,QAAQ,gCAAgC;AAChE,SAASC,SAAS,QAAQ,oBAAoB;AAC9C,SAASC,YAAY,QAAQ,0BAA0B;AAEvD;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACL,IAAIT;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIN;QACJ,IAAIO;QACJ,IAAIC;QACJ,IAAIC;KACL;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"AgentReadiness.d.ts","sourceRoot":"","sources":["../../../src/harnesses/readiness/AgentReadiness.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAEzB,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAC;AAcpB,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA8BD,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC,CAQ3D;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"AgentReadiness.d.ts","sourceRoot":"","sources":["../../../src/harnesses/readiness/AgentReadiness.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAEzB,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAC;AAepB,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA8BD,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC,CAQ3D;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC"}