lody 0.103.0 → 0.104.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -1
- package/dist/baguette-worker.js +480 -0
- package/dist/chunks/{acp-BzvviK-N.js → acp-C0LiROfB.js} +3 -3
- package/dist/chunks/{cloudflared-native-DbUToPHH.js → cloudflared-native-BWtpTE_5.js} +3 -3
- package/dist/chunks/{daemon-session-tools-DESvdAeY.js → daemon-session-tools-C6AE_3JK.js} +12 -9
- package/dist/chunks/{directory-handle-CtdCjbNh.js → directory-handle-DgcESjFj.js} +28 -22
- package/dist/chunks/{index-DEdPzgH7.js → index-Sv4ANSUY.js} +8893 -2560
- package/dist/chunks/ios-simulator-hTbW-tat.js +161 -0
- package/dist/chunks/{review-viewer-BZiXmQnJ.js → review-viewer-nQBrydLp.js} +13 -10
- package/dist/chunks/{schemas-WuyisFi9.js → schemas-D_XLKCXC.js} +8 -8
- package/dist/claude-acp.js +1 -1
- package/dist/cloudflared-worker.js +2 -2
- package/dist/codex-acp.js +1 -1
- package/dist/deepseek-acp.js +253 -128
- package/dist/devin-acp.js +1152 -0
- package/dist/file-index-scan-worker.js +2 -2
- package/dist/index.js +107512 -37004
- package/package.json +7 -5
- package/dist/chunks/index-DhSXbMsB.js +0 -742
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import "node:fs";
|
|
4
|
+
import { c as isLodySubagentOutput, S as SessionUsageAccumulator, e as supportsLodySubagentEvents, d as LODY_SUBAGENT_EVENT_METHOD, L as LODY_EXTENSION_METHODS } from "./chunks/subagent-events-BJ9JwZva.js";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
const FIELDS = [
|
|
7
|
+
"sidekickToolCallMeta",
|
|
8
|
+
"sidekickToolCallIdPrefix",
|
|
9
|
+
"subagentContextMeta",
|
|
10
|
+
"subagentStartedMeta",
|
|
11
|
+
"subagentCompletedMeta",
|
|
12
|
+
"subagentSupportClientCapability",
|
|
13
|
+
"rootAgentId",
|
|
14
|
+
"sidekickAgentId",
|
|
15
|
+
"toolNameMeta",
|
|
16
|
+
"inferenceToolNameMeta",
|
|
17
|
+
"compactionNotificationMethod",
|
|
18
|
+
"compactionSessionIdField",
|
|
19
|
+
"compactionStatusField",
|
|
20
|
+
"compactionSummaryField",
|
|
21
|
+
"compactionStartedStatus",
|
|
22
|
+
"compactionCompletedStatus",
|
|
23
|
+
"compactionFailedStatus",
|
|
24
|
+
"compactionManualCommand",
|
|
25
|
+
"elicitationAllowOtherMeta",
|
|
26
|
+
"usageInputTokensMeta",
|
|
27
|
+
"usageOutputTokensMeta",
|
|
28
|
+
"usageCachedReadTokensMeta",
|
|
29
|
+
"usageCachedWriteTokensMeta",
|
|
30
|
+
"usageTotalCreditCostMeta",
|
|
31
|
+
"usageTotalAcuCostMeta",
|
|
32
|
+
"usageResponseDimensionsMeta"
|
|
33
|
+
];
|
|
34
|
+
function loadPrivateWireContract() {
|
|
35
|
+
const raw = JSON.parse('{\n "officialRuntime": {\n "package": "devin",\n "version": "3000.11.3",\n "minimumSupportedVersion": "3000.11.1"\n },\n "privateWireContract": {\n "sidekickToolCallMeta": "cognition.ai/sidekick",\n "sidekickToolCallIdPrefix": "sk::",\n "subagentContextMeta": "cognition.ai/subagent_context",\n "subagentStartedMeta": "cognition.ai/subagent_started",\n "subagentCompletedMeta": "cognition.ai/subagent_completed",\n "subagentSupportClientCapability": "cognition.ai/subagentSupport",\n "rootAgentId": "root",\n "sidekickAgentId": "sidekick",\n "toolNameMeta": "cognition.ai/toolName",\n "inferenceToolNameMeta": "cognition.ai/inferenceToolName",\n "compactionNotificationMethod": "_cognition.ai/compaction",\n "compactionSessionIdField": "sessionId",\n "compactionStatusField": "status",\n "compactionSummaryField": "summary",\n "compactionStartedStatus": "started",\n "compactionCompletedStatus": "completed",\n "compactionFailedStatus": "failed",\n "compactionManualCommand": "/compact",\n "elicitationAllowOtherMeta": "cognition.ai/allowOther",\n "usageInputTokensMeta": "cognition.ai/inputTokens",\n "usageOutputTokensMeta": "cognition.ai/outputTokens",\n "usageCachedReadTokensMeta": "cognition.ai/cachedReadTokens",\n "usageCachedWriteTokensMeta": "cognition.ai/cachedWriteTokens",\n "usageTotalCreditCostMeta": "cognition.ai/totalCreditCost",\n "usageTotalAcuCostMeta": "cognition.ai/totalAcuCost",\n "usageResponseDimensionsMeta": "cognition.ai/responseDimensions"\n }\n}\n');
|
|
36
|
+
const section = raw["privateWireContract"];
|
|
37
|
+
if (typeof section !== "object" || section === null || Array.isArray(section)) {
|
|
38
|
+
throw new Error("runtime-manifest.json: privateWireContract must be an object");
|
|
39
|
+
}
|
|
40
|
+
const contract = {};
|
|
41
|
+
for (const field of FIELDS) {
|
|
42
|
+
const value = section[field];
|
|
43
|
+
if (typeof value !== "string" || !value) {
|
|
44
|
+
throw new Error(`runtime-manifest.json: privateWireContract.${field} must be a non-empty string`);
|
|
45
|
+
}
|
|
46
|
+
contract[field] = value;
|
|
47
|
+
}
|
|
48
|
+
return contract;
|
|
49
|
+
}
|
|
50
|
+
const privateWireContract = loadPrivateWireContract();
|
|
51
|
+
const ALLOW_OTHER_META = privateWireContract.elicitationAllowOtherMeta;
|
|
52
|
+
function isRecord$3(value) {
|
|
53
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
|
+
}
|
|
55
|
+
function clientSupportsLodyElicitation(capabilities) {
|
|
56
|
+
if (!isRecord$3(capabilities))
|
|
57
|
+
return false;
|
|
58
|
+
const meta = capabilities["_meta"];
|
|
59
|
+
if (!isRecord$3(meta))
|
|
60
|
+
return false;
|
|
61
|
+
const lody = meta["lody"];
|
|
62
|
+
if (!isRecord$3(lody))
|
|
63
|
+
return false;
|
|
64
|
+
const elicitation = lody["elicitation"];
|
|
65
|
+
return isRecord$3(elicitation) && elicitation["version"] === 1;
|
|
66
|
+
}
|
|
67
|
+
function extractOptions(entries) {
|
|
68
|
+
if (!Array.isArray(entries))
|
|
69
|
+
return [];
|
|
70
|
+
const options = [];
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
if (typeof entry === "string") {
|
|
73
|
+
options.push({ label: entry });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!isRecord$3(entry))
|
|
77
|
+
continue;
|
|
78
|
+
const value = entry["const"];
|
|
79
|
+
const title = entry["title"];
|
|
80
|
+
const description = entry["description"];
|
|
81
|
+
const label = typeof value === "string" ? value : typeof title === "string" ? title : void 0;
|
|
82
|
+
if (label === void 0)
|
|
83
|
+
continue;
|
|
84
|
+
const option = { label };
|
|
85
|
+
const detail = typeof value === "string" ? typeof title === "string" ? title : typeof description === "string" ? description : void 0 : typeof description === "string" ? description : void 0;
|
|
86
|
+
if (detail !== void 0)
|
|
87
|
+
option.description = detail;
|
|
88
|
+
options.push(option);
|
|
89
|
+
}
|
|
90
|
+
return options;
|
|
91
|
+
}
|
|
92
|
+
function questionShape(property) {
|
|
93
|
+
if (property["type"] === "array") {
|
|
94
|
+
const items = isRecord$3(property["items"]) ? property["items"] : {};
|
|
95
|
+
return {
|
|
96
|
+
multiSelect: true,
|
|
97
|
+
options: extractOptions(items["anyOf"] ?? items["oneOf"] ?? items["enum"])
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
multiSelect: false,
|
|
102
|
+
options: extractOptions(property["oneOf"] ?? property["anyOf"] ?? property["enum"])
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function hasOptions(property) {
|
|
106
|
+
if (property["type"] === "array") {
|
|
107
|
+
const items = isRecord$3(property["items"]) ? property["items"] : {};
|
|
108
|
+
return Array.isArray(items["anyOf"]) || Array.isArray(items["oneOf"]) || Array.isArray(items["enum"]);
|
|
109
|
+
}
|
|
110
|
+
return Array.isArray(property["oneOf"]) || Array.isArray(property["anyOf"]) || Array.isArray(property["enum"]);
|
|
111
|
+
}
|
|
112
|
+
function freeKey(base, properties) {
|
|
113
|
+
let key = `${base}__other`;
|
|
114
|
+
for (let i = 2; key in properties; i += 1)
|
|
115
|
+
key = `${base}__other_${i}`;
|
|
116
|
+
return key;
|
|
117
|
+
}
|
|
118
|
+
function rewriteElicitationParams(params) {
|
|
119
|
+
const meta = isRecord$3(params["_meta"]) ? params["_meta"] : void 0;
|
|
120
|
+
if (meta?.[ALLOW_OTHER_META] !== true)
|
|
121
|
+
return void 0;
|
|
122
|
+
const schema = isRecord$3(params["requestedSchema"]) ? params["requestedSchema"] : void 0;
|
|
123
|
+
const properties = schema && isRecord$3(schema["properties"]) ? schema["properties"] : void 0;
|
|
124
|
+
const questions = [];
|
|
125
|
+
const customFields = /* @__PURE__ */ new Map();
|
|
126
|
+
const rewrittenProperties = {};
|
|
127
|
+
if (properties) {
|
|
128
|
+
for (const [fieldId, raw] of Object.entries(properties)) {
|
|
129
|
+
rewrittenProperties[fieldId] = raw;
|
|
130
|
+
if (!isRecord$3(raw))
|
|
131
|
+
continue;
|
|
132
|
+
const { options, multiSelect } = questionShape(raw);
|
|
133
|
+
questions.push({
|
|
134
|
+
id: fieldId,
|
|
135
|
+
question: typeof raw["description"] === "string" ? raw["description"] : "",
|
|
136
|
+
header: typeof raw["title"] === "string" ? raw["title"] : "",
|
|
137
|
+
options,
|
|
138
|
+
multiSelect,
|
|
139
|
+
allowCustomAnswer: true
|
|
140
|
+
});
|
|
141
|
+
if (!hasOptions(raw))
|
|
142
|
+
continue;
|
|
143
|
+
const customKey = freeKey(fieldId, {
|
|
144
|
+
...properties,
|
|
145
|
+
...rewrittenProperties
|
|
146
|
+
});
|
|
147
|
+
rewrittenProperties[customKey] = {
|
|
148
|
+
type: "string",
|
|
149
|
+
title: "Other",
|
|
150
|
+
description: multiSelect ? "Type your own answer instead of choosing options above." : "Type your own answer instead of choosing an option above.",
|
|
151
|
+
_meta: {
|
|
152
|
+
lody: {
|
|
153
|
+
elicitation: {
|
|
154
|
+
version: 1,
|
|
155
|
+
customAnswerFor: fieldId,
|
|
156
|
+
secret: false
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
customFields.set(customKey, { target: fieldId, multiSelect });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const lody = isRecord$3(meta["lody"]) ? meta["lody"] : {};
|
|
165
|
+
const elicitation = {
|
|
166
|
+
...isRecord$3(lody["elicitation"]) ? lody["elicitation"] : {},
|
|
167
|
+
version: 1,
|
|
168
|
+
questions
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
params: {
|
|
172
|
+
...params,
|
|
173
|
+
...schema ? { requestedSchema: { ...schema, properties: rewrittenProperties } } : {},
|
|
174
|
+
_meta: { ...meta, lody: { ...lody, elicitation } }
|
|
175
|
+
},
|
|
176
|
+
customFields
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function foldElicitationResponse(result, customFields) {
|
|
180
|
+
if (customFields.size === 0 || !isRecord$3(result))
|
|
181
|
+
return void 0;
|
|
182
|
+
if (result["action"] !== "accept" || !isRecord$3(result["content"]))
|
|
183
|
+
return void 0;
|
|
184
|
+
const content = result["content"];
|
|
185
|
+
let changed = false;
|
|
186
|
+
const folded = { ...content };
|
|
187
|
+
for (const [key, field] of customFields) {
|
|
188
|
+
if (!(key in folded))
|
|
189
|
+
continue;
|
|
190
|
+
const value = folded[key];
|
|
191
|
+
delete folded[key];
|
|
192
|
+
changed = true;
|
|
193
|
+
if (typeof value === "string" && value.trim()) {
|
|
194
|
+
folded[field.target] = field.multiSelect ? [value] : value;
|
|
195
|
+
} else if (Array.isArray(value) && value.length > 0) {
|
|
196
|
+
folded[field.target] = value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (!changed)
|
|
200
|
+
return void 0;
|
|
201
|
+
return { ...result, content: folded };
|
|
202
|
+
}
|
|
203
|
+
const CONTEXT_META = privateWireContract.subagentContextMeta;
|
|
204
|
+
const SIDEKICK_META = privateWireContract.sidekickToolCallMeta;
|
|
205
|
+
const STARTED_META = privateWireContract.subagentStartedMeta;
|
|
206
|
+
const COMPLETED_META = privateWireContract.subagentCompletedMeta;
|
|
207
|
+
const SIDEKICK_ID_PREFIX = privateWireContract.sidekickToolCallIdPrefix;
|
|
208
|
+
const ROOT_ACTIVITY = /* @__PURE__ */ new Set([
|
|
209
|
+
"agent_message_chunk",
|
|
210
|
+
"agent_thought_chunk",
|
|
211
|
+
"tool_call",
|
|
212
|
+
"tool_call_update",
|
|
213
|
+
"plan"
|
|
214
|
+
]);
|
|
215
|
+
const TERMINAL_TOOL_STATUS = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
216
|
+
const TOOL_DISPLAY_FIELDS = ["title", "kind", "rawInput", "locations"];
|
|
217
|
+
const MAX_BUFFERED_CHILD_UPDATES = 64;
|
|
218
|
+
function isRecord$2(v) {
|
|
219
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
220
|
+
}
|
|
221
|
+
function agentContext$1(update) {
|
|
222
|
+
const meta = update["_meta"];
|
|
223
|
+
if (!isRecord$2(meta))
|
|
224
|
+
return void 0;
|
|
225
|
+
const ctx = meta[CONTEXT_META];
|
|
226
|
+
if (!isRecord$2(ctx))
|
|
227
|
+
return void 0;
|
|
228
|
+
const id = ctx["parentAgentId"];
|
|
229
|
+
return typeof id === "string" ? id : void 0;
|
|
230
|
+
}
|
|
231
|
+
function rememberToolDisplay(run, toolCallId, update) {
|
|
232
|
+
const entry = run.toolDisplay.get(toolCallId) ?? {};
|
|
233
|
+
for (const field of TOOL_DISPLAY_FIELDS) {
|
|
234
|
+
if (update[field] !== void 0)
|
|
235
|
+
entry[field] = update[field];
|
|
236
|
+
}
|
|
237
|
+
run.toolDisplay.set(toolCallId, entry);
|
|
238
|
+
}
|
|
239
|
+
function usageProgress(update) {
|
|
240
|
+
const progress = {};
|
|
241
|
+
if (typeof update["used"] === "number")
|
|
242
|
+
progress.contextTokens = update["used"];
|
|
243
|
+
if (typeof update["size"] === "number")
|
|
244
|
+
progress.contextWindowTokens = update["size"];
|
|
245
|
+
return progress;
|
|
246
|
+
}
|
|
247
|
+
class DevinSubagentEvents {
|
|
248
|
+
sessionId;
|
|
249
|
+
runs = /* @__PURE__ */ new Map();
|
|
250
|
+
sidekickRun;
|
|
251
|
+
buffered = /* @__PURE__ */ new Map();
|
|
252
|
+
overflowed = /* @__PURE__ */ new Set();
|
|
253
|
+
/** toolCallIds owned by any closed sidekick run; late updates are dropped. */
|
|
254
|
+
closedSkToolIds = /* @__PURE__ */ new Set();
|
|
255
|
+
/** latest sidekick usage seen before a run exists. */
|
|
256
|
+
stashedProgress;
|
|
257
|
+
/** root activity arrived while sidekick tools were still in flight. */
|
|
258
|
+
rootResumed = false;
|
|
259
|
+
newId;
|
|
260
|
+
now;
|
|
261
|
+
constructor(sessionId, { newId, now } = {}) {
|
|
262
|
+
this.sessionId = sessionId;
|
|
263
|
+
this.newId = newId ?? (() => crypto.randomUUID());
|
|
264
|
+
this.now = now ?? (() => Math.floor(Date.now() / 1e3));
|
|
265
|
+
}
|
|
266
|
+
emit(run, payload) {
|
|
267
|
+
return {
|
|
268
|
+
kind: "event",
|
|
269
|
+
event: {
|
|
270
|
+
version: 1,
|
|
271
|
+
sessionId: this.sessionId,
|
|
272
|
+
runId: run.runId,
|
|
273
|
+
...payload
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
createSubagentRun(agentId, started, ctx) {
|
|
278
|
+
let parentRunId = null;
|
|
279
|
+
if (ctx && ctx !== privateWireContract.rootAgentId) {
|
|
280
|
+
if (ctx === privateWireContract.sidekickAgentId) {
|
|
281
|
+
parentRunId = this.sidekickRun?.live ? this.sidekickRun.runId : null;
|
|
282
|
+
} else {
|
|
283
|
+
const parent = this.runs.get(ctx);
|
|
284
|
+
parentRunId = parent?.live ? parent.runId : null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const description = typeof started["title"] === "string" ? started["title"] : typeof started["task"] === "string" ? started["task"] : void 0;
|
|
288
|
+
const snapshot = {
|
|
289
|
+
state: "running",
|
|
290
|
+
parentRunId,
|
|
291
|
+
...typeof started["profile"] === "string" ? { name: started["profile"] } : {},
|
|
292
|
+
...description !== void 0 ? { description } : {},
|
|
293
|
+
...typeof started["model"] === "string" ? { modelId: started["model"] } : {},
|
|
294
|
+
startedAtEpochSeconds: this.now(),
|
|
295
|
+
support: {
|
|
296
|
+
stream: ["text", "thought", "tool", "plan"],
|
|
297
|
+
progress: true,
|
|
298
|
+
outputRead: "none",
|
|
299
|
+
cancel: false
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const run = {
|
|
303
|
+
runId: this.newId(),
|
|
304
|
+
agentId,
|
|
305
|
+
live: true,
|
|
306
|
+
snapshot,
|
|
307
|
+
toolIds: /* @__PURE__ */ new Set(),
|
|
308
|
+
inFlight: /* @__PURE__ */ new Set(),
|
|
309
|
+
mirrors: /* @__PURE__ */ new Set(),
|
|
310
|
+
toolDisplay: /* @__PURE__ */ new Map()
|
|
311
|
+
};
|
|
312
|
+
this.runs.set(agentId, run);
|
|
313
|
+
return run;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Open a new sidekick run. Returned outputs go to the client in order:
|
|
317
|
+
* the snapshot first, then any stashed progress observed before the run.
|
|
318
|
+
*/
|
|
319
|
+
openSidekickRun() {
|
|
320
|
+
const out = [];
|
|
321
|
+
const run = {
|
|
322
|
+
runId: this.newId(),
|
|
323
|
+
agentId: privateWireContract.sidekickAgentId,
|
|
324
|
+
live: true,
|
|
325
|
+
snapshot: {
|
|
326
|
+
state: "running",
|
|
327
|
+
parentRunId: null,
|
|
328
|
+
name: "Sidekick",
|
|
329
|
+
startedAtEpochSeconds: this.now(),
|
|
330
|
+
support: {
|
|
331
|
+
stream: ["tool"],
|
|
332
|
+
progress: true,
|
|
333
|
+
outputRead: "none",
|
|
334
|
+
cancel: false
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
toolIds: /* @__PURE__ */ new Set(),
|
|
338
|
+
inFlight: /* @__PURE__ */ new Set(),
|
|
339
|
+
mirrors: /* @__PURE__ */ new Set(),
|
|
340
|
+
toolDisplay: /* @__PURE__ */ new Map()
|
|
341
|
+
};
|
|
342
|
+
this.sidekickRun = run;
|
|
343
|
+
out.push(this.emit(run, { type: "snapshot", snapshot: run.snapshot }));
|
|
344
|
+
if (this.stashedProgress !== void 0) {
|
|
345
|
+
const progress = this.stashedProgress;
|
|
346
|
+
this.stashedProgress = void 0;
|
|
347
|
+
out.push(this.emit(run, { type: "progress", progress }));
|
|
348
|
+
}
|
|
349
|
+
return { run, out };
|
|
350
|
+
}
|
|
351
|
+
closeSidekickSnapshot(run, patch) {
|
|
352
|
+
run.live = false;
|
|
353
|
+
for (const id of run.toolIds)
|
|
354
|
+
this.closedSkToolIds.add(id);
|
|
355
|
+
this.rootResumed = false;
|
|
356
|
+
run.snapshot = {
|
|
357
|
+
...run.snapshot,
|
|
358
|
+
endedAtEpochSeconds: this.now(),
|
|
359
|
+
...patch
|
|
360
|
+
};
|
|
361
|
+
return this.emit(run, { type: "snapshot", snapshot: run.snapshot });
|
|
362
|
+
}
|
|
363
|
+
mirrorRootUpdate(run, update, toolCallId) {
|
|
364
|
+
return {
|
|
365
|
+
...update,
|
|
366
|
+
toolCallId: mirrorToolCallId(run.runId, toolCallId),
|
|
367
|
+
_meta: {
|
|
368
|
+
...isRecord$2(update["_meta"]) ? update["_meta"] : {},
|
|
369
|
+
lody: { subagentRunId: run.runId, subagentToolCallId: toolCallId }
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Classify one runtime `session/update` for this session.
|
|
375
|
+
* Returns run events and root-bound updates in order; drops consumed rows.
|
|
376
|
+
*/
|
|
377
|
+
handleSessionUpdate(update) {
|
|
378
|
+
const out = [];
|
|
379
|
+
const meta = isRecord$2(update["_meta"]) ? update["_meta"] : void 0;
|
|
380
|
+
const ctx = agentContext$1(update);
|
|
381
|
+
const sessionUpdate = update["sessionUpdate"];
|
|
382
|
+
const completedMeta = meta?.[COMPLETED_META];
|
|
383
|
+
const startedMeta = meta?.[STARTED_META];
|
|
384
|
+
if ((sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update") && (completedMeta !== void 0 || startedMeta !== void 0)) {
|
|
385
|
+
if (completedMeta !== void 0) {
|
|
386
|
+
if (!isRecord$2(completedMeta) || typeof completedMeta["agentId"] !== "string") {
|
|
387
|
+
out.push({ kind: "root", update });
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
const run2 = this.runs.get(completedMeta["agentId"]);
|
|
391
|
+
if (run2?.live) {
|
|
392
|
+
const failed = completedMeta["success"] === false;
|
|
393
|
+
const patch = {
|
|
394
|
+
state: failed ? "failed" : "completed",
|
|
395
|
+
endedAtEpochSeconds: this.now(),
|
|
396
|
+
...typeof completedMeta["summary"] === "string" ? { summary: completedMeta["summary"] } : {},
|
|
397
|
+
...failed && typeof completedMeta["summary"] === "string" ? {
|
|
398
|
+
reason: {
|
|
399
|
+
code: "error",
|
|
400
|
+
message: completedMeta["summary"]
|
|
401
|
+
}
|
|
402
|
+
} : {}
|
|
403
|
+
};
|
|
404
|
+
run2.live = false;
|
|
405
|
+
run2.snapshot = { ...run2.snapshot, ...patch };
|
|
406
|
+
out.push(this.emit(run2, { type: "snapshot", snapshot: run2.snapshot }));
|
|
407
|
+
}
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
if (!isRecord$2(startedMeta) || typeof startedMeta["agentId"] !== "string") {
|
|
411
|
+
out.push({ kind: "root", update });
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
const agentId = startedMeta["agentId"];
|
|
415
|
+
const existing = this.runs.get(agentId);
|
|
416
|
+
if (existing?.live)
|
|
417
|
+
return out;
|
|
418
|
+
const run = this.createSubagentRun(agentId, startedMeta, ctx);
|
|
419
|
+
out.push(this.emit(run, { type: "snapshot", snapshot: run.snapshot }));
|
|
420
|
+
if (this.overflowed.delete(agentId)) {
|
|
421
|
+
run.snapshot = { ...run.snapshot, outputIncomplete: true };
|
|
422
|
+
out.push(this.emit(run, { type: "snapshot", snapshot: run.snapshot }));
|
|
423
|
+
}
|
|
424
|
+
const buffered = this.buffered.get(agentId);
|
|
425
|
+
if (buffered) {
|
|
426
|
+
this.buffered.delete(agentId);
|
|
427
|
+
for (const u of buffered)
|
|
428
|
+
out.push(...this.childUpdate(run, u));
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
if (typeof ctx === "string" && ctx !== privateWireContract.rootAgentId && ctx !== privateWireContract.sidekickAgentId) {
|
|
433
|
+
const run = this.runs.get(ctx);
|
|
434
|
+
if (run?.live) {
|
|
435
|
+
out.push(...this.childUpdate(run, update));
|
|
436
|
+
} else if (!run) {
|
|
437
|
+
const buf = this.buffered.get(ctx) ?? [];
|
|
438
|
+
if (buf.length >= MAX_BUFFERED_CHILD_UPDATES) {
|
|
439
|
+
this.overflowed.add(ctx);
|
|
440
|
+
} else {
|
|
441
|
+
buf.push(update);
|
|
442
|
+
this.buffered.set(ctx, buf);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
const toolCallId = typeof update["toolCallId"] === "string" ? update["toolCallId"] : void 0;
|
|
448
|
+
const isSidekick = ctx === privateWireContract.sidekickAgentId || (sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update") && (meta?.[SIDEKICK_META] === true || toolCallId !== void 0 && toolCallId.startsWith(SIDEKICK_ID_PREFIX));
|
|
449
|
+
if (isSidekick) {
|
|
450
|
+
if (toolCallId && this.closedSkToolIds.has(toolCallId)) {
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
if (sessionUpdate === "usage_update" && !this.sidekickRun?.live) {
|
|
454
|
+
this.stashedProgress = usageProgress(update);
|
|
455
|
+
return out;
|
|
456
|
+
}
|
|
457
|
+
let run = this.sidekickRun;
|
|
458
|
+
if (!run?.live) {
|
|
459
|
+
const opened = this.openSidekickRun();
|
|
460
|
+
run = opened.run;
|
|
461
|
+
out.push(...opened.out);
|
|
462
|
+
}
|
|
463
|
+
if (sessionUpdate === "usage_update") {
|
|
464
|
+
out.push(this.emit(run, { type: "progress", progress: usageProgress(update) }));
|
|
465
|
+
return out;
|
|
466
|
+
}
|
|
467
|
+
if (sessionUpdate === "tool_call" && toolCallId) {
|
|
468
|
+
run.toolIds.add(toolCallId);
|
|
469
|
+
run.inFlight.add(toolCallId);
|
|
470
|
+
} else if (sessionUpdate === "tool_call_update" && toolCallId && TERMINAL_TOOL_STATUS.has(String(update["status"]))) {
|
|
471
|
+
run.inFlight.delete(toolCallId);
|
|
472
|
+
if (toolCallId)
|
|
473
|
+
run.toolIds.add(toolCallId);
|
|
474
|
+
}
|
|
475
|
+
if ((sessionUpdate === "tool_call" || sessionUpdate === "tool_call_update") && toolCallId) {
|
|
476
|
+
rememberToolDisplay(run, toolCallId, update);
|
|
477
|
+
}
|
|
478
|
+
if (isLodySubagentOutput(update)) {
|
|
479
|
+
out.push(this.emit(run, { type: "output", update }));
|
|
480
|
+
if (toolCallId && run.mirrors.has(toolCallId)) {
|
|
481
|
+
out.push({
|
|
482
|
+
kind: "root",
|
|
483
|
+
update: this.mirrorRootUpdate(run, update, toolCallId)
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (this.rootResumed && run.inFlight.size === 0) {
|
|
488
|
+
out.push(this.closeSidekickSnapshot(run, { state: "completed" }));
|
|
489
|
+
}
|
|
490
|
+
return out;
|
|
491
|
+
}
|
|
492
|
+
if (this.sidekickRun?.live && ROOT_ACTIVITY.has(String(sessionUpdate))) {
|
|
493
|
+
if (this.sidekickRun.inFlight.size === 0) {
|
|
494
|
+
out.push(this.closeSidekickSnapshot(this.sidekickRun, { state: "completed" }));
|
|
495
|
+
} else {
|
|
496
|
+
this.rootResumed = true;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
out.push({ kind: "root", update });
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
childUpdate(run, update) {
|
|
503
|
+
const out = [];
|
|
504
|
+
if (update["sessionUpdate"] === "usage_update") {
|
|
505
|
+
out.push(this.emit(run, { type: "progress", progress: usageProgress(update) }));
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
if ((update["sessionUpdate"] === "tool_call" || update["sessionUpdate"] === "tool_call_update") && typeof update["toolCallId"] === "string") {
|
|
509
|
+
rememberToolDisplay(run, update["toolCallId"], update);
|
|
510
|
+
}
|
|
511
|
+
if (isLodySubagentOutput(update)) {
|
|
512
|
+
const toolCallId = "toolCallId" in update && typeof update["toolCallId"] === "string" ? update["toolCallId"] : void 0;
|
|
513
|
+
if (toolCallId)
|
|
514
|
+
run.toolIds.add(toolCallId);
|
|
515
|
+
out.push(this.emit(run, { type: "output", update }));
|
|
516
|
+
if (toolCallId && run.mirrors.has(toolCallId)) {
|
|
517
|
+
out.push({
|
|
518
|
+
kind: "root",
|
|
519
|
+
update: this.mirrorRootUpdate(run, update, toolCallId)
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return out;
|
|
523
|
+
}
|
|
524
|
+
if (update["sessionUpdate"] === "agent_message_chunk" || update["sessionUpdate"] === "agent_thought_chunk") {
|
|
525
|
+
if (run.snapshot.outputIncomplete !== true) {
|
|
526
|
+
run.snapshot = { ...run.snapshot, outputIncomplete: true };
|
|
527
|
+
out.push(this.emit(run, { type: "snapshot", snapshot: run.snapshot }));
|
|
528
|
+
}
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
return out;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Runtime `session/request_permission` for this session. Returns the
|
|
535
|
+
* rewritten params when the toolCallId belongs to a live run, else null.
|
|
536
|
+
*/
|
|
537
|
+
rewritePermissionParams(params) {
|
|
538
|
+
if (!isRecord$2(params))
|
|
539
|
+
return null;
|
|
540
|
+
const toolCall = params["toolCall"];
|
|
541
|
+
if (!isRecord$2(toolCall) || typeof toolCall["toolCallId"] !== "string")
|
|
542
|
+
return null;
|
|
543
|
+
const id = toolCall["toolCallId"];
|
|
544
|
+
const events = [];
|
|
545
|
+
let run;
|
|
546
|
+
for (const r of this.runs.values()) {
|
|
547
|
+
if (r.live && r.toolIds.has(id)) {
|
|
548
|
+
run = r;
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (!run && this.sidekickRun?.live && this.sidekickRun.toolIds.has(id)) {
|
|
553
|
+
run = this.sidekickRun;
|
|
554
|
+
}
|
|
555
|
+
if (!run && id.startsWith(SIDEKICK_ID_PREFIX)) {
|
|
556
|
+
if (this.sidekickRun?.live) {
|
|
557
|
+
run = this.sidekickRun;
|
|
558
|
+
} else {
|
|
559
|
+
const opened = this.openSidekickRun();
|
|
560
|
+
events.push(...opened.out);
|
|
561
|
+
run = opened.run;
|
|
562
|
+
}
|
|
563
|
+
run.toolIds.add(id);
|
|
564
|
+
}
|
|
565
|
+
if (!run)
|
|
566
|
+
return null;
|
|
567
|
+
run.mirrors.add(id);
|
|
568
|
+
const known = run.toolDisplay.get(id) ?? {};
|
|
569
|
+
return {
|
|
570
|
+
events,
|
|
571
|
+
params: {
|
|
572
|
+
...params,
|
|
573
|
+
toolCall: {
|
|
574
|
+
...known,
|
|
575
|
+
...toolCall,
|
|
576
|
+
toolCallId: mirrorToolCallId(run.runId, id)
|
|
577
|
+
},
|
|
578
|
+
_meta: {
|
|
579
|
+
...isRecord$2(params["_meta"]) ? params["_meta"] : {},
|
|
580
|
+
lody: { subagentRunId: run.runId, subagentToolCallId: id }
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Client `session/prompt` response arrived. Close any live sidekick run
|
|
587
|
+
* before the response; subagent runs are unaffected (may be background).
|
|
588
|
+
*/
|
|
589
|
+
handlePromptDone(stopReason, isError) {
|
|
590
|
+
this.stashedProgress = void 0;
|
|
591
|
+
const run = this.sidekickRun;
|
|
592
|
+
if (!run?.live)
|
|
593
|
+
return [];
|
|
594
|
+
let patch;
|
|
595
|
+
if (isError) {
|
|
596
|
+
patch = {
|
|
597
|
+
state: "unknown",
|
|
598
|
+
outputIncomplete: true,
|
|
599
|
+
reason: { code: "error" }
|
|
600
|
+
};
|
|
601
|
+
} else if (stopReason === "cancelled") {
|
|
602
|
+
patch = { state: "cancelled", reason: { code: "cancelled" } };
|
|
603
|
+
} else {
|
|
604
|
+
patch = { state: "completed" };
|
|
605
|
+
}
|
|
606
|
+
return [this.closeSidekickSnapshot(run, patch)];
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function mirrorToolCallId(runId, toolCallId) {
|
|
610
|
+
return `subagent:${encodeURIComponent(runId)}:${encodeURIComponent(toolCallId)}`;
|
|
611
|
+
}
|
|
612
|
+
const MODEL_OPTION_ID = "model";
|
|
613
|
+
const FALLBACK_MODEL_ID = "devin";
|
|
614
|
+
const SIDEKICK_MODEL_ID = "sidekick";
|
|
615
|
+
const MAX_RECENT_SIGNATURES = 8;
|
|
616
|
+
const PASSTHROUGH_META = [
|
|
617
|
+
privateWireContract.usageTotalCreditCostMeta,
|
|
618
|
+
privateWireContract.usageTotalAcuCostMeta,
|
|
619
|
+
privateWireContract.usageResponseDimensionsMeta
|
|
620
|
+
];
|
|
621
|
+
function isRecord$1(value) {
|
|
622
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
623
|
+
}
|
|
624
|
+
function num(value) {
|
|
625
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
626
|
+
}
|
|
627
|
+
function agentContext(meta) {
|
|
628
|
+
const ctx = meta?.[privateWireContract.subagentContextMeta];
|
|
629
|
+
if (!isRecord$1(ctx))
|
|
630
|
+
return void 0;
|
|
631
|
+
const id = ctx["parentAgentId"];
|
|
632
|
+
return typeof id === "string" ? id : void 0;
|
|
633
|
+
}
|
|
634
|
+
class DevinSessionUsage {
|
|
635
|
+
sessionId;
|
|
636
|
+
/** One accumulator per ACP session: each session is its own accounting
|
|
637
|
+
* lifetime, so each keeps a never-reused usageScopeId. Created lazily so
|
|
638
|
+
* sessions without countable usage never consume an injected id. */
|
|
639
|
+
accumulator;
|
|
640
|
+
modelId = FALLBACK_MODEL_ID;
|
|
641
|
+
operation = 0;
|
|
642
|
+
signatures = /* @__PURE__ */ new Set();
|
|
643
|
+
signatureOrder = [];
|
|
644
|
+
newId;
|
|
645
|
+
constructor(sessionId, newId = crypto.randomUUID.bind(crypto)) {
|
|
646
|
+
this.sessionId = sessionId;
|
|
647
|
+
this.newId = newId;
|
|
648
|
+
}
|
|
649
|
+
/** Track the ACP `model` config option from session responses and
|
|
650
|
+
* config_option_update notifications; usage rows accumulate under it. */
|
|
651
|
+
observeConfigOptions(options) {
|
|
652
|
+
if (!Array.isArray(options))
|
|
653
|
+
return;
|
|
654
|
+
for (const option of options) {
|
|
655
|
+
if (!isRecord$1(option) || option["id"] !== MODEL_OPTION_ID)
|
|
656
|
+
continue;
|
|
657
|
+
const value = option["currentValue"];
|
|
658
|
+
if (typeof value === "string" && value.length > 0)
|
|
659
|
+
this.modelId = value;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Translate one runtime `usage_update` into a Core SessionUsageUpdate, or
|
|
664
|
+
* return undefined when the row must not enter this session's accounting:
|
|
665
|
+
* child-run usage, a duplicate report of an already-counted request, or a
|
|
666
|
+
* row without the private token counters needed for disjoint buckets.
|
|
667
|
+
*/
|
|
668
|
+
record(update) {
|
|
669
|
+
const meta = isRecord$1(update["_meta"]) ? update["_meta"] : void 0;
|
|
670
|
+
const ctx = agentContext(meta);
|
|
671
|
+
if (ctx !== void 0 && ctx !== privateWireContract.rootAgentId && ctx !== privateWireContract.sidekickAgentId) {
|
|
672
|
+
return void 0;
|
|
673
|
+
}
|
|
674
|
+
const input = num(meta?.[privateWireContract.usageInputTokensMeta]);
|
|
675
|
+
const output = num(meta?.[privateWireContract.usageOutputTokensMeta]);
|
|
676
|
+
if (input === void 0 || output === void 0)
|
|
677
|
+
return void 0;
|
|
678
|
+
const cachedRead = num(meta?.[privateWireContract.usageCachedReadTokensMeta]) ?? 0;
|
|
679
|
+
const cachedWrite = num(meta?.[privateWireContract.usageCachedWriteTokensMeta]);
|
|
680
|
+
const signature = JSON.stringify([
|
|
681
|
+
input,
|
|
682
|
+
output,
|
|
683
|
+
cachedRead,
|
|
684
|
+
cachedWrite ?? 0,
|
|
685
|
+
num(update["used"]) ?? null
|
|
686
|
+
]);
|
|
687
|
+
if (this.signatures.has(signature))
|
|
688
|
+
return void 0;
|
|
689
|
+
this.signatures.add(signature);
|
|
690
|
+
this.signatureOrder.push(signature);
|
|
691
|
+
if (this.signatureOrder.length > MAX_RECENT_SIGNATURES) {
|
|
692
|
+
this.signatures.delete(this.signatureOrder.shift());
|
|
693
|
+
}
|
|
694
|
+
const row = {
|
|
695
|
+
// Devin's inputTokens includes cache reads AND cache writes (verified on
|
|
696
|
+
// gpt-6-luna: input = fresh + cachedRead + cachedWrite); Core buckets
|
|
697
|
+
// are disjoint, so subtract both. SWE-2 rows omit cachedWriteTokens.
|
|
698
|
+
inputTokens: Math.max(0, input - cachedRead - (cachedWrite ?? 0)),
|
|
699
|
+
outputTokens: output,
|
|
700
|
+
cacheReadInputTokens: cachedRead
|
|
701
|
+
};
|
|
702
|
+
if (cachedWrite !== void 0)
|
|
703
|
+
row.cacheCreationInputTokens = cachedWrite;
|
|
704
|
+
const modelId = ctx === privateWireContract.sidekickAgentId ? SIDEKICK_MODEL_ID : this.modelId;
|
|
705
|
+
this.accumulator ??= new SessionUsageAccumulator(this.newId());
|
|
706
|
+
const result = this.accumulator.update(this.sessionId, `devin-request-${++this.operation}`, { [modelId]: row });
|
|
707
|
+
if (result === void 0)
|
|
708
|
+
return void 0;
|
|
709
|
+
const size = num(update["size"]);
|
|
710
|
+
if (size !== void 0)
|
|
711
|
+
result.usage.contextWindow = size;
|
|
712
|
+
if (meta !== void 0) {
|
|
713
|
+
const passthrough = {};
|
|
714
|
+
for (const key of PASSTHROUGH_META) {
|
|
715
|
+
if (meta[key] !== void 0)
|
|
716
|
+
passthrough[key] = meta[key];
|
|
717
|
+
}
|
|
718
|
+
if (Object.keys(passthrough).length > 0) {
|
|
719
|
+
result._meta = { ...result._meta, ...passthrough };
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return result;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const SUBAGENT_SUPPORT_META = privateWireContract.subagentSupportClientCapability;
|
|
726
|
+
const SUBAGENT_EVENTS_CAPABILITY = { version: 1 };
|
|
727
|
+
const COMPACTION_CAPABILITY = { version: 1 };
|
|
728
|
+
const ELICITATION_CAPABILITY = { version: 1 };
|
|
729
|
+
const USAGE_CAPABILITY = { version: 1 };
|
|
730
|
+
const COMPACTION_ACTIVITY_META = {
|
|
731
|
+
lody: { activity: { version: 1, kind: "context_compaction" } }
|
|
732
|
+
};
|
|
733
|
+
function isRecord(value) {
|
|
734
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
735
|
+
}
|
|
736
|
+
function isRequest(message) {
|
|
737
|
+
return typeof message.method === "string" && message.id !== void 0 && message.id !== null;
|
|
738
|
+
}
|
|
739
|
+
function isResponse(message) {
|
|
740
|
+
return message.id !== void 0 && message.id !== null && message.method === void 0;
|
|
741
|
+
}
|
|
742
|
+
function hasCompactionSummary(summary) {
|
|
743
|
+
return typeof summary === "string" && summary.trim().length > 0;
|
|
744
|
+
}
|
|
745
|
+
function isManualCompactionPrompt(params) {
|
|
746
|
+
if (!isRecord(params) || !Array.isArray(params["prompt"]))
|
|
747
|
+
return false;
|
|
748
|
+
let text;
|
|
749
|
+
for (const block of params["prompt"]) {
|
|
750
|
+
if (!isRecord(block) || block["type"] !== "text" || typeof block["text"] !== "string") {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
const value = block["text"].trim();
|
|
754
|
+
if (!value)
|
|
755
|
+
continue;
|
|
756
|
+
if (text !== void 0)
|
|
757
|
+
return false;
|
|
758
|
+
text = value;
|
|
759
|
+
}
|
|
760
|
+
const command = privateWireContract.compactionManualCommand;
|
|
761
|
+
return text === command || text?.startsWith(`${command} `) === true;
|
|
762
|
+
}
|
|
763
|
+
class DevinAcpProxy {
|
|
764
|
+
pending = /* @__PURE__ */ new Map();
|
|
765
|
+
sessions = /* @__PURE__ */ new Map();
|
|
766
|
+
usages = /* @__PURE__ */ new Map();
|
|
767
|
+
admitted = /* @__PURE__ */ new Set();
|
|
768
|
+
/** sessions inside a session/load replay window: updates pass through. */
|
|
769
|
+
replaying = /* @__PURE__ */ new Set();
|
|
770
|
+
/** sessions whose next compaction started event belongs to a manual /compact prompt. */
|
|
771
|
+
manualCompactionArmed = /* @__PURE__ */ new Set();
|
|
772
|
+
/** admitted sessions with a live native compaction. */
|
|
773
|
+
activeCompactions = /* @__PURE__ */ new Map();
|
|
774
|
+
negotiated = false;
|
|
775
|
+
/** client advertised _meta.lody.elicitation — translate private elicitation hints. */
|
|
776
|
+
lodyElicitation = false;
|
|
777
|
+
/** elicitation/create request id -> injected custom-answer companion fields. */
|
|
778
|
+
elicitationCustomFields = /* @__PURE__ */ new Map();
|
|
779
|
+
subagentOpts;
|
|
780
|
+
newId;
|
|
781
|
+
constructor(opts = {}) {
|
|
782
|
+
this.subagentOpts = opts;
|
|
783
|
+
this.newId = opts.newId ?? randomUUID;
|
|
784
|
+
}
|
|
785
|
+
handleClient(message) {
|
|
786
|
+
let msg = message;
|
|
787
|
+
if (!isRecord(msg))
|
|
788
|
+
return { toClient: [], toRuntime: [msg] };
|
|
789
|
+
if (isRequest(msg)) {
|
|
790
|
+
const sessionId = isRecord(msg.params) && typeof msg.params["sessionId"] === "string" ? msg.params["sessionId"] : void 0;
|
|
791
|
+
const manualCompaction = msg.method === "session/prompt" && sessionId !== void 0 && isManualCompactionPrompt(msg.params);
|
|
792
|
+
this.pending.set(msg.id, {
|
|
793
|
+
method: msg.method,
|
|
794
|
+
sessionId,
|
|
795
|
+
manualCompaction
|
|
796
|
+
});
|
|
797
|
+
if (msg.method === "initialize") {
|
|
798
|
+
const caps = isRecord(msg.params) ? msg.params["clientCapabilities"] : void 0;
|
|
799
|
+
this.negotiated = supportsLodySubagentEvents(caps);
|
|
800
|
+
this.lodyElicitation = clientSupportsLodyElicitation(caps);
|
|
801
|
+
if (this.negotiated && isRecord(msg.params)) {
|
|
802
|
+
msg = {
|
|
803
|
+
...msg,
|
|
804
|
+
params: {
|
|
805
|
+
...msg.params,
|
|
806
|
+
clientCapabilities: {
|
|
807
|
+
...isRecord(caps) ? caps : {},
|
|
808
|
+
_meta: {
|
|
809
|
+
...isRecord(caps) && isRecord(caps["_meta"]) ? caps["_meta"] : {},
|
|
810
|
+
[SUBAGENT_SUPPORT_META]: true
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (msg.method === "session/prompt" && sessionId) {
|
|
818
|
+
if (manualCompaction) {
|
|
819
|
+
this.manualCompactionArmed.add(sessionId);
|
|
820
|
+
} else {
|
|
821
|
+
this.manualCompactionArmed.delete(sessionId);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if ((msg.method === "session/load" || msg.method === "session/resume") && sessionId) {
|
|
825
|
+
this.replaying.add(sessionId);
|
|
826
|
+
this.manualCompactionArmed.delete(sessionId);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (isResponse(msg)) {
|
|
830
|
+
const customFields = this.elicitationCustomFields.get(msg.id);
|
|
831
|
+
if (customFields) {
|
|
832
|
+
this.elicitationCustomFields.delete(msg.id);
|
|
833
|
+
const result = foldElicitationResponse(msg.result, customFields);
|
|
834
|
+
if (result !== void 0)
|
|
835
|
+
msg = { ...msg, result };
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return { toClient: [], toRuntime: [msg] };
|
|
839
|
+
}
|
|
840
|
+
handleRuntime(message) {
|
|
841
|
+
const msg = message;
|
|
842
|
+
if (!isRecord(msg))
|
|
843
|
+
return { toClient: [msg], toRuntime: [] };
|
|
844
|
+
if (isResponse(msg)) {
|
|
845
|
+
const pending = this.pending.get(msg.id);
|
|
846
|
+
this.pending.delete(msg.id);
|
|
847
|
+
return this.handleRuntimeResponse(msg, pending);
|
|
848
|
+
}
|
|
849
|
+
if (isRequest(msg)) {
|
|
850
|
+
return this.handleRuntimeRequest(msg);
|
|
851
|
+
}
|
|
852
|
+
if (msg.method === "session/update" && isRecord(msg.params)) {
|
|
853
|
+
return this.handleSessionUpdate(msg);
|
|
854
|
+
}
|
|
855
|
+
if (msg.method === privateWireContract.compactionNotificationMethod) {
|
|
856
|
+
return this.handleCompactionNotification(msg);
|
|
857
|
+
}
|
|
858
|
+
return { toClient: [msg], toRuntime: [] };
|
|
859
|
+
}
|
|
860
|
+
handleRuntimeResponse(msg, pending) {
|
|
861
|
+
const toClient = [];
|
|
862
|
+
if (pending?.method === "initialize" && isRecord(msg.result)) {
|
|
863
|
+
const agentCaps = isRecord(msg.result["agentCapabilities"]) ? msg.result["agentCapabilities"] : {};
|
|
864
|
+
const meta = isRecord(agentCaps["_meta"]) ? agentCaps["_meta"] : {};
|
|
865
|
+
const lody = isRecord(meta["lody"]) ? meta["lody"] : {};
|
|
866
|
+
const result = {
|
|
867
|
+
...msg.result,
|
|
868
|
+
agentCapabilities: {
|
|
869
|
+
...agentCaps,
|
|
870
|
+
_meta: {
|
|
871
|
+
...meta,
|
|
872
|
+
lody: {
|
|
873
|
+
...lody,
|
|
874
|
+
subagentEvents: SUBAGENT_EVENTS_CAPABILITY,
|
|
875
|
+
compaction: COMPACTION_CAPABILITY,
|
|
876
|
+
elicitation: ELICITATION_CAPABILITY,
|
|
877
|
+
usage: USAGE_CAPABILITY
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
toClient.push({ ...msg, result });
|
|
883
|
+
return { toClient, toRuntime: [] };
|
|
884
|
+
}
|
|
885
|
+
if (pending?.manualCompaction && pending.sessionId && msg.error !== void 0) {
|
|
886
|
+
this.manualCompactionArmed.delete(pending.sessionId);
|
|
887
|
+
}
|
|
888
|
+
if (pending?.method === "session/new" || pending?.method === "session/fork") {
|
|
889
|
+
if (isRecord(msg.result) && typeof msg.result["sessionId"] === "string") {
|
|
890
|
+
this.admit(msg.result["sessionId"]);
|
|
891
|
+
this.usages.get(msg.result["sessionId"])?.observeConfigOptions(msg.result["configOptions"]);
|
|
892
|
+
}
|
|
893
|
+
} else if ((pending?.method === "session/load" || pending?.method === "session/resume") && pending.sessionId) {
|
|
894
|
+
this.replaying.delete(pending.sessionId);
|
|
895
|
+
if (msg.result !== void 0 && msg.error === void 0) {
|
|
896
|
+
this.admit(pending.sessionId);
|
|
897
|
+
if (isRecord(msg.result)) {
|
|
898
|
+
this.usages.get(pending.sessionId)?.observeConfigOptions(msg.result["configOptions"]);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
} else if (pending?.method === "session/set_config_option" && pending.sessionId) {
|
|
902
|
+
if (isRecord(msg.result)) {
|
|
903
|
+
this.usages.get(pending.sessionId)?.observeConfigOptions(msg.result["configOptions"]);
|
|
904
|
+
}
|
|
905
|
+
} else if (pending?.method === "session/prompt" && pending.sessionId) {
|
|
906
|
+
const session = this.sessions.get(pending.sessionId);
|
|
907
|
+
if (session) {
|
|
908
|
+
const isError = msg.error !== void 0;
|
|
909
|
+
const stopReason = isRecord(msg.result) && typeof msg.result["stopReason"] === "string" ? msg.result["stopReason"] : void 0;
|
|
910
|
+
for (const o of session.handlePromptDone(stopReason, isError)) {
|
|
911
|
+
toClient.push(...this.render(session, o));
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
toClient.push(msg);
|
|
916
|
+
return { toClient, toRuntime: [] };
|
|
917
|
+
}
|
|
918
|
+
handleRuntimeRequest(msg) {
|
|
919
|
+
if (msg.method === "elicitation/create" && isRecord(msg.params) && this.lodyElicitation) {
|
|
920
|
+
const rewritten = rewriteElicitationParams(msg.params);
|
|
921
|
+
if (rewritten) {
|
|
922
|
+
if (rewritten.customFields.size > 0) {
|
|
923
|
+
this.elicitationCustomFields.set(msg.id, rewritten.customFields);
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
toClient: [{ ...msg, params: rewritten.params }],
|
|
927
|
+
toRuntime: []
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (msg.method === "session/request_permission" && isRecord(msg.params)) {
|
|
932
|
+
const sessionId = typeof msg.params["sessionId"] === "string" ? msg.params["sessionId"] : void 0;
|
|
933
|
+
const session = sessionId ? this.sessions.get(sessionId) : void 0;
|
|
934
|
+
if (session && sessionId && this.admitted.has(sessionId) && !this.replaying.has(sessionId)) {
|
|
935
|
+
const rewritten = session.rewritePermissionParams(msg.params);
|
|
936
|
+
if (rewritten) {
|
|
937
|
+
const toClient = [];
|
|
938
|
+
for (const o of rewritten.events) {
|
|
939
|
+
toClient.push(...this.render(session, o));
|
|
940
|
+
}
|
|
941
|
+
toClient.push({ ...msg, params: rewritten.params });
|
|
942
|
+
return { toClient, toRuntime: [] };
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return { toClient: [msg], toRuntime: [] };
|
|
947
|
+
}
|
|
948
|
+
handleSessionUpdate(msg) {
|
|
949
|
+
const params = msg.params;
|
|
950
|
+
const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
|
|
951
|
+
const update = params["update"];
|
|
952
|
+
const session = sessionId ? this.sessions.get(sessionId) : void 0;
|
|
953
|
+
const live = sessionId !== void 0 && this.admitted.has(sessionId) && !this.replaying.has(sessionId);
|
|
954
|
+
const usageOut = [];
|
|
955
|
+
if (live && sessionId && isRecord(update)) {
|
|
956
|
+
const tracker = this.usages.get(sessionId);
|
|
957
|
+
if (tracker && update["sessionUpdate"] === "config_option_update") {
|
|
958
|
+
tracker.observeConfigOptions(update["configOptions"]);
|
|
959
|
+
}
|
|
960
|
+
if (tracker && update["sessionUpdate"] === "usage_update") {
|
|
961
|
+
const translated = tracker.record(update);
|
|
962
|
+
if (translated !== void 0) {
|
|
963
|
+
usageOut.push({
|
|
964
|
+
jsonrpc: "2.0",
|
|
965
|
+
method: LODY_EXTENSION_METHODS.sessionUsageUpdate,
|
|
966
|
+
params: translated
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (!this.negotiated || !sessionId || !session || !live || !isRecord(update)) {
|
|
972
|
+
return { toClient: [msg, ...usageOut], toRuntime: [] };
|
|
973
|
+
}
|
|
974
|
+
const toClient = [];
|
|
975
|
+
for (const o of session.handleSessionUpdate(update)) {
|
|
976
|
+
toClient.push(...this.render(session, o));
|
|
977
|
+
}
|
|
978
|
+
toClient.push(...usageOut);
|
|
979
|
+
return { toClient, toRuntime: [] };
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Devin's private `_cognition.ai/compaction` lifecycle, rendered as a Core
|
|
983
|
+
* context-compaction activity on a synthetic tool call. The native lifecycle
|
|
984
|
+
* owns completion independently of the `/compact` prompt response. Only
|
|
985
|
+
* admitted sessions outside a replay window produce activity; replay and
|
|
986
|
+
* malformed or unknown-status rows pass through untouched.
|
|
987
|
+
*/
|
|
988
|
+
handleCompactionNotification(msg) {
|
|
989
|
+
const params = msg.params;
|
|
990
|
+
const sessionId = isRecord(params) && typeof params[privateWireContract.compactionSessionIdField] === "string" && params[privateWireContract.compactionSessionIdField] ? params[privateWireContract.compactionSessionIdField] : void 0;
|
|
991
|
+
const status = isRecord(params) ? params[privateWireContract.compactionStatusField] : void 0;
|
|
992
|
+
const known = status === privateWireContract.compactionStartedStatus || status === privateWireContract.compactionCompletedStatus || status === privateWireContract.compactionFailedStatus;
|
|
993
|
+
if (!sessionId || !known || !this.admitted.has(sessionId) || this.replaying.has(sessionId)) {
|
|
994
|
+
return { toClient: [msg], toRuntime: [] };
|
|
995
|
+
}
|
|
996
|
+
if (status === privateWireContract.compactionStartedStatus) {
|
|
997
|
+
if (this.activeCompactions.has(sessionId)) {
|
|
998
|
+
return { toClient: [], toRuntime: [] };
|
|
999
|
+
}
|
|
1000
|
+
const toolCallId = `devin-compaction-${this.newId()}`;
|
|
1001
|
+
const shown = this.manualCompactionArmed.delete(sessionId);
|
|
1002
|
+
this.activeCompactions.set(sessionId, { toolCallId, shown });
|
|
1003
|
+
if (!shown)
|
|
1004
|
+
return { toClient: [], toRuntime: [] };
|
|
1005
|
+
return {
|
|
1006
|
+
toClient: [this.compactionStart(sessionId, toolCallId)],
|
|
1007
|
+
toRuntime: []
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
const active = this.activeCompactions.get(sessionId);
|
|
1011
|
+
this.activeCompactions.delete(sessionId);
|
|
1012
|
+
if (!active) {
|
|
1013
|
+
return { toClient: [], toRuntime: [] };
|
|
1014
|
+
}
|
|
1015
|
+
const manuallyArmed = this.manualCompactionArmed.delete(sessionId);
|
|
1016
|
+
const summary = params[privateWireContract.compactionSummaryField];
|
|
1017
|
+
const terminal = this.compactionTerminal(sessionId, active.toolCallId, status, summary);
|
|
1018
|
+
if (active.shown) {
|
|
1019
|
+
return { toClient: [terminal], toRuntime: [] };
|
|
1020
|
+
}
|
|
1021
|
+
if (!manuallyArmed && status === privateWireContract.compactionCompletedStatus && !hasCompactionSummary(summary)) {
|
|
1022
|
+
return { toClient: [], toRuntime: [] };
|
|
1023
|
+
}
|
|
1024
|
+
return {
|
|
1025
|
+
toClient: [this.compactionStart(sessionId, active.toolCallId), terminal],
|
|
1026
|
+
toRuntime: []
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
compactionStart(sessionId, toolCallId) {
|
|
1030
|
+
return {
|
|
1031
|
+
jsonrpc: "2.0",
|
|
1032
|
+
method: "session/update",
|
|
1033
|
+
params: {
|
|
1034
|
+
sessionId,
|
|
1035
|
+
update: {
|
|
1036
|
+
sessionUpdate: "tool_call",
|
|
1037
|
+
toolCallId,
|
|
1038
|
+
title: "Compact context",
|
|
1039
|
+
kind: "other",
|
|
1040
|
+
status: "in_progress",
|
|
1041
|
+
_meta: { ...COMPACTION_ACTIVITY_META }
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
compactionTerminal(sessionId, toolCallId, status, summary) {
|
|
1047
|
+
const update = {
|
|
1048
|
+
sessionUpdate: "tool_call_update",
|
|
1049
|
+
toolCallId,
|
|
1050
|
+
status,
|
|
1051
|
+
_meta: { ...COMPACTION_ACTIVITY_META }
|
|
1052
|
+
};
|
|
1053
|
+
if (status === privateWireContract.compactionCompletedStatus && hasCompactionSummary(summary)) {
|
|
1054
|
+
update["content"] = [
|
|
1055
|
+
{ type: "content", content: { type: "text", text: summary } }
|
|
1056
|
+
];
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
jsonrpc: "2.0",
|
|
1060
|
+
method: "session/update",
|
|
1061
|
+
params: { sessionId, update }
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
render(session, out) {
|
|
1065
|
+
if (out.kind === "event") {
|
|
1066
|
+
return [
|
|
1067
|
+
{
|
|
1068
|
+
jsonrpc: "2.0",
|
|
1069
|
+
method: LODY_SUBAGENT_EVENT_METHOD,
|
|
1070
|
+
params: out.event
|
|
1071
|
+
}
|
|
1072
|
+
];
|
|
1073
|
+
}
|
|
1074
|
+
return [
|
|
1075
|
+
{
|
|
1076
|
+
jsonrpc: "2.0",
|
|
1077
|
+
method: "session/update",
|
|
1078
|
+
params: { sessionId: session.sessionId, update: out.update }
|
|
1079
|
+
}
|
|
1080
|
+
];
|
|
1081
|
+
}
|
|
1082
|
+
admit(sessionId) {
|
|
1083
|
+
if (!this.admitted.has(sessionId)) {
|
|
1084
|
+
this.admitted.add(sessionId);
|
|
1085
|
+
this.sessions.set(sessionId, new DevinSubagentEvents(sessionId, this.subagentOpts));
|
|
1086
|
+
this.usages.set(sessionId, new DevinSessionUsage(sessionId, this.newId));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
/** Method of the still-pending client request, for translation dispatch. */
|
|
1090
|
+
pendingClientMethod(id) {
|
|
1091
|
+
return this.pending.get(id)?.method;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function spawnDevinRuntime(devinPath2, { env = process.env, spawnImpl = spawn } = {}) {
|
|
1095
|
+
return spawnImpl(devinPath2, ["acp"], {
|
|
1096
|
+
env,
|
|
1097
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
1098
|
+
windowsHide: true
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const devinPath = process.env.DEVIN_PATH;
|
|
1102
|
+
if (!devinPath) {
|
|
1103
|
+
console.error("DEVIN_PATH must point to the official Devin runtime");
|
|
1104
|
+
process.exit(1);
|
|
1105
|
+
}
|
|
1106
|
+
const child = spawnDevinRuntime(devinPath);
|
|
1107
|
+
const proxy = new DevinAcpProxy();
|
|
1108
|
+
if (!child.stdin || !child.stdout) {
|
|
1109
|
+
console.error("Official Devin runtime stdio is not piped");
|
|
1110
|
+
process.exit(1);
|
|
1111
|
+
}
|
|
1112
|
+
const childStdin = child.stdin;
|
|
1113
|
+
const childStdout = child.stdout;
|
|
1114
|
+
function exit(code) {
|
|
1115
|
+
process.stdout.write("", () => process.exit(code));
|
|
1116
|
+
}
|
|
1117
|
+
function write(stream, message) {
|
|
1118
|
+
stream.write(`${JSON.stringify(message)}
|
|
1119
|
+
`);
|
|
1120
|
+
}
|
|
1121
|
+
function dispatch(output) {
|
|
1122
|
+
for (const message of output.toRuntime)
|
|
1123
|
+
write(childStdin, message);
|
|
1124
|
+
for (const message of output.toClient)
|
|
1125
|
+
write(process.stdout, message);
|
|
1126
|
+
}
|
|
1127
|
+
createInterface({ input: process.stdin }).on("line", (line) => {
|
|
1128
|
+
try {
|
|
1129
|
+
dispatch(proxy.handleClient(JSON.parse(line)));
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
console.error(`Invalid ACP client message: ${error instanceof Error ? error.message : String(error)}`);
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
createInterface({ input: childStdout }).on("line", (line) => {
|
|
1135
|
+
try {
|
|
1136
|
+
dispatch(proxy.handleRuntime(JSON.parse(line)));
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
console.error(`Invalid Devin runtime message: ${error instanceof Error ? error.message : String(error)}`);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
process.stdin.on("end", () => childStdin.end());
|
|
1142
|
+
child.on("error", (error) => {
|
|
1143
|
+
console.error(`Failed to launch official Devin runtime: ${error.message}`);
|
|
1144
|
+
exit(1);
|
|
1145
|
+
});
|
|
1146
|
+
child.on("close", (code, signal) => {
|
|
1147
|
+
if (signal)
|
|
1148
|
+
process.kill(process.pid, signal);
|
|
1149
|
+
else
|
|
1150
|
+
exit(code ?? 1);
|
|
1151
|
+
});
|
|
1152
|
+
//# sourceMappingURL=devin-acp.js.map
|