lody 0.97.2 → 0.98.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/dist/chunks/{index-5o35kMTc.js → index-DGod--yP.js} +880 -709
- package/dist/chunks/{package-CZY_YQI4.js → package-BdifTDFO.js} +1 -1
- package/dist/chunks/{acp-DCoAUZc7.js → profile-BmD8b-zH.js} +434 -36
- package/dist/chunks/{review-viewer-Dah28phN.js → review-viewer-wpzL1tfQ.js} +4 -5
- package/dist/chunks/{runtime-manifest-DqfOlvSw.js → runtime-manifest-DZsDOOW3.js} +1 -1
- package/dist/claude-acp.js +12689 -7932
- package/dist/codex-acp.js +1 -1
- package/dist/deepseek-acp.js +352 -166
- package/dist/grok-acp.js +236 -32
- package/dist/index.js +540 -243
- package/package.json +1 -1
- package/dist/chunks/profile-DQaC7SJZ.js +0 -430
package/dist/deepseek-acp.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { S as SessionUsageAccumulator } from "./chunks/usage-DY4Eg1Mf.js";
|
|
2
|
+
import { o as object, s as string, l as literal, b as boolean, c as array, r as record, u as unknown } from "./chunks/schemas-C-NzK2uQ.js";
|
|
2
3
|
import { randomUUID, createHash } from "node:crypto";
|
|
3
4
|
import { isAbsolute } from "node:path";
|
|
4
5
|
import { Readable, Writable } from "node:stream";
|
|
5
|
-
import { n as ndJsonStream,
|
|
6
|
-
import { j as DEEPSEEK_HARNESS_BASE_URL_ENV, k as DEEPSEEK_HARNESS_API_KEY_ENV, a as DEEPSEEK_HARNESS_AGENT_PRESETS, l as ACP_EXTENSION_DSH_VERSION, m as DEEPSEEK_HARNESS_DEFAULT_MODEL, n as DEEPSEEK_HARNESS_PROVIDER } from "./chunks/profile-DQaC7SJZ.js";
|
|
6
|
+
import { j as DEEPSEEK_HARNESS_BASE_URL_ENV, l as DEEPSEEK_HARNESS_API_KEY_ENV, n as ndJsonStream, o as AgentSideConnection, a as DEEPSEEK_HARNESS_AGENT_PRESETS, R as RequestError, p as ACP_EXTENSION_DSH_VERSION, P as PROTOCOL_VERSION, q as DEEPSEEK_HARNESS_DEFAULT_MODEL, r as DEEPSEEK_HARNESS_PROVIDER } from "./chunks/profile-BmD8b-zH.js";
|
|
7
7
|
import { L as LODY_EXTENSION_METHODS } from "./chunks/methods-CvEDMahD.js";
|
|
8
8
|
import { c as createPlanModeConfigOption, L as LODY_PLAN_MODE_CONFIG_ID } from "./chunks/plan-mode-BLOER-fv.js";
|
|
9
9
|
import "node:url";
|
|
10
|
-
import "./chunks/schemas-C-NzK2uQ.js";
|
|
11
10
|
const prices = {
|
|
12
11
|
"deepseek-flash": [0.15, 3e-3, 0.6],
|
|
13
12
|
// Official aliases now served and billed as DeepSeek-V4.1-Flash.
|
|
@@ -60,6 +59,172 @@ class HarnessUsageTracker {
|
|
|
60
59
|
return this.accumulator.update(sessionId, String(seq), { [this.route.model]: usage });
|
|
61
60
|
}
|
|
62
61
|
}
|
|
62
|
+
const questionSchema = object({
|
|
63
|
+
id: string().refine((value) => value.trim().length > 0),
|
|
64
|
+
question: string().min(1),
|
|
65
|
+
header: string().optional(),
|
|
66
|
+
detail: string().optional(),
|
|
67
|
+
options: array(object({
|
|
68
|
+
label: string().min(1),
|
|
69
|
+
description: string().optional()
|
|
70
|
+
})).optional(),
|
|
71
|
+
multiSelect: boolean().optional(),
|
|
72
|
+
intent: object({ kind: literal("plan-review"), approve: string() }).optional()
|
|
73
|
+
});
|
|
74
|
+
function failure(code, message) {
|
|
75
|
+
return Object.assign(new Error(message), { name: "UserQuestionError", code });
|
|
76
|
+
}
|
|
77
|
+
const meta = (fields = {}) => ({
|
|
78
|
+
lody: { elicitation: { version: 1, ...fields } }
|
|
79
|
+
});
|
|
80
|
+
class UserQuestionBridge {
|
|
81
|
+
sessionId;
|
|
82
|
+
send;
|
|
83
|
+
capabilities;
|
|
84
|
+
tail = Promise.resolve();
|
|
85
|
+
pending = /* @__PURE__ */ new Set();
|
|
86
|
+
constructor(sessionId, send, capabilities) {
|
|
87
|
+
this.sessionId = sessionId;
|
|
88
|
+
this.send = send;
|
|
89
|
+
this.capabilities = capabilities;
|
|
90
|
+
}
|
|
91
|
+
cancel() {
|
|
92
|
+
for (const controller of this.pending)
|
|
93
|
+
controller.abort();
|
|
94
|
+
}
|
|
95
|
+
ask(request) {
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
this.pending.add(controller);
|
|
98
|
+
const signal = request.signal ? AbortSignal.any([request.signal, controller.signal]) : controller.signal;
|
|
99
|
+
const aborted = () => failure("ASK_ABORTED", "ask_user_question was cancelled before the user answered");
|
|
100
|
+
let removeListener = () => {
|
|
101
|
+
};
|
|
102
|
+
const cancellation = new Promise((_, reject) => {
|
|
103
|
+
const abort = () => reject(aborted());
|
|
104
|
+
if (signal.aborted)
|
|
105
|
+
abort();
|
|
106
|
+
else {
|
|
107
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
108
|
+
removeListener = () => signal.removeEventListener("abort", abort);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const previous = this.tail;
|
|
112
|
+
const work = previous.then(async () => {
|
|
113
|
+
if (signal.aborted)
|
|
114
|
+
throw aborted();
|
|
115
|
+
return this.elicit(request);
|
|
116
|
+
});
|
|
117
|
+
const result = Promise.race([work, cancellation]).finally(() => {
|
|
118
|
+
removeListener();
|
|
119
|
+
this.pending.delete(controller);
|
|
120
|
+
});
|
|
121
|
+
this.tail = Promise.all([previous, result.catch(() => {
|
|
122
|
+
})]).then(() => {
|
|
123
|
+
});
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
async elicit(request) {
|
|
127
|
+
const capabilities = this.capabilities();
|
|
128
|
+
if (!capabilities.form)
|
|
129
|
+
throw failure("NO_PROVIDER", "ACP client does not support form elicitation");
|
|
130
|
+
const parsed = array(questionSchema).min(1).safeParse(request.questions);
|
|
131
|
+
if (!parsed.success)
|
|
132
|
+
throw failure("BAD_QUESTIONS", "Invalid user question request");
|
|
133
|
+
const questions = parsed.data;
|
|
134
|
+
if (new Set(questions.map((q) => q.id)).size !== questions.length)
|
|
135
|
+
throw failure("BAD_QUESTIONS", "Question ids must be unique");
|
|
136
|
+
const properties = /* @__PURE__ */ Object.create(null);
|
|
137
|
+
const fields = questions.map((question, index) => {
|
|
138
|
+
const key = `question_${index}`;
|
|
139
|
+
const customKey = `custom_${index}`;
|
|
140
|
+
const options = question.options ?? [];
|
|
141
|
+
if (new Set(options.map((o) => o.label)).size !== options.length)
|
|
142
|
+
throw failure("BAD_QUESTIONS", "Option labels must be unique");
|
|
143
|
+
if (question.intent && (question.detail === void 0 || !options.some((o) => o.label === question.intent?.approve)))
|
|
144
|
+
throw failure("BAD_INTENT", "Plan review requires detail and an existing approval option");
|
|
145
|
+
const description = [question.question, question.detail].filter(Boolean).join("\n\n");
|
|
146
|
+
const additive = options.length > 0 && question.multiSelect === true && capabilities.answerNotes;
|
|
147
|
+
let other = "Other";
|
|
148
|
+
while (options.some((o) => o.label === other))
|
|
149
|
+
other += " (custom)";
|
|
150
|
+
const choices = options.map((o) => ({
|
|
151
|
+
const: o.label,
|
|
152
|
+
title: o.label,
|
|
153
|
+
...o.description ? { description: o.description } : {}
|
|
154
|
+
}));
|
|
155
|
+
if (additive)
|
|
156
|
+
choices.push({ const: other, title: other });
|
|
157
|
+
const base = { title: question.header || question.id, description };
|
|
158
|
+
properties[key] = options.length === 0 ? { ...base, type: "string" } : question.multiSelect ? { ...base, type: "array", items: { anyOf: choices } } : { ...base, type: "string", oneOf: choices };
|
|
159
|
+
if (options.length > 0)
|
|
160
|
+
properties[customKey] = {
|
|
161
|
+
type: "string",
|
|
162
|
+
title: "Other",
|
|
163
|
+
description: additive ? "Add a custom answer alongside your selections, or select Other and enter it here." : "Type your own answer instead of selecting an option.",
|
|
164
|
+
_meta: meta(additive ? { noteFor: key } : { customAnswerFor: key })
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
question,
|
|
168
|
+
key,
|
|
169
|
+
customKey,
|
|
170
|
+
options,
|
|
171
|
+
additive,
|
|
172
|
+
other,
|
|
173
|
+
description
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
let response;
|
|
177
|
+
try {
|
|
178
|
+
response = await this.send({
|
|
179
|
+
sessionId: this.sessionId,
|
|
180
|
+
mode: "form",
|
|
181
|
+
message: fields.length === 1 ? fields[0]?.description ?? "Input requested" : "Input requested",
|
|
182
|
+
requestedSchema: { type: "object", properties },
|
|
183
|
+
_meta: meta({ autoResolveAfterSeconds: null })
|
|
184
|
+
});
|
|
185
|
+
} catch (cause) {
|
|
186
|
+
throw failure("ANSWER_FAILED", `ACP user question request failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
187
|
+
}
|
|
188
|
+
if (response.action === "cancel")
|
|
189
|
+
throw failure("ASK_ABORTED", "The user cancelled the question");
|
|
190
|
+
if (response.action === "decline")
|
|
191
|
+
throw failure("ASK_DECLINED", "The user declined the question");
|
|
192
|
+
const accepted = object({
|
|
193
|
+
action: literal("accept"),
|
|
194
|
+
content: record(string(), unknown())
|
|
195
|
+
}).safeParse(response);
|
|
196
|
+
if (!accepted.success)
|
|
197
|
+
throw failure("INVALID_ANSWER", "ACP client returned an invalid question response");
|
|
198
|
+
const content = accepted.data.content;
|
|
199
|
+
return {
|
|
200
|
+
answers: fields.map(({ question, key, customKey, options, additive, other }) => {
|
|
201
|
+
const raw = content[key];
|
|
202
|
+
const custom = content[customKey];
|
|
203
|
+
const invalid = () => failure("INVALID_ANSWER", `Invalid answer for question ${question.id}`);
|
|
204
|
+
if (custom !== void 0 && typeof custom !== "string")
|
|
205
|
+
throw invalid();
|
|
206
|
+
if (options.length === 0) {
|
|
207
|
+
if (typeof raw !== "string" || !raw.trim())
|
|
208
|
+
throw invalid();
|
|
209
|
+
return { id: question.id, selected: [], custom: raw };
|
|
210
|
+
}
|
|
211
|
+
const selected = raw === void 0 ? [] : question.multiSelect ? raw : [raw];
|
|
212
|
+
if (!Array.isArray(selected) || selected.some((v) => typeof v !== "string") || new Set(selected).size !== selected.length)
|
|
213
|
+
throw invalid();
|
|
214
|
+
if (selected.some((v) => !options.some((o) => o.label === v) && !(additive && v === other)))
|
|
215
|
+
throw invalid();
|
|
216
|
+
const hasCustom = typeof custom === "string" && custom.trim().length > 0;
|
|
217
|
+
if (!selected.length && !hasCustom || additive && selected.includes(other) && !hasCustom || !question.multiSelect && selected.length > 0 && hasCustom)
|
|
218
|
+
throw invalid();
|
|
219
|
+
return {
|
|
220
|
+
id: question.id,
|
|
221
|
+
selected: selected.filter((v) => !(additive && v === other)),
|
|
222
|
+
...hasCustom ? { custom } : {}
|
|
223
|
+
};
|
|
224
|
+
})
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
63
228
|
const name = "acp-extension-dsh";
|
|
64
229
|
const inject = [
|
|
65
230
|
"agents",
|
|
@@ -69,7 +234,8 @@ const inject = [
|
|
|
69
234
|
"llm",
|
|
70
235
|
"permissionPresets",
|
|
71
236
|
"sessionPersistence",
|
|
72
|
-
"sessionQuery"
|
|
237
|
+
"sessionQuery",
|
|
238
|
+
"userQuestions"
|
|
73
239
|
];
|
|
74
240
|
const LODY_CAPABILITIES = {
|
|
75
241
|
compaction: { version: 1 },
|
|
@@ -346,10 +512,10 @@ function installModelSelection(agentContext, selection) {
|
|
|
346
512
|
};
|
|
347
513
|
});
|
|
348
514
|
}
|
|
349
|
-
function planModeService(
|
|
350
|
-
return
|
|
515
|
+
function planModeService(record2) {
|
|
516
|
+
return record2.agent.ctx.get("planMode");
|
|
351
517
|
}
|
|
352
|
-
function configOptions(
|
|
518
|
+
function configOptions(record2) {
|
|
353
519
|
const options = [
|
|
354
520
|
{
|
|
355
521
|
id: MODE_CONFIG_ID,
|
|
@@ -357,8 +523,8 @@ function configOptions(record) {
|
|
|
357
523
|
description: "Sandbox and approval policy for the session",
|
|
358
524
|
category: "mode",
|
|
359
525
|
type: "select",
|
|
360
|
-
currentValue:
|
|
361
|
-
options:
|
|
526
|
+
currentValue: record2.permissionMode,
|
|
527
|
+
options: record2.permissionOptions.map((mode) => ({
|
|
362
528
|
value: mode.value,
|
|
363
529
|
name: mode.name,
|
|
364
530
|
description: mode.description ?? null
|
|
@@ -370,8 +536,8 @@ function configOptions(record) {
|
|
|
370
536
|
description: "Tools, prompt, and capabilities composed for the session",
|
|
371
537
|
category: "agent_preset",
|
|
372
538
|
type: "select",
|
|
373
|
-
currentValue:
|
|
374
|
-
options:
|
|
539
|
+
currentValue: record2.agentPreset,
|
|
540
|
+
options: record2.agentPresetOptions.map((preset) => {
|
|
375
541
|
const builtIn = DEEPSEEK_HARNESS_AGENT_PRESETS.find((candidate) => candidate.value === preset.id);
|
|
376
542
|
return {
|
|
377
543
|
value: preset.id,
|
|
@@ -386,28 +552,28 @@ function configOptions(record) {
|
|
|
386
552
|
description: "DeepSeek model used for the session",
|
|
387
553
|
category: "model",
|
|
388
554
|
type: "select",
|
|
389
|
-
currentValue:
|
|
390
|
-
options:
|
|
555
|
+
currentValue: record2.selection.current.model,
|
|
556
|
+
options: record2.models.map((model2) => ({
|
|
391
557
|
value: model2.id,
|
|
392
558
|
name: model2.name,
|
|
393
559
|
description: model2.description ?? null
|
|
394
560
|
}))
|
|
395
561
|
}
|
|
396
562
|
];
|
|
397
|
-
const plan = planModeService(
|
|
563
|
+
const plan = planModeService(record2);
|
|
398
564
|
if (plan) {
|
|
399
|
-
const state = plan.get(
|
|
565
|
+
const state = plan.get(record2.agent);
|
|
400
566
|
options.push(createPlanModeConfigOption(state.pending ?? state.active));
|
|
401
567
|
}
|
|
402
|
-
const model =
|
|
403
|
-
if (model?.reasoning &&
|
|
568
|
+
const model = record2.models.find((candidate) => candidate.id === record2.selection.current.model);
|
|
569
|
+
if (model?.reasoning && record2.selection.current.reasoningEffort) {
|
|
404
570
|
options.push({
|
|
405
571
|
id: REASONING_EFFORT_CONFIG_ID,
|
|
406
572
|
name: "Reasoning effort",
|
|
407
573
|
description: "How much reasoning effort the model should use",
|
|
408
574
|
category: "thought_level",
|
|
409
575
|
type: "select",
|
|
410
|
-
currentValue:
|
|
576
|
+
currentValue: record2.selection.current.reasoningEffort,
|
|
411
577
|
options: model.reasoning.efforts.map((effort) => ({
|
|
412
578
|
value: effort.id,
|
|
413
579
|
name: effort.name,
|
|
@@ -417,10 +583,10 @@ function configOptions(record) {
|
|
|
417
583
|
}
|
|
418
584
|
return options;
|
|
419
585
|
}
|
|
420
|
-
function legacyModels(
|
|
586
|
+
function legacyModels(record2) {
|
|
421
587
|
return {
|
|
422
|
-
currentModelId:
|
|
423
|
-
availableModels:
|
|
588
|
+
currentModelId: record2.selection.current.model,
|
|
589
|
+
availableModels: record2.models.flatMap((model) => [
|
|
424
590
|
{
|
|
425
591
|
modelId: model.id,
|
|
426
592
|
name: model.name,
|
|
@@ -434,10 +600,10 @@ function legacyModels(record) {
|
|
|
434
600
|
])
|
|
435
601
|
};
|
|
436
602
|
}
|
|
437
|
-
function modeState(
|
|
603
|
+
function modeState(record2) {
|
|
438
604
|
return {
|
|
439
|
-
currentModeId:
|
|
440
|
-
availableModes:
|
|
605
|
+
currentModeId: record2.permissionMode,
|
|
606
|
+
availableModes: record2.permissionOptions.map((mode) => ({
|
|
441
607
|
id: mode.value,
|
|
442
608
|
name: mode.name,
|
|
443
609
|
description: mode.description ?? null
|
|
@@ -642,6 +808,7 @@ function apply(ctx, rawConfig) {
|
|
|
642
808
|
let closed = false;
|
|
643
809
|
let conn;
|
|
644
810
|
let imagePromptEnabled = false;
|
|
811
|
+
let questionCapabilities = { form: false, answerNotes: false };
|
|
645
812
|
if (ctx.permissionPresets.names.length === 0) {
|
|
646
813
|
throw new Error("acp-extension-dsh: no permission presets are composed");
|
|
647
814
|
}
|
|
@@ -658,42 +825,42 @@ function apply(ctx, rawConfig) {
|
|
|
658
825
|
throw internalError("the ACP bridge has been disposed");
|
|
659
826
|
};
|
|
660
827
|
const requireSession = (sessionId) => {
|
|
661
|
-
const
|
|
662
|
-
if (!
|
|
828
|
+
const record2 = sessions.get(sessionId);
|
|
829
|
+
if (!record2)
|
|
663
830
|
throw invalidParams(`unknown session: ${sessionId}`);
|
|
664
|
-
return
|
|
831
|
+
return record2;
|
|
665
832
|
};
|
|
666
833
|
const ownedRecord = (agent) => {
|
|
667
|
-
const
|
|
668
|
-
return
|
|
834
|
+
const record2 = sessions.get(agent.session.id);
|
|
835
|
+
return record2?.agent === agent ? record2 : void 0;
|
|
669
836
|
};
|
|
670
837
|
const notify = async (notification) => {
|
|
671
838
|
await conn.sessionUpdate(notification).catch((error) => {
|
|
672
839
|
ctx.logger.warn(`acp-extension-dsh: session/update failed: ${String(error)}`);
|
|
673
840
|
});
|
|
674
841
|
};
|
|
675
|
-
const enqueueOutput = (
|
|
676
|
-
|
|
677
|
-
const
|
|
842
|
+
const enqueueOutput = (record2, work, inflight) => {
|
|
843
|
+
record2.outputTail = record2.outputTail.then(work).catch((error) => {
|
|
844
|
+
const failure2 = error instanceof Error ? error : new Error(String(error));
|
|
678
845
|
if (inflight)
|
|
679
|
-
inflight.outputError ??=
|
|
846
|
+
inflight.outputError ??= failure2;
|
|
680
847
|
ctx.logger.warn(`acp-extension-dsh: assistant output failed: ${errorChain(error)}`);
|
|
681
848
|
});
|
|
682
849
|
};
|
|
683
|
-
const enqueueNotification = (
|
|
684
|
-
const settleAfterQuiescence = (
|
|
850
|
+
const enqueueNotification = (record2, notification, inflight) => enqueueOutput(record2, () => notify(notification), inflight);
|
|
851
|
+
const settleAfterQuiescence = (record2, inflight) => {
|
|
685
852
|
if (inflight.settlementStarted)
|
|
686
853
|
return;
|
|
687
854
|
inflight.settlementStarted = true;
|
|
688
855
|
void (async () => {
|
|
689
856
|
await inflight.admissionDone;
|
|
690
857
|
if (inflight.messageQueued) {
|
|
691
|
-
await
|
|
692
|
-
await
|
|
858
|
+
await record2.agent.whenIdle();
|
|
859
|
+
await record2.outputTail;
|
|
693
860
|
}
|
|
694
|
-
if (
|
|
861
|
+
if (record2.inflight !== inflight)
|
|
695
862
|
return;
|
|
696
|
-
|
|
863
|
+
record2.inflight = void 0;
|
|
697
864
|
if (inflight.cancelRequested) {
|
|
698
865
|
inflight.resolve("cancelled");
|
|
699
866
|
} else if (inflight.outputError) {
|
|
@@ -707,43 +874,43 @@ function apply(ctx, rawConfig) {
|
|
|
707
874
|
inflight.resolve(end ? end.kind === "interrupted" ? "cancelled" : "end_turn" : "cancelled");
|
|
708
875
|
}
|
|
709
876
|
})().catch((error) => {
|
|
710
|
-
if (
|
|
877
|
+
if (record2.inflight !== inflight)
|
|
711
878
|
return;
|
|
712
|
-
|
|
879
|
+
record2.inflight = void 0;
|
|
713
880
|
inflight.reject(internalError(`prompt settlement failed: ${errorChain(error)}`));
|
|
714
881
|
});
|
|
715
882
|
};
|
|
716
|
-
const refreshPermissionState = (
|
|
717
|
-
const permissionMode = ctx.permissionPresets.current(
|
|
718
|
-
if (permissionMode ===
|
|
883
|
+
const refreshPermissionState = (record2) => {
|
|
884
|
+
const permissionMode = ctx.permissionPresets.current(record2.agent.session);
|
|
885
|
+
if (permissionMode === record2.permissionMode)
|
|
719
886
|
return false;
|
|
720
|
-
|
|
721
|
-
|
|
887
|
+
record2.permissionMode = permissionMode;
|
|
888
|
+
record2.permissionOptions = permissionState(ctx, permissionMode);
|
|
722
889
|
return true;
|
|
723
890
|
};
|
|
724
|
-
const schedulePermissionSync = (
|
|
725
|
-
if (
|
|
891
|
+
const schedulePermissionSync = (record2) => {
|
|
892
|
+
if (record2.permissionSyncQueued)
|
|
726
893
|
return;
|
|
727
|
-
|
|
894
|
+
record2.permissionSyncQueued = true;
|
|
728
895
|
queueMicrotask(() => {
|
|
729
|
-
|
|
730
|
-
if (sessions.get(
|
|
896
|
+
record2.permissionSyncQueued = false;
|
|
897
|
+
if (sessions.get(record2.agent.session.id) !== record2)
|
|
731
898
|
return;
|
|
732
899
|
try {
|
|
733
|
-
if (!refreshPermissionState(
|
|
900
|
+
if (!refreshPermissionState(record2))
|
|
734
901
|
return;
|
|
735
|
-
enqueueNotification(
|
|
736
|
-
sessionId:
|
|
902
|
+
enqueueNotification(record2, {
|
|
903
|
+
sessionId: record2.agent.session.id,
|
|
737
904
|
update: {
|
|
738
905
|
sessionUpdate: "current_mode_update",
|
|
739
|
-
currentModeId:
|
|
906
|
+
currentModeId: record2.permissionMode
|
|
740
907
|
}
|
|
741
908
|
});
|
|
742
|
-
enqueueNotification(
|
|
743
|
-
sessionId:
|
|
909
|
+
enqueueNotification(record2, {
|
|
910
|
+
sessionId: record2.agent.session.id,
|
|
744
911
|
update: {
|
|
745
912
|
sessionUpdate: "config_option_update",
|
|
746
|
-
configOptions: configOptions(
|
|
913
|
+
configOptions: configOptions(record2)
|
|
747
914
|
}
|
|
748
915
|
});
|
|
749
916
|
} catch (error) {
|
|
@@ -752,43 +919,43 @@ function apply(ctx, rawConfig) {
|
|
|
752
919
|
});
|
|
753
920
|
};
|
|
754
921
|
const disposeRecords = async (records) => {
|
|
755
|
-
await Promise.all(records.map(async (
|
|
756
|
-
await
|
|
757
|
-
await
|
|
758
|
-
await
|
|
922
|
+
await Promise.all(records.map(async (record2) => {
|
|
923
|
+
await record2.inflight?.admissionDone;
|
|
924
|
+
await record2.agent.whenIdle();
|
|
925
|
+
await record2.outputTail;
|
|
759
926
|
}));
|
|
760
927
|
const subagents = ctx.get("subagents");
|
|
761
928
|
if (subagents) {
|
|
762
929
|
try {
|
|
763
|
-
await subagents.drainContinuableDescendants(records.map((
|
|
930
|
+
await subagents.drainContinuableDescendants(records.map((record2) => record2.agent));
|
|
764
931
|
} catch (error) {
|
|
765
932
|
ctx.logger.warn(`acp-extension-dsh: continuable subagent teardown failed: ${String(error)}`);
|
|
766
933
|
}
|
|
767
934
|
}
|
|
768
|
-
const results = await Promise.allSettled(records.map((
|
|
935
|
+
const results = await Promise.allSettled(records.map((record2) => record2.dispose()));
|
|
769
936
|
const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
770
937
|
if (failures.length > 0) {
|
|
771
938
|
throw new AggregateError(failures, `DeepSeek ACP teardown failed for ${failures.length} session(s): ${failures.map(errorChain).join("; ")}`);
|
|
772
939
|
}
|
|
773
940
|
};
|
|
774
941
|
ctx.on("agent/assistant-stream", ({ agent, frame }) => {
|
|
775
|
-
const
|
|
776
|
-
if (!
|
|
942
|
+
const record2 = ownedRecord(agent);
|
|
943
|
+
if (!record2 || frame.type !== "chunk")
|
|
777
944
|
return;
|
|
778
945
|
const chunk = frame.chunk;
|
|
779
946
|
if (!chunk)
|
|
780
947
|
return;
|
|
781
948
|
if (chunk.type === "reasoning-delta" && typeof chunk.text === "string" && chunk.text.length > 0) {
|
|
782
|
-
enqueueNotification(
|
|
783
|
-
sessionId:
|
|
949
|
+
enqueueNotification(record2, {
|
|
950
|
+
sessionId: record2.agent.session.id,
|
|
784
951
|
update: {
|
|
785
952
|
sessionUpdate: "agent_thought_chunk",
|
|
786
953
|
content: { type: "text", text: chunk.text }
|
|
787
954
|
}
|
|
788
955
|
});
|
|
789
956
|
} else if (chunk.type === "block-end" && chunk.block?.type === "reasoning") {
|
|
790
|
-
enqueueNotification(
|
|
791
|
-
sessionId:
|
|
957
|
+
enqueueNotification(record2, {
|
|
958
|
+
sessionId: record2.agent.session.id,
|
|
792
959
|
update: {
|
|
793
960
|
sessionUpdate: "agent_thought_chunk",
|
|
794
961
|
content: { type: "text", text: "\n\n" }
|
|
@@ -797,35 +964,38 @@ function apply(ctx, rawConfig) {
|
|
|
797
964
|
}
|
|
798
965
|
});
|
|
799
966
|
ctx.on("session/event", (session, event) => {
|
|
800
|
-
const
|
|
801
|
-
if (!
|
|
967
|
+
const record2 = sessions.get(session.header.id);
|
|
968
|
+
if (!record2 || record2.agent.session !== session)
|
|
802
969
|
return;
|
|
803
970
|
if (event.type === "plan/mode") {
|
|
804
|
-
enqueueNotification(
|
|
805
|
-
sessionId:
|
|
806
|
-
update: {
|
|
971
|
+
enqueueNotification(record2, {
|
|
972
|
+
sessionId: record2.agent.session.id,
|
|
973
|
+
update: {
|
|
974
|
+
sessionUpdate: "config_option_update",
|
|
975
|
+
configOptions: configOptions(record2)
|
|
976
|
+
}
|
|
807
977
|
});
|
|
808
978
|
}
|
|
809
979
|
if (PERMISSION_EVENT_TYPES.has(event.type))
|
|
810
|
-
schedulePermissionSync(
|
|
980
|
+
schedulePermissionSync(record2);
|
|
811
981
|
try {
|
|
812
982
|
if (event.type === "request/context" && event.data.provider && event.data.model) {
|
|
813
|
-
|
|
983
|
+
record2.usage.setRoute(event.data.provider, event.data.model);
|
|
814
984
|
}
|
|
815
985
|
if (event.type === "assistant/message" && event.data.usage && event.seq !== void 0) {
|
|
816
|
-
const update =
|
|
986
|
+
const update = record2.usage.record(session.id, event.seq, event.time ?? NaN, event.data.usage);
|
|
817
987
|
if (update)
|
|
818
|
-
enqueueOutput(
|
|
988
|
+
enqueueOutput(record2, () => conn.extNotification(LODY_EXTENSION_METHODS.sessionUsageUpdate, update), record2.inflight);
|
|
819
989
|
}
|
|
820
990
|
if (event.type === "assistant/message") {
|
|
821
|
-
const inflight =
|
|
822
|
-
enqueueOutput(
|
|
991
|
+
const inflight = record2.inflight?.turn === event.data.turn ? record2.inflight : void 0;
|
|
992
|
+
enqueueOutput(record2, async () => {
|
|
823
993
|
for (const block of event.data.message?.content ?? []) {
|
|
824
994
|
const content = await assistantBlockToAcp(block, attachments);
|
|
825
995
|
if (!content)
|
|
826
996
|
continue;
|
|
827
997
|
await notify({
|
|
828
|
-
sessionId:
|
|
998
|
+
sessionId: record2.agent.session.id,
|
|
829
999
|
update: { sessionUpdate: "agent_message_chunk", content }
|
|
830
1000
|
});
|
|
831
1001
|
}
|
|
@@ -836,8 +1006,8 @@ function apply(ctx, rawConfig) {
|
|
|
836
1006
|
kind: "context_compaction",
|
|
837
1007
|
automatic: event.data.turn !== null
|
|
838
1008
|
};
|
|
839
|
-
enqueueNotification(
|
|
840
|
-
sessionId:
|
|
1009
|
+
enqueueNotification(record2, {
|
|
1010
|
+
sessionId: record2.agent.session.id,
|
|
841
1011
|
update: {
|
|
842
1012
|
sessionUpdate: "tool_call",
|
|
843
1013
|
toolCallId: `context-compaction:${event.data.compactionId}`,
|
|
@@ -854,8 +1024,8 @@ function apply(ctx, rawConfig) {
|
|
|
854
1024
|
automatic: event.data.turn !== null,
|
|
855
1025
|
...event.data.error ? { failureReason: event.data.error } : {}
|
|
856
1026
|
};
|
|
857
|
-
enqueueNotification(
|
|
858
|
-
sessionId:
|
|
1027
|
+
enqueueNotification(record2, {
|
|
1028
|
+
sessionId: record2.agent.session.id,
|
|
859
1029
|
update: {
|
|
860
1030
|
sessionUpdate: "tool_call_update",
|
|
861
1031
|
toolCallId: `context-compaction:${event.data.compactionId}`,
|
|
@@ -866,7 +1036,7 @@ function apply(ctx, rawConfig) {
|
|
|
866
1036
|
});
|
|
867
1037
|
}
|
|
868
1038
|
} finally {
|
|
869
|
-
const inflight =
|
|
1039
|
+
const inflight = record2.inflight;
|
|
870
1040
|
if (inflight && event.type === "turn/end" && inflight.turn === event.data.turn && event.data.reason) {
|
|
871
1041
|
inflight.endReason = event.data.reason;
|
|
872
1042
|
}
|
|
@@ -878,19 +1048,19 @@ function apply(ctx, rawConfig) {
|
|
|
878
1048
|
inflight.turn = turn;
|
|
879
1049
|
});
|
|
880
1050
|
ctx.on("agent/error", ({ agent, turn, error }) => {
|
|
881
|
-
const
|
|
882
|
-
const inflight =
|
|
883
|
-
if (!
|
|
1051
|
+
const record2 = ownedRecord(agent);
|
|
1052
|
+
const inflight = record2?.inflight;
|
|
1053
|
+
if (!record2 || !inflight || !inflight.messageQueued || inflight.turn !== turn)
|
|
884
1054
|
return;
|
|
885
1055
|
inflight.agentError = new Error(errorChain(error));
|
|
886
|
-
settleAfterQuiescence(
|
|
1056
|
+
settleAfterQuiescence(record2, inflight);
|
|
887
1057
|
});
|
|
888
1058
|
ctx.on("approval/request", (request, next) => {
|
|
889
|
-
const
|
|
890
|
-
if (!
|
|
1059
|
+
const record2 = ownedRecord(request.agent);
|
|
1060
|
+
if (!record2 || !request.callId)
|
|
891
1061
|
return next();
|
|
892
1062
|
return conn.requestPermission({
|
|
893
|
-
sessionId:
|
|
1063
|
+
sessionId: record2.agent.session.id,
|
|
894
1064
|
toolCall: { toolCallId: request.callId },
|
|
895
1065
|
options: [
|
|
896
1066
|
{ optionId: "allow-once", name: "Allow once", kind: "allow_once" },
|
|
@@ -902,91 +1072,100 @@ function apply(ctx, rawConfig) {
|
|
|
902
1072
|
return outcome.optionId === "allow-once" ? "allowed-once" : "rejected";
|
|
903
1073
|
});
|
|
904
1074
|
});
|
|
905
|
-
const setPermissionMode = (
|
|
906
|
-
if (modeId ===
|
|
1075
|
+
const setPermissionMode = (record2, modeId) => {
|
|
1076
|
+
if (modeId === record2.permissionMode)
|
|
907
1077
|
return;
|
|
908
1078
|
assertAllowed(modeId, new Set(ctx.permissionPresets.names), "permission mode");
|
|
909
|
-
ctx.permissionPresets.set(
|
|
910
|
-
const permissionMode = ctx.permissionPresets.current(
|
|
1079
|
+
ctx.permissionPresets.set(record2.agent.session, modeId);
|
|
1080
|
+
const permissionMode = ctx.permissionPresets.current(record2.agent.session);
|
|
911
1081
|
if (permissionMode !== modeId) {
|
|
912
1082
|
throw internalError(`permission preset ${JSON.stringify(modeId)} did not become the effective mode`);
|
|
913
1083
|
}
|
|
914
|
-
|
|
915
|
-
|
|
1084
|
+
record2.permissionMode = permissionMode;
|
|
1085
|
+
record2.permissionOptions = permissionState(ctx, permissionMode);
|
|
916
1086
|
};
|
|
917
|
-
const setConfigOption = (
|
|
1087
|
+
const setConfigOption = (record2, params) => {
|
|
918
1088
|
if (params.configId === LODY_PLAN_MODE_CONFIG_ID) {
|
|
919
|
-
const plan = planModeService(
|
|
1089
|
+
const plan = planModeService(record2);
|
|
920
1090
|
if (!plan || typeof params.value !== "boolean") {
|
|
921
1091
|
throw invalidParams("Plan mode requires a supported Agent preset and a boolean value");
|
|
922
1092
|
}
|
|
923
|
-
plan.set(
|
|
924
|
-
return { configOptions: configOptions(
|
|
1093
|
+
plan.set(record2.agent, params.value);
|
|
1094
|
+
return { configOptions: configOptions(record2) };
|
|
925
1095
|
}
|
|
926
1096
|
const value = requireSelectValue(params);
|
|
927
1097
|
if (params.configId === MODE_CONFIG_ID) {
|
|
928
|
-
setPermissionMode(
|
|
1098
|
+
setPermissionMode(record2, value);
|
|
929
1099
|
} else if (params.configId === AGENT_PRESET_CONFIG_ID) {
|
|
930
|
-
const available = new Set(
|
|
1100
|
+
const available = new Set(record2.agentPresetOptions.map((preset) => preset.id));
|
|
931
1101
|
assertAllowed(value, available, "agent preset");
|
|
932
|
-
if (value ===
|
|
933
|
-
return { configOptions: configOptions(
|
|
934
|
-
if (
|
|
1102
|
+
if (value === record2.agentPreset)
|
|
1103
|
+
return { configOptions: configOptions(record2) };
|
|
1104
|
+
if (record2.started) {
|
|
935
1105
|
throw invalidParams("agent preset is fixed after the session has started");
|
|
936
1106
|
}
|
|
937
|
-
return ctx.agentPresets.select(
|
|
938
|
-
|
|
939
|
-
return { configOptions: configOptions(
|
|
1107
|
+
return ctx.agentPresets.select(record2.agent, value).then((selectedPreset) => {
|
|
1108
|
+
record2.agentPreset = selectedPreset;
|
|
1109
|
+
return { configOptions: configOptions(record2) };
|
|
940
1110
|
}).catch((error) => {
|
|
941
1111
|
if (error instanceof RequestError)
|
|
942
1112
|
throw error;
|
|
943
1113
|
throw invalidParams(`failed to select agent preset ${JSON.stringify(value)}: ${errorChain(error)}`);
|
|
944
1114
|
});
|
|
945
1115
|
} else if (params.configId === MODEL_CONFIG_ID) {
|
|
946
|
-
return resolveHarnessModel(llm,
|
|
947
|
-
const index =
|
|
1116
|
+
return resolveHarnessModel(llm, record2.selection.current.provider, value).then((model) => {
|
|
1117
|
+
const index = record2.models.findIndex((candidate) => candidate.id === value);
|
|
948
1118
|
if (index === -1)
|
|
949
|
-
|
|
1119
|
+
record2.models.push(model);
|
|
950
1120
|
else
|
|
951
|
-
|
|
952
|
-
const reasoningEffort = resolveReasoningEffort(model,
|
|
953
|
-
|
|
954
|
-
provider:
|
|
1121
|
+
record2.models[index] = model;
|
|
1122
|
+
const reasoningEffort = resolveReasoningEffort(model, record2.selection.current.reasoningEffort && model.reasoning?.efforts.some((effort) => effort.id === record2.selection.current.reasoningEffort) ? record2.selection.current.reasoningEffort : void 0);
|
|
1123
|
+
record2.selection.current = {
|
|
1124
|
+
provider: record2.selection.current.provider,
|
|
955
1125
|
model: value,
|
|
956
1126
|
...reasoningEffort ? { reasoningEffort } : {}
|
|
957
1127
|
};
|
|
958
|
-
return { configOptions: configOptions(
|
|
1128
|
+
return { configOptions: configOptions(record2) };
|
|
959
1129
|
}).catch((error) => {
|
|
960
1130
|
if (error instanceof RequestError)
|
|
961
1131
|
throw error;
|
|
962
1132
|
throw invalidParams(`failed to resolve model ${JSON.stringify(value)}: ${errorChain(error)}`);
|
|
963
1133
|
});
|
|
964
1134
|
} else if (params.configId === REASONING_EFFORT_CONFIG_ID) {
|
|
965
|
-
const model =
|
|
1135
|
+
const model = record2.models.find((candidate) => candidate.id === record2.selection.current.model);
|
|
966
1136
|
if (!model)
|
|
967
1137
|
throw internalError("selected model metadata is unavailable");
|
|
968
1138
|
const reasoningEffort = resolveReasoningEffort(model, value);
|
|
969
1139
|
if (!reasoningEffort) {
|
|
970
1140
|
throw invalidParams(`model ${JSON.stringify(model.id)} has no reasoning selector`);
|
|
971
1141
|
}
|
|
972
|
-
|
|
973
|
-
...
|
|
1142
|
+
record2.selection.current = {
|
|
1143
|
+
...record2.selection.current,
|
|
974
1144
|
reasoningEffort
|
|
975
1145
|
};
|
|
976
1146
|
} else {
|
|
977
1147
|
throw invalidParams(`unknown config option: ${params.configId}`);
|
|
978
1148
|
}
|
|
979
|
-
return { configOptions: configOptions(
|
|
1149
|
+
return { configOptions: configOptions(record2) };
|
|
980
1150
|
};
|
|
981
1151
|
const makeAgent = (connection) => {
|
|
982
1152
|
conn = connection;
|
|
983
1153
|
return {
|
|
984
1154
|
async initialize(_params) {
|
|
1155
|
+
const elicitation = _params.clientCapabilities?._meta?.lody;
|
|
1156
|
+
const notes = elicitation && typeof elicitation === "object" && !Array.isArray(elicitation) && "elicitation" in elicitation ? elicitation.elicitation : void 0;
|
|
1157
|
+
questionCapabilities = {
|
|
1158
|
+
form: _params.clientCapabilities?.elicitation?.form != null,
|
|
1159
|
+
answerNotes: !!notes && typeof notes === "object" && !Array.isArray(notes) && "version" in notes && notes.version === 1 && "answerNotes" in notes && notes.answerNotes === true
|
|
1160
|
+
};
|
|
985
1161
|
const { models } = await loadModelCatalog();
|
|
986
1162
|
imagePromptEnabled = supportsAcpImagePrompts(attachments, models);
|
|
987
1163
|
return {
|
|
988
1164
|
protocolVersion: PROTOCOL_VERSION,
|
|
989
|
-
agentInfo: {
|
|
1165
|
+
agentInfo: {
|
|
1166
|
+
name: "acp-extension-dsh",
|
|
1167
|
+
version: ACP_EXTENSION_DSH_VERSION
|
|
1168
|
+
},
|
|
990
1169
|
agentCapabilities: {
|
|
991
1170
|
promptCapabilities: {
|
|
992
1171
|
image: imagePromptEnabled,
|
|
@@ -1014,6 +1193,7 @@ function apply(ctx, rawConfig) {
|
|
|
1014
1193
|
}
|
|
1015
1194
|
const { models, initialModel: initialModelId } = catalog;
|
|
1016
1195
|
const sessionId = randomUUID();
|
|
1196
|
+
const questions = new UserQuestionBridge(sessionId, (request) => conn.unstable_createElicitation(request), () => questionCapabilities);
|
|
1017
1197
|
const initialModel = models.find((model) => model.id === initialModelId);
|
|
1018
1198
|
if (!initialModel)
|
|
1019
1199
|
throw internalError("initial model metadata is unavailable");
|
|
@@ -1026,13 +1206,9 @@ function apply(ctx, rawConfig) {
|
|
|
1026
1206
|
}
|
|
1027
1207
|
};
|
|
1028
1208
|
const agentPresetOptions = (await ctx.agentPresets.list()).filter((preset) => preset.broken === void 0);
|
|
1029
|
-
|
|
1209
|
+
const requestedPreset = ctx.agentPresets.defaultId;
|
|
1030
1210
|
if (!agentPresetOptions.some((preset) => preset.id === requestedPreset)) {
|
|
1031
|
-
|
|
1032
|
-
throw internalError(`default agent preset ${JSON.stringify(requestedPreset)} and recovery preset "standard" are unavailable; restore the bundled standard preset or choose an available agent-presets.default in $DSH_HOME/settings.yaml`);
|
|
1033
|
-
}
|
|
1034
|
-
ctx.logger.warn(`acp-extension-dsh: default agent preset ${JSON.stringify(requestedPreset)} is unavailable; using "standard" for this session without changing settings`);
|
|
1035
|
-
requestedPreset = "standard";
|
|
1211
|
+
throw internalError(`default agent preset ${JSON.stringify(requestedPreset)} is unavailable`);
|
|
1036
1212
|
}
|
|
1037
1213
|
const mcpServerNames = reserveMcpServerNames(params.mcpServers, sessionId, activeMcpServerNames);
|
|
1038
1214
|
let mountedPreset = requestedPreset;
|
|
@@ -1044,6 +1220,11 @@ function apply(ctx, rawConfig) {
|
|
|
1044
1220
|
agentOptions: { provider: config.provider, model: initialModelId },
|
|
1045
1221
|
setup: async (agentContext) => {
|
|
1046
1222
|
installModelSelection(agentContext, selection);
|
|
1223
|
+
agentContext.on("user-questions/request", (request, next) => {
|
|
1224
|
+
if (!request.agent || ownedRecord(request.agent)?.questions !== questions)
|
|
1225
|
+
return next();
|
|
1226
|
+
return questions.ask(request);
|
|
1227
|
+
});
|
|
1047
1228
|
mountedPreset = (await ctx.agentPresets.mount(agentContext, requestedPreset)).id;
|
|
1048
1229
|
await mountMcpServers(agentContext, params.mcpServers, mcpServerNames.names, params.cwd);
|
|
1049
1230
|
}
|
|
@@ -1055,6 +1236,7 @@ function apply(ctx, rawConfig) {
|
|
|
1055
1236
|
throw internalError(`failed to create session: ${errorChain(error)}`);
|
|
1056
1237
|
}
|
|
1057
1238
|
const dispose = async () => {
|
|
1239
|
+
questions.cancel();
|
|
1058
1240
|
try {
|
|
1059
1241
|
await handle.dispose();
|
|
1060
1242
|
} finally {
|
|
@@ -1074,7 +1256,8 @@ function apply(ctx, rawConfig) {
|
|
|
1074
1256
|
await dispose();
|
|
1075
1257
|
throw error;
|
|
1076
1258
|
}
|
|
1077
|
-
const
|
|
1259
|
+
const record2 = {
|
|
1260
|
+
questions,
|
|
1078
1261
|
usage: new HarnessUsageTracker(!baseUrl || /^https:\/\/api\.deepseek\.com(?:\/v1)?\/?$/.test(baseUrl)),
|
|
1079
1262
|
agent: handle.agent,
|
|
1080
1263
|
dispose,
|
|
@@ -1087,27 +1270,27 @@ function apply(ctx, rawConfig) {
|
|
|
1087
1270
|
started: false,
|
|
1088
1271
|
outputTail: Promise.resolve()
|
|
1089
1272
|
};
|
|
1090
|
-
sessions.set(sessionId,
|
|
1273
|
+
sessions.set(sessionId, record2);
|
|
1091
1274
|
return {
|
|
1092
1275
|
sessionId,
|
|
1093
|
-
modes: modeState(
|
|
1094
|
-
configOptions: configOptions(
|
|
1095
|
-
models: legacyModels(
|
|
1276
|
+
modes: modeState(record2),
|
|
1277
|
+
configOptions: configOptions(record2),
|
|
1278
|
+
models: legacyModels(record2)
|
|
1096
1279
|
};
|
|
1097
1280
|
},
|
|
1098
1281
|
setSessionMode(params) {
|
|
1099
|
-
const
|
|
1100
|
-
setPermissionMode(
|
|
1282
|
+
const record2 = requireSession(params.sessionId);
|
|
1283
|
+
setPermissionMode(record2, params.modeId);
|
|
1101
1284
|
},
|
|
1102
1285
|
setSessionConfigOption(params) {
|
|
1103
1286
|
return setConfigOption(requireSession(params.sessionId), params);
|
|
1104
1287
|
},
|
|
1105
1288
|
async prompt(params) {
|
|
1106
1289
|
assertOpen();
|
|
1107
|
-
const
|
|
1108
|
-
if (
|
|
1290
|
+
const record2 = requireSession(params.sessionId);
|
|
1291
|
+
if (record2.inflight)
|
|
1109
1292
|
throw invalidParams("a prompt is already in flight for this session");
|
|
1110
|
-
if (ctx.agents.get(
|
|
1293
|
+
if (ctx.agents.get(record2.agent.id) !== record2.agent) {
|
|
1111
1294
|
throw internalError("prompt was not queued: the agent was disposed outside the bridge");
|
|
1112
1295
|
}
|
|
1113
1296
|
const messageId = randomUUID();
|
|
@@ -1131,24 +1314,24 @@ function apply(ctx, rawConfig) {
|
|
|
1131
1314
|
cancelRequested: false,
|
|
1132
1315
|
settlementStarted: false
|
|
1133
1316
|
};
|
|
1134
|
-
|
|
1317
|
+
record2.inflight = inflight;
|
|
1135
1318
|
let admissionError;
|
|
1136
1319
|
try {
|
|
1137
|
-
const content = await admitAcpPrompt(params.prompt, llm,
|
|
1320
|
+
const content = await admitAcpPrompt(params.prompt, llm, record2.selection.current, attachments, imagePromptEnabled, admissionController.signal);
|
|
1138
1321
|
if (!inflight.cancelRequested) {
|
|
1139
|
-
if (ctx.agents.get(
|
|
1322
|
+
if (ctx.agents.get(record2.agent.id) !== record2.agent) {
|
|
1140
1323
|
throw internalError("prompt was not queued: the agent was disposed outside the bridge");
|
|
1141
1324
|
}
|
|
1142
1325
|
const message = createUserMessage(messageId, content);
|
|
1143
1326
|
inflight.messageId = messageId;
|
|
1144
1327
|
inflight.messageQueued = true;
|
|
1145
|
-
const wasStarted =
|
|
1328
|
+
const wasStarted = record2.started;
|
|
1146
1329
|
try {
|
|
1147
|
-
|
|
1148
|
-
|
|
1330
|
+
record2.agent.followup(message);
|
|
1331
|
+
record2.started = true;
|
|
1149
1332
|
} catch (error) {
|
|
1150
1333
|
inflight.messageQueued = false;
|
|
1151
|
-
|
|
1334
|
+
record2.started = wasStarted;
|
|
1152
1335
|
throw new Error(`prompt was not queued: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1153
1336
|
}
|
|
1154
1337
|
}
|
|
@@ -1158,45 +1341,47 @@ function apply(ctx, rawConfig) {
|
|
|
1158
1341
|
finishAdmission();
|
|
1159
1342
|
}
|
|
1160
1343
|
if (inflight.cancelRequested) {
|
|
1161
|
-
settleAfterQuiescence(
|
|
1344
|
+
settleAfterQuiescence(record2, inflight);
|
|
1162
1345
|
return { stopReason: await completion };
|
|
1163
1346
|
}
|
|
1164
1347
|
if (admissionError) {
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1348
|
+
if (record2.inflight === inflight)
|
|
1349
|
+
record2.inflight = void 0;
|
|
1167
1350
|
if (admissionError instanceof RequestError)
|
|
1168
1351
|
throw admissionError;
|
|
1169
1352
|
throw internalError(errorChain(admissionError));
|
|
1170
1353
|
}
|
|
1171
|
-
settleAfterQuiescence(
|
|
1354
|
+
settleAfterQuiescence(record2, inflight);
|
|
1172
1355
|
return { stopReason: await completion };
|
|
1173
1356
|
},
|
|
1174
1357
|
cancel(params) {
|
|
1175
|
-
const
|
|
1176
|
-
if (!
|
|
1358
|
+
const record2 = sessions.get(params.sessionId);
|
|
1359
|
+
if (!record2)
|
|
1177
1360
|
return Promise.resolve();
|
|
1178
|
-
|
|
1361
|
+
record2.questions.cancel();
|
|
1362
|
+
const inflight = record2.inflight;
|
|
1179
1363
|
if (inflight) {
|
|
1180
1364
|
inflight.cancelRequested = true;
|
|
1181
1365
|
inflight.admissionController.abort(new Error("ACP prompt cancelled"));
|
|
1182
|
-
settleAfterQuiescence(
|
|
1366
|
+
settleAfterQuiescence(record2, inflight);
|
|
1183
1367
|
}
|
|
1184
1368
|
if (!inflight || inflight.messageQueued)
|
|
1185
|
-
|
|
1369
|
+
record2.agent.cancel({ kind: "user" });
|
|
1186
1370
|
return Promise.resolve();
|
|
1187
1371
|
},
|
|
1188
1372
|
async closeSession(params) {
|
|
1189
|
-
const
|
|
1373
|
+
const record2 = requireSession(params.sessionId);
|
|
1374
|
+
record2.questions.cancel();
|
|
1190
1375
|
sessions.delete(params.sessionId);
|
|
1191
|
-
const inflight =
|
|
1376
|
+
const inflight = record2.inflight;
|
|
1192
1377
|
if (inflight) {
|
|
1193
1378
|
inflight.cancelRequested = true;
|
|
1194
1379
|
inflight.admissionController.abort(new Error("ACP session closed"));
|
|
1195
|
-
settleAfterQuiescence(
|
|
1380
|
+
settleAfterQuiescence(record2, inflight);
|
|
1196
1381
|
}
|
|
1197
1382
|
if (!inflight || inflight.messageQueued)
|
|
1198
|
-
|
|
1199
|
-
await disposeRecords([
|
|
1383
|
+
record2.agent.cancel({ kind: "user" });
|
|
1384
|
+
await disposeRecords([record2]);
|
|
1200
1385
|
}
|
|
1201
1386
|
};
|
|
1202
1387
|
};
|
|
@@ -1209,15 +1394,16 @@ function apply(ctx, rawConfig) {
|
|
|
1209
1394
|
closed = true;
|
|
1210
1395
|
const records = [...sessions.values()];
|
|
1211
1396
|
sessions.clear();
|
|
1212
|
-
for (const
|
|
1213
|
-
|
|
1397
|
+
for (const record2 of records) {
|
|
1398
|
+
record2.questions.cancel();
|
|
1399
|
+
const inflight = record2.inflight;
|
|
1214
1400
|
if (inflight) {
|
|
1215
1401
|
inflight.cancelRequested = true;
|
|
1216
1402
|
inflight.admissionController.abort(new Error("ACP bridge disposed"));
|
|
1217
|
-
settleAfterQuiescence(
|
|
1403
|
+
settleAfterQuiescence(record2, inflight);
|
|
1218
1404
|
}
|
|
1219
1405
|
if (!inflight || inflight.messageQueued)
|
|
1220
|
-
|
|
1406
|
+
record2.agent.cancel({ kind: "user" });
|
|
1221
1407
|
}
|
|
1222
1408
|
quiescing = (async () => {
|
|
1223
1409
|
await disposeRecords(records);
|