@openma/deepseek-harness-acp 0.4.26 → 0.4.28
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 +10 -1
- package/cordis.patch.yml +8 -0
- package/dist/bridge.js +118 -31
- package/dist/plugin.js +146 -39
- package/dist/server.js +118 -31
- package/package.json +57 -58
- package/vendor/dsh-runtime.tgz +0 -0
- package/vendor/runtime.json +2 -2
package/README.md
CHANGED
|
@@ -171,6 +171,15 @@ Optional wire behavior follows ACP's extension conventions:
|
|
|
171
171
|
extension must still get normal sessions, prompts, updates, cancellation,
|
|
172
172
|
auth, and config options.
|
|
173
173
|
|
|
174
|
+
#### DSH tool result values
|
|
175
|
+
|
|
176
|
+
The [ACP metadata registry][metadata-registry] is the authoritative list of
|
|
177
|
+
every `_meta` field this adapter reads or emits. It records direction, carrier,
|
|
178
|
+
shape, negotiation requirements, and replay behavior, including native DSH
|
|
179
|
+
tool values at `_meta.dsh.toolResult.value`.
|
|
180
|
+
|
|
181
|
+
[metadata-registry]: https://github.com/openma-ai/deepseek-harness-acp/blob/main/docs/metadata.md
|
|
182
|
+
|
|
174
183
|
The current package applies this pattern to the built-in `_dsh/cordis/*`
|
|
175
184
|
family used by the TUI for Client capability discovery, dynamic Package
|
|
176
185
|
lifecycle, and package-private Host/Client RPC. It is an explicit, versioned
|
|
@@ -217,7 +226,7 @@ another provider.
|
|
|
217
226
|
- **Live model catalog** — providers × models from the running composition (third-party providers added in the Web UI appear immediately), plus reasoning-effort selection that follows your product default.
|
|
218
227
|
- **Slash commands** — adapter built-ins (`/status`, `/model`) plus the harness command registry (`/compact`, `/goal`, `/permission`, `/plan`, …) executed without a model turn, plus **skills** (`/skill-name` — the harness's own invocation gesture). Login and logout are ACP methods, not chat commands.
|
|
219
228
|
- **Plans & usage** — `todo_write` snapshots as ACP plans; token accounting as `usage_update` and per-turn usage.
|
|
220
|
-
- **Sessions** — `session/load`
|
|
229
|
+
- **Sessions** — `session/resume` restores a durable session without replaying its transcript; `session/load` remains the full-history path. Also supports `session/list`, silent restore when a client prompts an old session after an agent restart, and titles as `session_info_update`. Multi-root sessions are not advertised: non-empty `additionalDirectories` on `session/new`, `session/resume`, or `session/load` return `Invalid params` until [dsh supports multiple workspace roots](https://github.com/deepseek-ai/deepseek-harness/discussions/2474).
|
|
221
230
|
- **MCP servers** — per-session `mcpServers` mount `@deepseek-ai/dsh-mcp-client` instances (stdio + streamable HTTP); tools join as `mcp__<server>__<tool>`; a failing server never takes the session down.
|
|
222
231
|
- **Real cancellation** — `session/cancel` interrupts the live turn through the harness agent.
|
|
223
232
|
|
package/cordis.patch.yml
CHANGED
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
- id: hmr
|
|
21
21
|
disabled: true
|
|
22
22
|
|
|
23
|
+
# Newer dsh versions seed the acp profile with their own ACP transport.
|
|
24
|
+
# This bundle owns stdout and must replace that transport.
|
|
25
|
+
- id: acp
|
|
26
|
+
disabled: true
|
|
27
|
+
|
|
28
|
+
- id: acp-app-startup
|
|
29
|
+
disabled: true
|
|
30
|
+
|
|
23
31
|
# Model-facing tool rows move off the host plane and into each agent preset's
|
|
24
32
|
# own composition (standard/code/minimal/cordis ship complete agent.cordis.yml
|
|
25
33
|
# files) — the same split the web-app bundle makes, and what gives "minimal"
|
package/dist/bridge.js
CHANGED
|
@@ -419,6 +419,28 @@ function diffsFromToolMeta(meta) {
|
|
|
419
419
|
}
|
|
420
420
|
return result;
|
|
421
421
|
}
|
|
422
|
+
function acknowledgementId(text, prefix) {
|
|
423
|
+
if (!text.startsWith(prefix)) return void 0;
|
|
424
|
+
const id = text.slice(prefix.length);
|
|
425
|
+
if (id.length === 0 || id.trim() !== id || id.includes(" ") || id.includes(" ") || id.includes("\n")) {
|
|
426
|
+
return void 0;
|
|
427
|
+
}
|
|
428
|
+
return id;
|
|
429
|
+
}
|
|
430
|
+
function typedToolAcknowledgement(state, text, failed) {
|
|
431
|
+
if (state === void 0 || failed) return void 0;
|
|
432
|
+
if (state.name === "bash" || state.name === "pwsh") {
|
|
433
|
+
const jobId = acknowledgementId(text, "started background job ");
|
|
434
|
+
return jobId === void 0 ? void 0 : { kind: "background", jobId };
|
|
435
|
+
}
|
|
436
|
+
if (state.name === "subagent") {
|
|
437
|
+
const jobId = acknowledgementId(text, "started background subagent job ");
|
|
438
|
+
if (jobId !== void 0) return { kind: "background", jobId };
|
|
439
|
+
const subagentId = acknowledgementId(text, "started subagent ");
|
|
440
|
+
if (subagentId !== void 0) return { kind: "continuable", subagentId };
|
|
441
|
+
}
|
|
442
|
+
return void 0;
|
|
443
|
+
}
|
|
422
444
|
function extractToolResult(data) {
|
|
423
445
|
let failed = data["error"] !== void 0 && data["error"] !== null;
|
|
424
446
|
const parts = [];
|
|
@@ -532,6 +554,8 @@ var SessionProjection = class {
|
|
|
532
554
|
title;
|
|
533
555
|
streamedText = /* @__PURE__ */ new Set();
|
|
534
556
|
streamedReasoning = /* @__PURE__ */ new Set();
|
|
557
|
+
/** Structured tool values observed live before DSH omits them from its durable event. */
|
|
558
|
+
toolResultValues = /* @__PURE__ */ new Map();
|
|
535
559
|
/** One authoritative state per open ACP tool call. Block indexes only
|
|
536
560
|
* route streaming fragments to that state; they never own lifecycle. */
|
|
537
561
|
toolCalls = /* @__PURE__ */ new Map();
|
|
@@ -551,6 +575,10 @@ var SessionProjection = class {
|
|
|
551
575
|
this.presentCall = options?.presentCall;
|
|
552
576
|
this.subagent = options?.subagent;
|
|
553
577
|
}
|
|
578
|
+
/** Stage one live `tools/result` value for the matching durable `tool/result` event. */
|
|
579
|
+
recordToolResult(callId, value) {
|
|
580
|
+
this.toolResultValues.set(callId, value);
|
|
581
|
+
}
|
|
554
582
|
/** Reset per-prompt accumulators; call when a new `session/prompt` starts. */
|
|
555
583
|
beginPrompt() {
|
|
556
584
|
this.window = emptyWindow();
|
|
@@ -941,6 +969,18 @@ var SessionProjection = class {
|
|
|
941
969
|
const state = this.toolCalls.get(callId);
|
|
942
970
|
const facts = state?.facts;
|
|
943
971
|
const { failed, text, diffs } = extractToolResult(data);
|
|
972
|
+
const hasLiveValue = this.toolResultValues.has(callId);
|
|
973
|
+
const liveValue = this.toolResultValues.get(callId);
|
|
974
|
+
this.toolResultValues.delete(callId);
|
|
975
|
+
const toolResultValue = hasLiveValue ? liveValue : typedToolAcknowledgement(state, text, failed);
|
|
976
|
+
const toolResultMeta = toolResultValue === void 0 ? void 0 : {
|
|
977
|
+
dsh: {
|
|
978
|
+
toolResult: {
|
|
979
|
+
value: toolResultValue
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
const rawOutput = text.length > 0 ? { output: text, isError: failed } : void 0;
|
|
944
984
|
const content = diffs.map((diff) => ({
|
|
945
985
|
type: "diff",
|
|
946
986
|
path: diff.path,
|
|
@@ -965,8 +1005,9 @@ var SessionProjection = class {
|
|
|
965
1005
|
sessionUpdate: "tool_call_update",
|
|
966
1006
|
toolCallId: callId,
|
|
967
1007
|
status: failed ? "failed" : "completed",
|
|
968
|
-
...
|
|
1008
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
969
1009
|
_meta: {
|
|
1010
|
+
...toolResultMeta ?? {},
|
|
970
1011
|
terminal_exit: { terminal_id: callId, exit_code: failed ? 1 : 0, signal: null }
|
|
971
1012
|
}
|
|
972
1013
|
}
|
|
@@ -992,7 +1033,8 @@ ${text.replace(/\n+$/, "")}
|
|
|
992
1033
|
toolCallId: callId,
|
|
993
1034
|
status: failed ? "failed" : "completed",
|
|
994
1035
|
...content.length > 0 ? { content } : {},
|
|
995
|
-
...
|
|
1036
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
1037
|
+
...toolResultMeta !== void 0 ? { _meta: toolResultMeta } : {}
|
|
996
1038
|
}
|
|
997
1039
|
];
|
|
998
1040
|
}
|
|
@@ -1009,11 +1051,28 @@ ${text.replace(/\n+$/, "")}
|
|
|
1009
1051
|
}
|
|
1010
1052
|
this.toolCallBlocks.clear();
|
|
1011
1053
|
this.toolCalls.clear();
|
|
1054
|
+
this.toolResultValues.clear();
|
|
1012
1055
|
return [];
|
|
1013
1056
|
}
|
|
1014
1057
|
};
|
|
1015
1058
|
|
|
1016
1059
|
// src/bridge/history.ts
|
|
1060
|
+
function buildResumeMetadata(events) {
|
|
1061
|
+
let title;
|
|
1062
|
+
let contextWindow;
|
|
1063
|
+
for (const event of events) {
|
|
1064
|
+
if (event.type === "session/title") {
|
|
1065
|
+
const value = event.data?.["title"];
|
|
1066
|
+
if (typeof value === "string" && value.length > 0) title = value;
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (event.type === "request/context") {
|
|
1070
|
+
const value = event.data?.["contextWindow"];
|
|
1071
|
+
if (typeof value === "number" && value > 0) contextWindow = value;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return { title, contextWindow };
|
|
1075
|
+
}
|
|
1017
1076
|
function userMessage(data) {
|
|
1018
1077
|
if (data === void 0) return void 0;
|
|
1019
1078
|
const source = data["source"];
|
|
@@ -1852,26 +1911,27 @@ function installAcpUserQuestionProvider(service, options) {
|
|
|
1852
1911
|
let router = routers.get(owner);
|
|
1853
1912
|
if (router === void 0) {
|
|
1854
1913
|
const routes = [];
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
);
|
|
1866
|
-
}
|
|
1867
|
-
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1914
|
+
const ask = async (request, next) => {
|
|
1915
|
+
for (let index = routes.length - 1; index >= 0; index -= 1) {
|
|
1916
|
+
const route = routes[index];
|
|
1917
|
+
const sessionId = route?.sessionIdForRequest(request);
|
|
1918
|
+
if (route === void 0 || sessionId === void 0) continue;
|
|
1919
|
+
if (!route.formSupported()) {
|
|
1920
|
+
throw new UserQuestionError(
|
|
1921
|
+
"the ACP client does not support form elicitation",
|
|
1922
|
+
"CLIENT_UNSUPPORTED"
|
|
1923
|
+
);
|
|
1868
1924
|
}
|
|
1869
|
-
|
|
1870
|
-
"ACP user interaction requires a live root session",
|
|
1871
|
-
"ASK_MISSING_AGENT"
|
|
1872
|
-
);
|
|
1925
|
+
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1873
1926
|
}
|
|
1874
|
-
|
|
1927
|
+
if (next !== void 0) return next();
|
|
1928
|
+
throw new UserQuestionError(
|
|
1929
|
+
"ACP user interaction requires a live root session",
|
|
1930
|
+
"ASK_MISSING_AGENT"
|
|
1931
|
+
);
|
|
1932
|
+
};
|
|
1933
|
+
const legacy = service;
|
|
1934
|
+
const disposeProvider = typeof legacy.registerProvider === "function" ? legacy.registerProvider({ ask }) : service.ctx.root.on("user-questions/request", ask);
|
|
1875
1935
|
router = { routes, disposeProvider };
|
|
1876
1936
|
routers.set(owner, router);
|
|
1877
1937
|
}
|
|
@@ -1914,6 +1974,9 @@ function presetDisplayName(preset) {
|
|
|
1914
1974
|
// src/bridge/index.ts
|
|
1915
1975
|
var name3 = "acp-bridge";
|
|
1916
1976
|
var inject3 = ["agents", "credentials", "llm", "agentDefaultModel", "sessionPersistence", "approval", "permissionPresets", "commands", "agentPresets", "skills", "subagents", "userQuestions"];
|
|
1977
|
+
function readSessionEvents(session) {
|
|
1978
|
+
return session.snapshotEvents?.() ?? session.events ?? [];
|
|
1979
|
+
}
|
|
1917
1980
|
function invalidParams(detail) {
|
|
1918
1981
|
return RequestError.invalidParams(void 0, detail);
|
|
1919
1982
|
}
|
|
@@ -2257,8 +2320,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2257
2320
|
} catch (error) {
|
|
2258
2321
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2259
2322
|
}
|
|
2260
|
-
|
|
2323
|
+
let restored;
|
|
2261
2324
|
if (options.replay) {
|
|
2325
|
+
const replay = buildReplay(events);
|
|
2326
|
+
restored = replay;
|
|
2262
2327
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2263
2328
|
if (replay.title !== void 0) {
|
|
2264
2329
|
notify(sessionId, {
|
|
@@ -2266,6 +2331,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2266
2331
|
title: replay.title
|
|
2267
2332
|
});
|
|
2268
2333
|
}
|
|
2334
|
+
} else {
|
|
2335
|
+
restored = buildResumeMetadata(events);
|
|
2269
2336
|
}
|
|
2270
2337
|
const presets = presetsService();
|
|
2271
2338
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2276,11 +2343,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2276
2343
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2277
2344
|
});
|
|
2278
2345
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2279
|
-
const projection = new SessionProjection(
|
|
2346
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2280
2347
|
terminalOutput: clientTerminalOutput,
|
|
2281
2348
|
...cwd !== void 0 ? { cwd } : {}
|
|
2282
2349
|
});
|
|
2283
|
-
projection.title =
|
|
2350
|
+
projection.title = restored.title;
|
|
2284
2351
|
const record = registerRecord(
|
|
2285
2352
|
sessionId,
|
|
2286
2353
|
handle.agent,
|
|
@@ -2291,8 +2358,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2291
2358
|
);
|
|
2292
2359
|
if (presetId !== void 0) record.preset = presetId;
|
|
2293
2360
|
{
|
|
2294
|
-
const
|
|
2295
|
-
const storedMode = permission.current(events);
|
|
2361
|
+
const storedMode = currentPermission(record);
|
|
2296
2362
|
if (storedMode !== void 0) record.modeId = storedMode;
|
|
2297
2363
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2298
2364
|
const event = events[index];
|
|
@@ -2487,6 +2553,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2487
2553
|
record.approvals = policy;
|
|
2488
2554
|
};
|
|
2489
2555
|
const permissionService = () => permissionPresets;
|
|
2556
|
+
const currentPermission = (record) => {
|
|
2557
|
+
const session = record.agent.session;
|
|
2558
|
+
return permissionService().current(typeof session.snapshotEvents === "function" ? session : readSessionEvents(session));
|
|
2559
|
+
};
|
|
2490
2560
|
const permissionPresentation = (id) => {
|
|
2491
2561
|
const spec = permissionService().resolve(id);
|
|
2492
2562
|
const stock = MODE_LABELS[id];
|
|
@@ -2572,7 +2642,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2572
2642
|
});
|
|
2573
2643
|
}
|
|
2574
2644
|
if (commandRuntime.list(record.agent).some((command) => command.name === "plan")) {
|
|
2575
|
-
const active = [...record.agent.session
|
|
2645
|
+
const active = [...readSessionEvents(record.agent.session)].reverse().map((event) => event).find((event) => event.type === "plan/mode")?.data?.["active"] === true;
|
|
2576
2646
|
result.push({
|
|
2577
2647
|
type: "select",
|
|
2578
2648
|
id: "collaboration_mode",
|
|
@@ -2740,10 +2810,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2740
2810
|
});
|
|
2741
2811
|
};
|
|
2742
2812
|
const syncPermissionState = (record, sessionId) => {
|
|
2743
|
-
const events = record.agent.session
|
|
2744
|
-
|
|
2745
|
-
const permission = permissionService();
|
|
2746
|
-
const storedMode = permission.current(events);
|
|
2813
|
+
const events = readSessionEvents(record.agent.session);
|
|
2814
|
+
const storedMode = currentPermission(record);
|
|
2747
2815
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2748
2816
|
const event = events[index];
|
|
2749
2817
|
if (event?.type === "approval/policy") {
|
|
@@ -2874,6 +2942,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2874
2942
|
watchSubagentParent(agent);
|
|
2875
2943
|
return record;
|
|
2876
2944
|
};
|
|
2945
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2946
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2947
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2948
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2949
|
+
});
|
|
2877
2950
|
ctx.on("session/event", (session, event) => {
|
|
2878
2951
|
const sessionId = String(session.header.id);
|
|
2879
2952
|
const record = sessions.get(sessionId);
|
|
@@ -2961,7 +3034,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2961
3034
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2962
3035
|
mcpCapabilities: { http: true, sse: false },
|
|
2963
3036
|
auth: { logout: {} },
|
|
2964
|
-
sessionCapabilities: { list: {} }
|
|
3037
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2965
3038
|
},
|
|
2966
3039
|
authMethods: advertisedAuthMethods(
|
|
2967
3040
|
providers,
|
|
@@ -3076,6 +3149,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3076
3149
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3077
3150
|
};
|
|
3078
3151
|
},
|
|
3152
|
+
async resumeSession(params) {
|
|
3153
|
+
assertOpen();
|
|
3154
|
+
await requireCredential(config.provider);
|
|
3155
|
+
validateCwd(params.cwd);
|
|
3156
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3157
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3158
|
+
requirePersistence();
|
|
3159
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3160
|
+
const options = await configOptions(record);
|
|
3161
|
+
return {
|
|
3162
|
+
modes: modeState(record),
|
|
3163
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3164
|
+
};
|
|
3165
|
+
},
|
|
3079
3166
|
async listSessions(params) {
|
|
3080
3167
|
assertOpen();
|
|
3081
3168
|
const persistence = requirePersistence();
|
package/dist/plugin.js
CHANGED
|
@@ -421,6 +421,28 @@ function diffsFromToolMeta(meta) {
|
|
|
421
421
|
}
|
|
422
422
|
return result;
|
|
423
423
|
}
|
|
424
|
+
function acknowledgementId(text, prefix) {
|
|
425
|
+
if (!text.startsWith(prefix)) return void 0;
|
|
426
|
+
const id = text.slice(prefix.length);
|
|
427
|
+
if (id.length === 0 || id.trim() !== id || id.includes(" ") || id.includes(" ") || id.includes("\n")) {
|
|
428
|
+
return void 0;
|
|
429
|
+
}
|
|
430
|
+
return id;
|
|
431
|
+
}
|
|
432
|
+
function typedToolAcknowledgement(state, text, failed) {
|
|
433
|
+
if (state === void 0 || failed) return void 0;
|
|
434
|
+
if (state.name === "bash" || state.name === "pwsh") {
|
|
435
|
+
const jobId = acknowledgementId(text, "started background job ");
|
|
436
|
+
return jobId === void 0 ? void 0 : { kind: "background", jobId };
|
|
437
|
+
}
|
|
438
|
+
if (state.name === "subagent") {
|
|
439
|
+
const jobId = acknowledgementId(text, "started background subagent job ");
|
|
440
|
+
if (jobId !== void 0) return { kind: "background", jobId };
|
|
441
|
+
const subagentId = acknowledgementId(text, "started subagent ");
|
|
442
|
+
if (subagentId !== void 0) return { kind: "continuable", subagentId };
|
|
443
|
+
}
|
|
444
|
+
return void 0;
|
|
445
|
+
}
|
|
424
446
|
function extractToolResult(data) {
|
|
425
447
|
let failed = data["error"] !== void 0 && data["error"] !== null;
|
|
426
448
|
const parts = [];
|
|
@@ -534,6 +556,8 @@ var SessionProjection = class {
|
|
|
534
556
|
title;
|
|
535
557
|
streamedText = /* @__PURE__ */ new Set();
|
|
536
558
|
streamedReasoning = /* @__PURE__ */ new Set();
|
|
559
|
+
/** Structured tool values observed live before DSH omits them from its durable event. */
|
|
560
|
+
toolResultValues = /* @__PURE__ */ new Map();
|
|
537
561
|
/** One authoritative state per open ACP tool call. Block indexes only
|
|
538
562
|
* route streaming fragments to that state; they never own lifecycle. */
|
|
539
563
|
toolCalls = /* @__PURE__ */ new Map();
|
|
@@ -553,6 +577,10 @@ var SessionProjection = class {
|
|
|
553
577
|
this.presentCall = options?.presentCall;
|
|
554
578
|
this.subagent = options?.subagent;
|
|
555
579
|
}
|
|
580
|
+
/** Stage one live `tools/result` value for the matching durable `tool/result` event. */
|
|
581
|
+
recordToolResult(callId, value) {
|
|
582
|
+
this.toolResultValues.set(callId, value);
|
|
583
|
+
}
|
|
556
584
|
/** Reset per-prompt accumulators; call when a new `session/prompt` starts. */
|
|
557
585
|
beginPrompt() {
|
|
558
586
|
this.window = emptyWindow();
|
|
@@ -943,6 +971,18 @@ var SessionProjection = class {
|
|
|
943
971
|
const state = this.toolCalls.get(callId);
|
|
944
972
|
const facts = state?.facts;
|
|
945
973
|
const { failed, text, diffs } = extractToolResult(data);
|
|
974
|
+
const hasLiveValue = this.toolResultValues.has(callId);
|
|
975
|
+
const liveValue = this.toolResultValues.get(callId);
|
|
976
|
+
this.toolResultValues.delete(callId);
|
|
977
|
+
const toolResultValue = hasLiveValue ? liveValue : typedToolAcknowledgement(state, text, failed);
|
|
978
|
+
const toolResultMeta = toolResultValue === void 0 ? void 0 : {
|
|
979
|
+
dsh: {
|
|
980
|
+
toolResult: {
|
|
981
|
+
value: toolResultValue
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
const rawOutput = text.length > 0 ? { output: text, isError: failed } : void 0;
|
|
946
986
|
const content = diffs.map((diff) => ({
|
|
947
987
|
type: "diff",
|
|
948
988
|
path: diff.path,
|
|
@@ -967,8 +1007,9 @@ var SessionProjection = class {
|
|
|
967
1007
|
sessionUpdate: "tool_call_update",
|
|
968
1008
|
toolCallId: callId,
|
|
969
1009
|
status: failed ? "failed" : "completed",
|
|
970
|
-
...
|
|
1010
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
971
1011
|
_meta: {
|
|
1012
|
+
...toolResultMeta ?? {},
|
|
972
1013
|
terminal_exit: { terminal_id: callId, exit_code: failed ? 1 : 0, signal: null }
|
|
973
1014
|
}
|
|
974
1015
|
}
|
|
@@ -994,7 +1035,8 @@ ${text.replace(/\n+$/, "")}
|
|
|
994
1035
|
toolCallId: callId,
|
|
995
1036
|
status: failed ? "failed" : "completed",
|
|
996
1037
|
...content.length > 0 ? { content } : {},
|
|
997
|
-
...
|
|
1038
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
1039
|
+
...toolResultMeta !== void 0 ? { _meta: toolResultMeta } : {}
|
|
998
1040
|
}
|
|
999
1041
|
];
|
|
1000
1042
|
}
|
|
@@ -1011,11 +1053,28 @@ ${text.replace(/\n+$/, "")}
|
|
|
1011
1053
|
}
|
|
1012
1054
|
this.toolCallBlocks.clear();
|
|
1013
1055
|
this.toolCalls.clear();
|
|
1056
|
+
this.toolResultValues.clear();
|
|
1014
1057
|
return [];
|
|
1015
1058
|
}
|
|
1016
1059
|
};
|
|
1017
1060
|
|
|
1018
1061
|
// src/bridge/history.ts
|
|
1062
|
+
function buildResumeMetadata(events) {
|
|
1063
|
+
let title;
|
|
1064
|
+
let contextWindow;
|
|
1065
|
+
for (const event of events) {
|
|
1066
|
+
if (event.type === "session/title") {
|
|
1067
|
+
const value = event.data?.["title"];
|
|
1068
|
+
if (typeof value === "string" && value.length > 0) title = value;
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
if (event.type === "request/context") {
|
|
1072
|
+
const value = event.data?.["contextWindow"];
|
|
1073
|
+
if (typeof value === "number" && value > 0) contextWindow = value;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return { title, contextWindow };
|
|
1077
|
+
}
|
|
1019
1078
|
function userMessage(data) {
|
|
1020
1079
|
if (data === void 0) return void 0;
|
|
1021
1080
|
const source = data["source"];
|
|
@@ -1854,26 +1913,27 @@ function installAcpUserQuestionProvider(service, options) {
|
|
|
1854
1913
|
let router = routers.get(owner);
|
|
1855
1914
|
if (router === void 0) {
|
|
1856
1915
|
const routes = [];
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
);
|
|
1868
|
-
}
|
|
1869
|
-
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1916
|
+
const ask = async (request, next) => {
|
|
1917
|
+
for (let index = routes.length - 1; index >= 0; index -= 1) {
|
|
1918
|
+
const route = routes[index];
|
|
1919
|
+
const sessionId = route?.sessionIdForRequest(request);
|
|
1920
|
+
if (route === void 0 || sessionId === void 0) continue;
|
|
1921
|
+
if (!route.formSupported()) {
|
|
1922
|
+
throw new UserQuestionError(
|
|
1923
|
+
"the ACP client does not support form elicitation",
|
|
1924
|
+
"CLIENT_UNSUPPORTED"
|
|
1925
|
+
);
|
|
1870
1926
|
}
|
|
1871
|
-
|
|
1872
|
-
"ACP user interaction requires a live root session",
|
|
1873
|
-
"ASK_MISSING_AGENT"
|
|
1874
|
-
);
|
|
1927
|
+
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1875
1928
|
}
|
|
1876
|
-
|
|
1929
|
+
if (next !== void 0) return next();
|
|
1930
|
+
throw new UserQuestionError(
|
|
1931
|
+
"ACP user interaction requires a live root session",
|
|
1932
|
+
"ASK_MISSING_AGENT"
|
|
1933
|
+
);
|
|
1934
|
+
};
|
|
1935
|
+
const legacy = service;
|
|
1936
|
+
const disposeProvider = typeof legacy.registerProvider === "function" ? legacy.registerProvider({ ask }) : service.ctx.root.on("user-questions/request", ask);
|
|
1877
1937
|
router = { routes, disposeProvider };
|
|
1878
1938
|
routers.set(owner, router);
|
|
1879
1939
|
}
|
|
@@ -1916,6 +1976,9 @@ function presetDisplayName(preset) {
|
|
|
1916
1976
|
// src/bridge/index.ts
|
|
1917
1977
|
var name3 = "acp-bridge";
|
|
1918
1978
|
var inject3 = ["agents", "credentials", "llm", "agentDefaultModel", "sessionPersistence", "approval", "permissionPresets", "commands", "agentPresets", "skills", "subagents", "userQuestions"];
|
|
1979
|
+
function readSessionEvents(session) {
|
|
1980
|
+
return session.snapshotEvents?.() ?? session.events ?? [];
|
|
1981
|
+
}
|
|
1919
1982
|
function invalidParams(detail) {
|
|
1920
1983
|
return RequestError.invalidParams(void 0, detail);
|
|
1921
1984
|
}
|
|
@@ -2259,8 +2322,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2259
2322
|
} catch (error) {
|
|
2260
2323
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2261
2324
|
}
|
|
2262
|
-
|
|
2325
|
+
let restored;
|
|
2263
2326
|
if (options.replay) {
|
|
2327
|
+
const replay = buildReplay(events);
|
|
2328
|
+
restored = replay;
|
|
2264
2329
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2265
2330
|
if (replay.title !== void 0) {
|
|
2266
2331
|
notify(sessionId, {
|
|
@@ -2268,6 +2333,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2268
2333
|
title: replay.title
|
|
2269
2334
|
});
|
|
2270
2335
|
}
|
|
2336
|
+
} else {
|
|
2337
|
+
restored = buildResumeMetadata(events);
|
|
2271
2338
|
}
|
|
2272
2339
|
const presets = presetsService();
|
|
2273
2340
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2278,11 +2345,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2278
2345
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2279
2346
|
});
|
|
2280
2347
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2281
|
-
const projection = new SessionProjection(
|
|
2348
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2282
2349
|
terminalOutput: clientTerminalOutput,
|
|
2283
2350
|
...cwd !== void 0 ? { cwd } : {}
|
|
2284
2351
|
});
|
|
2285
|
-
projection.title =
|
|
2352
|
+
projection.title = restored.title;
|
|
2286
2353
|
const record = registerRecord(
|
|
2287
2354
|
sessionId,
|
|
2288
2355
|
handle.agent,
|
|
@@ -2293,8 +2360,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2293
2360
|
);
|
|
2294
2361
|
if (presetId !== void 0) record.preset = presetId;
|
|
2295
2362
|
{
|
|
2296
|
-
const
|
|
2297
|
-
const storedMode = permission.current(events);
|
|
2363
|
+
const storedMode = currentPermission(record);
|
|
2298
2364
|
if (storedMode !== void 0) record.modeId = storedMode;
|
|
2299
2365
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2300
2366
|
const event = events[index];
|
|
@@ -2489,6 +2555,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2489
2555
|
record.approvals = policy;
|
|
2490
2556
|
};
|
|
2491
2557
|
const permissionService = () => permissionPresets;
|
|
2558
|
+
const currentPermission = (record) => {
|
|
2559
|
+
const session = record.agent.session;
|
|
2560
|
+
return permissionService().current(typeof session.snapshotEvents === "function" ? session : readSessionEvents(session));
|
|
2561
|
+
};
|
|
2492
2562
|
const permissionPresentation = (id) => {
|
|
2493
2563
|
const spec = permissionService().resolve(id);
|
|
2494
2564
|
const stock = MODE_LABELS[id];
|
|
@@ -2574,7 +2644,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2574
2644
|
});
|
|
2575
2645
|
}
|
|
2576
2646
|
if (commandRuntime.list(record.agent).some((command) => command.name === "plan")) {
|
|
2577
|
-
const active = [...record.agent.session
|
|
2647
|
+
const active = [...readSessionEvents(record.agent.session)].reverse().map((event) => event).find((event) => event.type === "plan/mode")?.data?.["active"] === true;
|
|
2578
2648
|
result.push({
|
|
2579
2649
|
type: "select",
|
|
2580
2650
|
id: "collaboration_mode",
|
|
@@ -2742,10 +2812,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2742
2812
|
});
|
|
2743
2813
|
};
|
|
2744
2814
|
const syncPermissionState = (record, sessionId) => {
|
|
2745
|
-
const events = record.agent.session
|
|
2746
|
-
|
|
2747
|
-
const permission = permissionService();
|
|
2748
|
-
const storedMode = permission.current(events);
|
|
2815
|
+
const events = readSessionEvents(record.agent.session);
|
|
2816
|
+
const storedMode = currentPermission(record);
|
|
2749
2817
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2750
2818
|
const event = events[index];
|
|
2751
2819
|
if (event?.type === "approval/policy") {
|
|
@@ -2876,6 +2944,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2876
2944
|
watchSubagentParent(agent);
|
|
2877
2945
|
return record;
|
|
2878
2946
|
};
|
|
2947
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2948
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2949
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2950
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2951
|
+
});
|
|
2879
2952
|
ctx.on("session/event", (session, event) => {
|
|
2880
2953
|
const sessionId = String(session.header.id);
|
|
2881
2954
|
const record = sessions.get(sessionId);
|
|
@@ -2963,7 +3036,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2963
3036
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2964
3037
|
mcpCapabilities: { http: true, sse: false },
|
|
2965
3038
|
auth: { logout: {} },
|
|
2966
|
-
sessionCapabilities: { list: {} }
|
|
3039
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2967
3040
|
},
|
|
2968
3041
|
authMethods: advertisedAuthMethods(
|
|
2969
3042
|
providers,
|
|
@@ -3078,6 +3151,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3078
3151
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3079
3152
|
};
|
|
3080
3153
|
},
|
|
3154
|
+
async resumeSession(params) {
|
|
3155
|
+
assertOpen();
|
|
3156
|
+
await requireCredential(config.provider);
|
|
3157
|
+
validateCwd(params.cwd);
|
|
3158
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3159
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3160
|
+
requirePersistence();
|
|
3161
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3162
|
+
const options = await configOptions(record);
|
|
3163
|
+
return {
|
|
3164
|
+
modes: modeState(record),
|
|
3165
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3166
|
+
};
|
|
3167
|
+
},
|
|
3081
3168
|
async listSessions(params) {
|
|
3082
3169
|
assertOpen();
|
|
3083
3170
|
const persistence = requirePersistence();
|
|
@@ -3487,39 +3574,59 @@ function resolvedHostModule(ctx, specifier) {
|
|
|
3487
3574
|
for (const anchor of anchors) {
|
|
3488
3575
|
try {
|
|
3489
3576
|
return pathToFileURL(createRequire2(anchor).resolve(specifier)).href;
|
|
3490
|
-
} catch {
|
|
3577
|
+
} catch (error) {
|
|
3578
|
+
if (error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return specifier;
|
|
3491
3579
|
}
|
|
3492
3580
|
}
|
|
3493
3581
|
return specifier;
|
|
3494
3582
|
}
|
|
3495
|
-
function
|
|
3583
|
+
function shippedPresetRoots(ctx) {
|
|
3496
3584
|
const anchors = [ctx.baseUrl, import.meta.url].filter((value) => typeof value === "string");
|
|
3497
3585
|
for (const anchor of anchors) {
|
|
3498
3586
|
try {
|
|
3499
3587
|
const manifest = createRequire2(anchor).resolve("@deepseek-ai/dsh/package.json");
|
|
3500
3588
|
const root = join(dirname(manifest), "config", "agent-presets");
|
|
3501
|
-
if (existsSync(root)) return root;
|
|
3589
|
+
if (existsSync(root)) return [{ path: root, trust: "system" }];
|
|
3590
|
+
} catch {
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
for (const anchor of anchors) {
|
|
3594
|
+
try {
|
|
3595
|
+
const manifest = createRequire2(anchor).resolve("@deepseek-ai/dsh-agent-presets/package.json");
|
|
3596
|
+
if (existsSync(join(dirname(manifest), "presets"))) return [];
|
|
3502
3597
|
} catch {
|
|
3503
3598
|
}
|
|
3504
3599
|
}
|
|
3505
|
-
throw new Error(
|
|
3600
|
+
throw new Error(
|
|
3601
|
+
"dsh-acp-plugin: cannot resolve the dsh shipped agent presets (looked for @deepseek-ai/dsh/config/agent-presets and a self-shipping @deepseek-ai/dsh-agent-presets)"
|
|
3602
|
+
);
|
|
3506
3603
|
}
|
|
3507
3604
|
async function mountService(ctx, service, specifier, config) {
|
|
3508
3605
|
if (ctx.get(service) !== void 0) return;
|
|
3606
|
+
const resolved = typeof config === "function" ? config() : config;
|
|
3509
3607
|
const exports = await ctx.loader.import(resolvedHostModule(ctx, specifier));
|
|
3510
3608
|
const plugin = ctx.loader.unwrapExports(exports);
|
|
3511
|
-
await ctx.plugin(plugin,
|
|
3609
|
+
await ctx.plugin(plugin, resolved);
|
|
3512
3610
|
if (ctx.get(service) === void 0) {
|
|
3513
3611
|
throw new Error(`dsh-acp-plugin: ${specifier} did not provide ${service}`);
|
|
3514
3612
|
}
|
|
3515
3613
|
}
|
|
3516
3614
|
async function apply5(ctx, config = {}) {
|
|
3517
3615
|
if (ctx.get("acpServer") !== void 0) return;
|
|
3518
|
-
await mountService(ctx, "agentPresets", "@deepseek-ai/dsh-agent-presets", {
|
|
3519
|
-
|
|
3520
|
-
|
|
3616
|
+
await mountService(ctx, "agentPresets", "@deepseek-ai/dsh-agent-presets", () => {
|
|
3617
|
+
const roots = shippedPresetRoots(ctx);
|
|
3618
|
+
return {
|
|
3619
|
+
default: "standard",
|
|
3620
|
+
// dsh 0.1.2's roster self-ships its presets (includeShippedRoot);
|
|
3621
|
+
// passing the legacy root there would duplicate the roster.
|
|
3622
|
+
...roots.length > 0 ? { roots } : {}
|
|
3623
|
+
};
|
|
3521
3624
|
});
|
|
3522
3625
|
await mountService(ctx, "dynamicCordisRunner", "@deepseek-ai/dsh-cordis-host-runner");
|
|
3626
|
+
const modelSettings = "@deepseek-ai/dsh-tool-subagent/model-selection-settings";
|
|
3627
|
+
if (ctx.get("subagentModelSelection") === void 0 && resolvedHostModule(ctx, modelSettings) !== modelSettings) {
|
|
3628
|
+
await mountService(ctx, "subagentModelSelection", modelSettings);
|
|
3629
|
+
}
|
|
3523
3630
|
await ctx.plugin(server_exports, config);
|
|
3524
3631
|
if (ctx.get("acpServer") === void 0) {
|
|
3525
3632
|
throw new Error("dsh-acp-plugin: ACP server did not activate");
|
package/dist/server.js
CHANGED
|
@@ -415,6 +415,28 @@ function diffsFromToolMeta(meta) {
|
|
|
415
415
|
}
|
|
416
416
|
return result;
|
|
417
417
|
}
|
|
418
|
+
function acknowledgementId(text, prefix) {
|
|
419
|
+
if (!text.startsWith(prefix)) return void 0;
|
|
420
|
+
const id = text.slice(prefix.length);
|
|
421
|
+
if (id.length === 0 || id.trim() !== id || id.includes(" ") || id.includes(" ") || id.includes("\n")) {
|
|
422
|
+
return void 0;
|
|
423
|
+
}
|
|
424
|
+
return id;
|
|
425
|
+
}
|
|
426
|
+
function typedToolAcknowledgement(state, text, failed) {
|
|
427
|
+
if (state === void 0 || failed) return void 0;
|
|
428
|
+
if (state.name === "bash" || state.name === "pwsh") {
|
|
429
|
+
const jobId = acknowledgementId(text, "started background job ");
|
|
430
|
+
return jobId === void 0 ? void 0 : { kind: "background", jobId };
|
|
431
|
+
}
|
|
432
|
+
if (state.name === "subagent") {
|
|
433
|
+
const jobId = acknowledgementId(text, "started background subagent job ");
|
|
434
|
+
if (jobId !== void 0) return { kind: "background", jobId };
|
|
435
|
+
const subagentId = acknowledgementId(text, "started subagent ");
|
|
436
|
+
if (subagentId !== void 0) return { kind: "continuable", subagentId };
|
|
437
|
+
}
|
|
438
|
+
return void 0;
|
|
439
|
+
}
|
|
418
440
|
function extractToolResult(data) {
|
|
419
441
|
let failed = data["error"] !== void 0 && data["error"] !== null;
|
|
420
442
|
const parts = [];
|
|
@@ -528,6 +550,8 @@ var SessionProjection = class {
|
|
|
528
550
|
title;
|
|
529
551
|
streamedText = /* @__PURE__ */ new Set();
|
|
530
552
|
streamedReasoning = /* @__PURE__ */ new Set();
|
|
553
|
+
/** Structured tool values observed live before DSH omits them from its durable event. */
|
|
554
|
+
toolResultValues = /* @__PURE__ */ new Map();
|
|
531
555
|
/** One authoritative state per open ACP tool call. Block indexes only
|
|
532
556
|
* route streaming fragments to that state; they never own lifecycle. */
|
|
533
557
|
toolCalls = /* @__PURE__ */ new Map();
|
|
@@ -547,6 +571,10 @@ var SessionProjection = class {
|
|
|
547
571
|
this.presentCall = options?.presentCall;
|
|
548
572
|
this.subagent = options?.subagent;
|
|
549
573
|
}
|
|
574
|
+
/** Stage one live `tools/result` value for the matching durable `tool/result` event. */
|
|
575
|
+
recordToolResult(callId, value) {
|
|
576
|
+
this.toolResultValues.set(callId, value);
|
|
577
|
+
}
|
|
550
578
|
/** Reset per-prompt accumulators; call when a new `session/prompt` starts. */
|
|
551
579
|
beginPrompt() {
|
|
552
580
|
this.window = emptyWindow();
|
|
@@ -937,6 +965,18 @@ var SessionProjection = class {
|
|
|
937
965
|
const state = this.toolCalls.get(callId);
|
|
938
966
|
const facts = state?.facts;
|
|
939
967
|
const { failed, text, diffs } = extractToolResult(data);
|
|
968
|
+
const hasLiveValue = this.toolResultValues.has(callId);
|
|
969
|
+
const liveValue = this.toolResultValues.get(callId);
|
|
970
|
+
this.toolResultValues.delete(callId);
|
|
971
|
+
const toolResultValue = hasLiveValue ? liveValue : typedToolAcknowledgement(state, text, failed);
|
|
972
|
+
const toolResultMeta = toolResultValue === void 0 ? void 0 : {
|
|
973
|
+
dsh: {
|
|
974
|
+
toolResult: {
|
|
975
|
+
value: toolResultValue
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
const rawOutput = text.length > 0 ? { output: text, isError: failed } : void 0;
|
|
940
980
|
const content = diffs.map((diff) => ({
|
|
941
981
|
type: "diff",
|
|
942
982
|
path: diff.path,
|
|
@@ -961,8 +1001,9 @@ var SessionProjection = class {
|
|
|
961
1001
|
sessionUpdate: "tool_call_update",
|
|
962
1002
|
toolCallId: callId,
|
|
963
1003
|
status: failed ? "failed" : "completed",
|
|
964
|
-
...
|
|
1004
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
965
1005
|
_meta: {
|
|
1006
|
+
...toolResultMeta ?? {},
|
|
966
1007
|
terminal_exit: { terminal_id: callId, exit_code: failed ? 1 : 0, signal: null }
|
|
967
1008
|
}
|
|
968
1009
|
}
|
|
@@ -988,7 +1029,8 @@ ${text.replace(/\n+$/, "")}
|
|
|
988
1029
|
toolCallId: callId,
|
|
989
1030
|
status: failed ? "failed" : "completed",
|
|
990
1031
|
...content.length > 0 ? { content } : {},
|
|
991
|
-
...
|
|
1032
|
+
...rawOutput !== void 0 ? { rawOutput } : {},
|
|
1033
|
+
...toolResultMeta !== void 0 ? { _meta: toolResultMeta } : {}
|
|
992
1034
|
}
|
|
993
1035
|
];
|
|
994
1036
|
}
|
|
@@ -1005,11 +1047,28 @@ ${text.replace(/\n+$/, "")}
|
|
|
1005
1047
|
}
|
|
1006
1048
|
this.toolCallBlocks.clear();
|
|
1007
1049
|
this.toolCalls.clear();
|
|
1050
|
+
this.toolResultValues.clear();
|
|
1008
1051
|
return [];
|
|
1009
1052
|
}
|
|
1010
1053
|
};
|
|
1011
1054
|
|
|
1012
1055
|
// src/bridge/history.ts
|
|
1056
|
+
function buildResumeMetadata(events) {
|
|
1057
|
+
let title;
|
|
1058
|
+
let contextWindow;
|
|
1059
|
+
for (const event of events) {
|
|
1060
|
+
if (event.type === "session/title") {
|
|
1061
|
+
const value = event.data?.["title"];
|
|
1062
|
+
if (typeof value === "string" && value.length > 0) title = value;
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
if (event.type === "request/context") {
|
|
1066
|
+
const value = event.data?.["contextWindow"];
|
|
1067
|
+
if (typeof value === "number" && value > 0) contextWindow = value;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return { title, contextWindow };
|
|
1071
|
+
}
|
|
1013
1072
|
function userMessage(data) {
|
|
1014
1073
|
if (data === void 0) return void 0;
|
|
1015
1074
|
const source = data["source"];
|
|
@@ -1848,26 +1907,27 @@ function installAcpUserQuestionProvider(service, options) {
|
|
|
1848
1907
|
let router = routers.get(owner);
|
|
1849
1908
|
if (router === void 0) {
|
|
1850
1909
|
const routes = [];
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
);
|
|
1862
|
-
}
|
|
1863
|
-
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1910
|
+
const ask = async (request, next) => {
|
|
1911
|
+
for (let index = routes.length - 1; index >= 0; index -= 1) {
|
|
1912
|
+
const route = routes[index];
|
|
1913
|
+
const sessionId = route?.sessionIdForRequest(request);
|
|
1914
|
+
if (route === void 0 || sessionId === void 0) continue;
|
|
1915
|
+
if (!route.formSupported()) {
|
|
1916
|
+
throw new UserQuestionError(
|
|
1917
|
+
"the ACP client does not support form elicitation",
|
|
1918
|
+
"CLIENT_UNSUPPORTED"
|
|
1919
|
+
);
|
|
1864
1920
|
}
|
|
1865
|
-
|
|
1866
|
-
"ACP user interaction requires a live root session",
|
|
1867
|
-
"ASK_MISSING_AGENT"
|
|
1868
|
-
);
|
|
1921
|
+
return askUserQuestionsOverAcp(request, sessionId, route.create);
|
|
1869
1922
|
}
|
|
1870
|
-
|
|
1923
|
+
if (next !== void 0) return next();
|
|
1924
|
+
throw new UserQuestionError(
|
|
1925
|
+
"ACP user interaction requires a live root session",
|
|
1926
|
+
"ASK_MISSING_AGENT"
|
|
1927
|
+
);
|
|
1928
|
+
};
|
|
1929
|
+
const legacy = service;
|
|
1930
|
+
const disposeProvider = typeof legacy.registerProvider === "function" ? legacy.registerProvider({ ask }) : service.ctx.root.on("user-questions/request", ask);
|
|
1871
1931
|
router = { routes, disposeProvider };
|
|
1872
1932
|
routers.set(owner, router);
|
|
1873
1933
|
}
|
|
@@ -1910,6 +1970,9 @@ function presetDisplayName(preset) {
|
|
|
1910
1970
|
// src/bridge/index.ts
|
|
1911
1971
|
var name3 = "acp-bridge";
|
|
1912
1972
|
var inject3 = ["agents", "credentials", "llm", "agentDefaultModel", "sessionPersistence", "approval", "permissionPresets", "commands", "agentPresets", "skills", "subagents", "userQuestions"];
|
|
1973
|
+
function readSessionEvents(session) {
|
|
1974
|
+
return session.snapshotEvents?.() ?? session.events ?? [];
|
|
1975
|
+
}
|
|
1913
1976
|
function invalidParams(detail) {
|
|
1914
1977
|
return RequestError.invalidParams(void 0, detail);
|
|
1915
1978
|
}
|
|
@@ -2253,8 +2316,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2253
2316
|
} catch (error) {
|
|
2254
2317
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2255
2318
|
}
|
|
2256
|
-
|
|
2319
|
+
let restored;
|
|
2257
2320
|
if (options.replay) {
|
|
2321
|
+
const replay = buildReplay(events);
|
|
2322
|
+
restored = replay;
|
|
2258
2323
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2259
2324
|
if (replay.title !== void 0) {
|
|
2260
2325
|
notify(sessionId, {
|
|
@@ -2262,6 +2327,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2262
2327
|
title: replay.title
|
|
2263
2328
|
});
|
|
2264
2329
|
}
|
|
2330
|
+
} else {
|
|
2331
|
+
restored = buildResumeMetadata(events);
|
|
2265
2332
|
}
|
|
2266
2333
|
const presets = presetsService();
|
|
2267
2334
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2272,11 +2339,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2272
2339
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2273
2340
|
});
|
|
2274
2341
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2275
|
-
const projection = new SessionProjection(
|
|
2342
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2276
2343
|
terminalOutput: clientTerminalOutput,
|
|
2277
2344
|
...cwd !== void 0 ? { cwd } : {}
|
|
2278
2345
|
});
|
|
2279
|
-
projection.title =
|
|
2346
|
+
projection.title = restored.title;
|
|
2280
2347
|
const record = registerRecord(
|
|
2281
2348
|
sessionId,
|
|
2282
2349
|
handle.agent,
|
|
@@ -2287,8 +2354,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2287
2354
|
);
|
|
2288
2355
|
if (presetId !== void 0) record.preset = presetId;
|
|
2289
2356
|
{
|
|
2290
|
-
const
|
|
2291
|
-
const storedMode = permission.current(events);
|
|
2357
|
+
const storedMode = currentPermission(record);
|
|
2292
2358
|
if (storedMode !== void 0) record.modeId = storedMode;
|
|
2293
2359
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2294
2360
|
const event = events[index];
|
|
@@ -2483,6 +2549,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2483
2549
|
record.approvals = policy;
|
|
2484
2550
|
};
|
|
2485
2551
|
const permissionService = () => permissionPresets;
|
|
2552
|
+
const currentPermission = (record) => {
|
|
2553
|
+
const session = record.agent.session;
|
|
2554
|
+
return permissionService().current(typeof session.snapshotEvents === "function" ? session : readSessionEvents(session));
|
|
2555
|
+
};
|
|
2486
2556
|
const permissionPresentation = (id) => {
|
|
2487
2557
|
const spec = permissionService().resolve(id);
|
|
2488
2558
|
const stock = MODE_LABELS[id];
|
|
@@ -2568,7 +2638,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2568
2638
|
});
|
|
2569
2639
|
}
|
|
2570
2640
|
if (commandRuntime.list(record.agent).some((command) => command.name === "plan")) {
|
|
2571
|
-
const active = [...record.agent.session
|
|
2641
|
+
const active = [...readSessionEvents(record.agent.session)].reverse().map((event) => event).find((event) => event.type === "plan/mode")?.data?.["active"] === true;
|
|
2572
2642
|
result.push({
|
|
2573
2643
|
type: "select",
|
|
2574
2644
|
id: "collaboration_mode",
|
|
@@ -2736,10 +2806,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2736
2806
|
});
|
|
2737
2807
|
};
|
|
2738
2808
|
const syncPermissionState = (record, sessionId) => {
|
|
2739
|
-
const events = record.agent.session
|
|
2740
|
-
|
|
2741
|
-
const permission = permissionService();
|
|
2742
|
-
const storedMode = permission.current(events);
|
|
2809
|
+
const events = readSessionEvents(record.agent.session);
|
|
2810
|
+
const storedMode = currentPermission(record);
|
|
2743
2811
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2744
2812
|
const event = events[index];
|
|
2745
2813
|
if (event?.type === "approval/policy") {
|
|
@@ -2870,6 +2938,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2870
2938
|
watchSubagentParent(agent);
|
|
2871
2939
|
return record;
|
|
2872
2940
|
};
|
|
2941
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2942
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2943
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2944
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2945
|
+
});
|
|
2873
2946
|
ctx.on("session/event", (session, event) => {
|
|
2874
2947
|
const sessionId = String(session.header.id);
|
|
2875
2948
|
const record = sessions.get(sessionId);
|
|
@@ -2957,7 +3030,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2957
3030
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2958
3031
|
mcpCapabilities: { http: true, sse: false },
|
|
2959
3032
|
auth: { logout: {} },
|
|
2960
|
-
sessionCapabilities: { list: {} }
|
|
3033
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2961
3034
|
},
|
|
2962
3035
|
authMethods: advertisedAuthMethods(
|
|
2963
3036
|
providers,
|
|
@@ -3072,6 +3145,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3072
3145
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3073
3146
|
};
|
|
3074
3147
|
},
|
|
3148
|
+
async resumeSession(params) {
|
|
3149
|
+
assertOpen();
|
|
3150
|
+
await requireCredential(config.provider);
|
|
3151
|
+
validateCwd(params.cwd);
|
|
3152
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3153
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3154
|
+
requirePersistence();
|
|
3155
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3156
|
+
const options = await configOptions(record);
|
|
3157
|
+
return {
|
|
3158
|
+
modes: modeState(record),
|
|
3159
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3160
|
+
};
|
|
3161
|
+
},
|
|
3075
3162
|
async listSessions(params) {
|
|
3076
3163
|
assertOpen();
|
|
3077
3164
|
const persistence = requirePersistence();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.28",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"
|
|
@@ -53,62 +53,61 @@
|
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
55
55
|
"@deepseek-ai/cordis-plugin-timer": "^1.1.3",
|
|
56
|
-
"@deepseek-ai/dsh": "0.1.
|
|
57
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
58
|
-
"@deepseek-ai/dsh-agent-instructions": "0.1.
|
|
59
|
-
"@deepseek-ai/dsh-agent-loop": "0.1.
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
66
|
-
"@deepseek-ai/dsh-compaction": "0.1.
|
|
67
|
-
"@deepseek-ai/dsh-compaction-
|
|
68
|
-
"@deepseek-ai/dsh-
|
|
69
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-fs": "0.1.
|
|
72
|
-
"@deepseek-ai/dsh-fs-
|
|
73
|
-
"@deepseek-ai/dsh-fs-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-goal": "0.1.
|
|
76
|
-
"@deepseek-ai/dsh-
|
|
77
|
-
"@deepseek-ai/dsh-
|
|
78
|
-
"@deepseek-ai/dsh-
|
|
79
|
-
"@deepseek-ai/dsh-
|
|
80
|
-
"@deepseek-ai/dsh-
|
|
81
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
82
|
-
"@deepseek-ai/dsh-llm-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-sandbox": "0.1.
|
|
85
|
-
"@deepseek-ai/dsh-sandbox-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
89
|
-
"@deepseek-ai/dsh-session-
|
|
90
|
-
"@deepseek-ai/dsh-session-persistence": "0.1.
|
|
91
|
-
"@deepseek-ai/dsh-session-
|
|
92
|
-
"@deepseek-ai/dsh-session-
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/dsh-
|
|
95
|
-
"@deepseek-ai/dsh-shell": "0.1.
|
|
96
|
-
"@deepseek-ai/dsh-
|
|
97
|
-
"@deepseek-ai/dsh-skill": "0.1.
|
|
98
|
-
"@deepseek-ai/dsh-
|
|
99
|
-
"@deepseek-ai/dsh-subprocess": "0.1.
|
|
100
|
-
"@deepseek-ai/dsh-
|
|
101
|
-
"@deepseek-ai/dsh-
|
|
102
|
-
"@deepseek-ai/dsh-
|
|
103
|
-
"@deepseek-ai/dsh-
|
|
104
|
-
"@deepseek-ai/dsh-tool-
|
|
105
|
-
"@deepseek-ai/dsh-tool-
|
|
106
|
-
"@deepseek-ai/dsh-tool-
|
|
107
|
-
"@deepseek-ai/dsh-tool-
|
|
108
|
-
"@deepseek-ai/dsh-tool-
|
|
109
|
-
"@deepseek-ai/dsh-
|
|
110
|
-
"@deepseek-ai/dsh-
|
|
111
|
-
"@deepseek-ai/dsh-user-approval": "0.1.1-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh": "0.1.2-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-agent-instructions": "0.1.2-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.2-rc.1",
|
|
60
|
+
"@deepseek-ai/dsh-anonymous-user-id": "0.1.2-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-bash-sandbox": "0.1.2-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-brand": "0.1.2-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-commands": "0.1.2-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-compaction": "0.1.2-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-compaction-basic": "0.1.2-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-compaction-tool-result-pruner": "0.1.2-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-credentials-local": "0.1.2-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-fs": "0.1.2-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-fs-local": "0.1.2-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-fs-observation-policy": "0.1.2-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-fs-sandbox": "0.1.2-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-goal": "0.1.2-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-goal-round-driver": "0.1.2-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-jobs-local": "0.1.2-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.2-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.1.2-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-llm-retry": "0.1.2-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-sandbox": "0.1.2-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-sandbox-local": "0.1.2-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.2-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-scope": "0.1.2-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-session-checkpoint-policy": "0.1.2-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.2-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-session-title": "0.1.2-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-shell": "0.1.2-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-shell-env": "0.1.2-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-skill": "0.1.2-rc.1",
|
|
97
|
+
"@deepseek-ai/dsh-skill-filesystem": "0.1.2-rc.1",
|
|
98
|
+
"@deepseek-ai/dsh-subprocess": "0.1.2-rc.1",
|
|
99
|
+
"@deepseek-ai/dsh-subprocess-local": "0.1.2-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
|
|
101
|
+
"@deepseek-ai/dsh-timeout": "0.1.2-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-token-meter": "0.1.2-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-tool-bash": "0.1.2-rc.1",
|
|
104
|
+
"@deepseek-ai/dsh-tool-fs": "0.1.2-rc.1",
|
|
105
|
+
"@deepseek-ai/dsh-tool-goal": "0.1.2-rc.1",
|
|
106
|
+
"@deepseek-ai/dsh-tool-jobs": "0.1.2-rc.1",
|
|
107
|
+
"@deepseek-ai/dsh-tool-skill": "0.1.2-rc.1",
|
|
108
|
+
"@deepseek-ai/dsh-tool-todo": "0.1.2-rc.1",
|
|
109
|
+
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
110
|
+
"@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
|
|
112
111
|
"@types/node": "^26.1.0",
|
|
113
112
|
"esbuild": "^0.28.1",
|
|
114
113
|
"pnpm": "11.22.0",
|
|
@@ -145,7 +144,7 @@
|
|
|
145
144
|
}
|
|
146
145
|
},
|
|
147
146
|
"dshAcp": {
|
|
148
|
-
"standaloneDsh": "0.1.
|
|
147
|
+
"standaloneDsh": "0.1.2-rc.1",
|
|
149
148
|
"hostCompatibility": {
|
|
150
149
|
"cordisProtocol": 0,
|
|
151
150
|
"policy": "capability-negotiated"
|
package/vendor/dsh-runtime.tgz
CHANGED
|
Binary file
|
package/vendor/runtime.json
CHANGED