@openma/deepseek-harness-acp 0.4.25 → 0.4.27
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/dist/bridge.js +88 -6
- package/dist/index.js +11 -30
- package/dist/plugin.js +88 -6
- package/dist/server.js +88 -6
- package/package.json +1 -1
- package/vendor/dsh-runtime.tgz +0 -0
- package/vendor/runtime.json +1 -1
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/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"];
|
|
@@ -2257,8 +2316,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2257
2316
|
} catch (error) {
|
|
2258
2317
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2259
2318
|
}
|
|
2260
|
-
|
|
2319
|
+
let restored;
|
|
2261
2320
|
if (options.replay) {
|
|
2321
|
+
const replay = buildReplay(events);
|
|
2322
|
+
restored = replay;
|
|
2262
2323
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2263
2324
|
if (replay.title !== void 0) {
|
|
2264
2325
|
notify(sessionId, {
|
|
@@ -2266,6 +2327,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2266
2327
|
title: replay.title
|
|
2267
2328
|
});
|
|
2268
2329
|
}
|
|
2330
|
+
} else {
|
|
2331
|
+
restored = buildResumeMetadata(events);
|
|
2269
2332
|
}
|
|
2270
2333
|
const presets = presetsService();
|
|
2271
2334
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2276,11 +2339,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2276
2339
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2277
2340
|
});
|
|
2278
2341
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2279
|
-
const projection = new SessionProjection(
|
|
2342
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2280
2343
|
terminalOutput: clientTerminalOutput,
|
|
2281
2344
|
...cwd !== void 0 ? { cwd } : {}
|
|
2282
2345
|
});
|
|
2283
|
-
projection.title =
|
|
2346
|
+
projection.title = restored.title;
|
|
2284
2347
|
const record = registerRecord(
|
|
2285
2348
|
sessionId,
|
|
2286
2349
|
handle.agent,
|
|
@@ -2874,6 +2937,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2874
2937
|
watchSubagentParent(agent);
|
|
2875
2938
|
return record;
|
|
2876
2939
|
};
|
|
2940
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2941
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2942
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2943
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2944
|
+
});
|
|
2877
2945
|
ctx.on("session/event", (session, event) => {
|
|
2878
2946
|
const sessionId = String(session.header.id);
|
|
2879
2947
|
const record = sessions.get(sessionId);
|
|
@@ -2961,7 +3029,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2961
3029
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2962
3030
|
mcpCapabilities: { http: true, sse: false },
|
|
2963
3031
|
auth: { logout: {} },
|
|
2964
|
-
sessionCapabilities: { list: {} }
|
|
3032
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2965
3033
|
},
|
|
2966
3034
|
authMethods: advertisedAuthMethods(
|
|
2967
3035
|
providers,
|
|
@@ -3076,6 +3144,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3076
3144
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3077
3145
|
};
|
|
3078
3146
|
},
|
|
3147
|
+
async resumeSession(params) {
|
|
3148
|
+
assertOpen();
|
|
3149
|
+
await requireCredential(config.provider);
|
|
3150
|
+
validateCwd(params.cwd);
|
|
3151
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3152
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3153
|
+
requirePersistence();
|
|
3154
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3155
|
+
const options = await configOptions(record);
|
|
3156
|
+
return {
|
|
3157
|
+
modes: modeState(record),
|
|
3158
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3159
|
+
};
|
|
3160
|
+
},
|
|
3079
3161
|
async listSessions(params) {
|
|
3080
3162
|
assertOpen();
|
|
3081
3163
|
const persistence = requirePersistence();
|
package/dist/index.js
CHANGED
|
@@ -295,11 +295,8 @@ async function bootAcpProfile(host, overrides) {
|
|
|
295
295
|
const appBoot = await import(pathToFileURL(req.resolve("@deepseek-ai/dsh-app-boot")).href);
|
|
296
296
|
const homePaths = await import(pathToFileURL(req.resolve("@deepseek-ai/dsh-home-paths")).href);
|
|
297
297
|
const home = homePaths.resolveDshHome();
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
let profilePatchPath;
|
|
301
|
-
let rootConfigPath = join3(ownRoot(), "profile-root.cordis.yml");
|
|
302
|
-
let bareModuleBaseUrl = pathToFileURL(dshDir + sep2).href;
|
|
298
|
+
const rootConfigPath = join3(ownRoot(), "profile-root.cordis.yml");
|
|
299
|
+
const bareModuleBaseUrl = pathToFileURL(dshDir + sep2).href;
|
|
303
300
|
const distBridge = join3(ownRoot(), "dist", "bridge.js");
|
|
304
301
|
const bridgeEntry = import.meta.url.endsWith(".ts") || !existsSync3(distBridge) ? join3(ownRoot(), "src", "bundle.ts") : distBridge;
|
|
305
302
|
const distServer = join3(ownRoot(), "dist", "server.js");
|
|
@@ -308,26 +305,13 @@ async function bootAcpProfile(host, overrides) {
|
|
|
308
305
|
const pluginEntry = import.meta.url.endsWith(".ts") || !existsSync3(distPlugin) ? join3(ownRoot(), "src", "plugin.ts") : distPlugin;
|
|
309
306
|
const distStdio = join3(ownRoot(), "dist", "stdio.js");
|
|
310
307
|
const stdioEntry = import.meta.url.endsWith(".ts") || !existsSync3(distStdio) ? join3(ownRoot(), "src", "stdio.ts") : distStdio;
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const realRoot = join3(profile.dir, "cordis.yml");
|
|
319
|
-
if (existsSync3(realRoot)) rootConfigPath = realRoot;
|
|
320
|
-
bareModuleBaseUrl = void 0;
|
|
321
|
-
logDebug(`profile: real (${profile.dir})`);
|
|
322
|
-
} else {
|
|
323
|
-
const basePatch = bundlePatchPath(req.resolve("@deepseek-ai/dsh-base/package.json"));
|
|
324
|
-
const ownPatch = bundlePatchPath(join3(ownRoot(), "package.json"));
|
|
325
|
-
bundlePatches = [
|
|
326
|
-
...appBoot.loadOverlayPatches(BIN, basePatch),
|
|
327
|
-
...appBoot.loadOverlayPatches(BIN, ownPatch)
|
|
328
|
-
];
|
|
329
|
-
logDebug(`profile: virtual (dsh-base + ${ownRoot()})`);
|
|
330
|
-
}
|
|
308
|
+
const basePatch = bundlePatchPath(req.resolve("@deepseek-ai/dsh-base/package.json"));
|
|
309
|
+
const ownPatch = bundlePatchPath(join3(ownRoot(), "package.json"));
|
|
310
|
+
const bundlePatches = [
|
|
311
|
+
...appBoot.loadOverlayPatches(BIN, basePatch),
|
|
312
|
+
...appBoot.loadOverlayPatches(BIN, ownPatch)
|
|
313
|
+
];
|
|
314
|
+
logDebug(`profile: standalone (dsh-base + ${ownRoot()})`);
|
|
331
315
|
for (const bundle of overrides?.bundles ?? []) {
|
|
332
316
|
bundlePatches.push(...loadBundleLayer(appBoot, bundle));
|
|
333
317
|
logDebug(`profile: added bundle ${bundle}`);
|
|
@@ -343,7 +327,7 @@ async function bootAcpProfile(host, overrides) {
|
|
|
343
327
|
const homePatchPath = join3(home, "cordis.patch.yml");
|
|
344
328
|
const homePatches = appBoot.loadOptionalPatches(BIN, homePatchPath) ?? [];
|
|
345
329
|
const rows = /* @__PURE__ */ new Map();
|
|
346
|
-
for (const row of appBoot.composeEntries([bundlePatches,
|
|
330
|
+
for (const row of appBoot.composeEntries([bundlePatches, homePatches])) {
|
|
347
331
|
if (typeof row.id === "string") rows.set(row.id, row);
|
|
348
332
|
}
|
|
349
333
|
const overlays = [];
|
|
@@ -376,7 +360,7 @@ async function bootAcpProfile(host, overrides) {
|
|
|
376
360
|
overlays.push({ id: configRowId, config: { ...configRow.config ?? {}, ...patch } });
|
|
377
361
|
}
|
|
378
362
|
}
|
|
379
|
-
const allPatches = [...bundlePatches, ...
|
|
363
|
+
const allPatches = [...bundlePatches, ...homePatches, ...overlays];
|
|
380
364
|
const launchEnvironment = appBoot.loadLayeredEnv("dsh");
|
|
381
365
|
const launchEnvKey = (await import(pathToFileURL(req.resolve("@deepseek-ai/dsh-launch-environment")).href)).DSH_LAUNCH_ENVIRONMENT_KEY;
|
|
382
366
|
const ctx = await appBoot.boot(
|
|
@@ -396,9 +380,6 @@ async function bootAcpProfile(host, overrides) {
|
|
|
396
380
|
});
|
|
397
381
|
throw error;
|
|
398
382
|
}
|
|
399
|
-
if (profilePatchPath !== void 0) {
|
|
400
|
-
logDebug(`user patch layers are read at boot; edit ${profilePatchPath} and restart to apply`);
|
|
401
|
-
}
|
|
402
383
|
return ctx;
|
|
403
384
|
}
|
|
404
385
|
|
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"];
|
|
@@ -2259,8 +2318,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2259
2318
|
} catch (error) {
|
|
2260
2319
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2261
2320
|
}
|
|
2262
|
-
|
|
2321
|
+
let restored;
|
|
2263
2322
|
if (options.replay) {
|
|
2323
|
+
const replay = buildReplay(events);
|
|
2324
|
+
restored = replay;
|
|
2264
2325
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2265
2326
|
if (replay.title !== void 0) {
|
|
2266
2327
|
notify(sessionId, {
|
|
@@ -2268,6 +2329,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2268
2329
|
title: replay.title
|
|
2269
2330
|
});
|
|
2270
2331
|
}
|
|
2332
|
+
} else {
|
|
2333
|
+
restored = buildResumeMetadata(events);
|
|
2271
2334
|
}
|
|
2272
2335
|
const presets = presetsService();
|
|
2273
2336
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2278,11 +2341,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2278
2341
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2279
2342
|
});
|
|
2280
2343
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2281
|
-
const projection = new SessionProjection(
|
|
2344
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2282
2345
|
terminalOutput: clientTerminalOutput,
|
|
2283
2346
|
...cwd !== void 0 ? { cwd } : {}
|
|
2284
2347
|
});
|
|
2285
|
-
projection.title =
|
|
2348
|
+
projection.title = restored.title;
|
|
2286
2349
|
const record = registerRecord(
|
|
2287
2350
|
sessionId,
|
|
2288
2351
|
handle.agent,
|
|
@@ -2876,6 +2939,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2876
2939
|
watchSubagentParent(agent);
|
|
2877
2940
|
return record;
|
|
2878
2941
|
};
|
|
2942
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2943
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2944
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2945
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2946
|
+
});
|
|
2879
2947
|
ctx.on("session/event", (session, event) => {
|
|
2880
2948
|
const sessionId = String(session.header.id);
|
|
2881
2949
|
const record = sessions.get(sessionId);
|
|
@@ -2963,7 +3031,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2963
3031
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2964
3032
|
mcpCapabilities: { http: true, sse: false },
|
|
2965
3033
|
auth: { logout: {} },
|
|
2966
|
-
sessionCapabilities: { list: {} }
|
|
3034
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2967
3035
|
},
|
|
2968
3036
|
authMethods: advertisedAuthMethods(
|
|
2969
3037
|
providers,
|
|
@@ -3078,6 +3146,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3078
3146
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3079
3147
|
};
|
|
3080
3148
|
},
|
|
3149
|
+
async resumeSession(params) {
|
|
3150
|
+
assertOpen();
|
|
3151
|
+
await requireCredential(config.provider);
|
|
3152
|
+
validateCwd(params.cwd);
|
|
3153
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3154
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3155
|
+
requirePersistence();
|
|
3156
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3157
|
+
const options = await configOptions(record);
|
|
3158
|
+
return {
|
|
3159
|
+
modes: modeState(record),
|
|
3160
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3161
|
+
};
|
|
3162
|
+
},
|
|
3081
3163
|
async listSessions(params) {
|
|
3082
3164
|
assertOpen();
|
|
3083
3165
|
const persistence = requirePersistence();
|
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"];
|
|
@@ -2253,8 +2312,10 @@ async function apply3(ctx, config = {}) {
|
|
|
2253
2312
|
} catch (error) {
|
|
2254
2313
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
2255
2314
|
}
|
|
2256
|
-
|
|
2315
|
+
let restored;
|
|
2257
2316
|
if (options.replay) {
|
|
2317
|
+
const replay = buildReplay(events);
|
|
2318
|
+
restored = replay;
|
|
2258
2319
|
for (const update of replay.updates) notify(sessionId, update);
|
|
2259
2320
|
if (replay.title !== void 0) {
|
|
2260
2321
|
notify(sessionId, {
|
|
@@ -2262,6 +2323,8 @@ async function apply3(ctx, config = {}) {
|
|
|
2262
2323
|
title: replay.title
|
|
2263
2324
|
});
|
|
2264
2325
|
}
|
|
2326
|
+
} else {
|
|
2327
|
+
restored = buildResumeMetadata(events);
|
|
2265
2328
|
}
|
|
2266
2329
|
const presets = presetsService();
|
|
2267
2330
|
const storedPreset = presetFromLog(storedHeader, events);
|
|
@@ -2272,11 +2335,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2272
2335
|
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
2273
2336
|
});
|
|
2274
2337
|
const cwd = options.cwd ?? storedHeader?.cwd;
|
|
2275
|
-
const projection = new SessionProjection(
|
|
2338
|
+
const projection = new SessionProjection(restored.contextWindow, {
|
|
2276
2339
|
terminalOutput: clientTerminalOutput,
|
|
2277
2340
|
...cwd !== void 0 ? { cwd } : {}
|
|
2278
2341
|
});
|
|
2279
|
-
projection.title =
|
|
2342
|
+
projection.title = restored.title;
|
|
2280
2343
|
const record = registerRecord(
|
|
2281
2344
|
sessionId,
|
|
2282
2345
|
handle.agent,
|
|
@@ -2870,6 +2933,11 @@ async function apply3(ctx, config = {}) {
|
|
|
2870
2933
|
watchSubagentParent(agent);
|
|
2871
2934
|
return record;
|
|
2872
2935
|
};
|
|
2936
|
+
ctx.on("tools/result", (exec, result) => {
|
|
2937
|
+
if (exec.parent !== void 0 || exec.agent === void 0 || result.isError) return;
|
|
2938
|
+
const projection = ownedRecord(exec.agent)?.projection ?? subagentByChild.get(String(exec.agent.session.id))?.projection;
|
|
2939
|
+
projection?.recordToolResult(String(exec.callId), result.value);
|
|
2940
|
+
});
|
|
2873
2941
|
ctx.on("session/event", (session, event) => {
|
|
2874
2942
|
const sessionId = String(session.header.id);
|
|
2875
2943
|
const record = sessions.get(sessionId);
|
|
@@ -2957,7 +3025,7 @@ async function apply3(ctx, config = {}) {
|
|
|
2957
3025
|
// mcp-client's second transport. Legacy SSE does not.
|
|
2958
3026
|
mcpCapabilities: { http: true, sse: false },
|
|
2959
3027
|
auth: { logout: {} },
|
|
2960
|
-
sessionCapabilities: { list: {} }
|
|
3028
|
+
sessionCapabilities: { list: {}, resume: {} }
|
|
2961
3029
|
},
|
|
2962
3030
|
authMethods: advertisedAuthMethods(
|
|
2963
3031
|
providers,
|
|
@@ -3072,6 +3140,20 @@ async function apply3(ctx, config = {}) {
|
|
|
3072
3140
|
...options.length > 0 ? { configOptions: options } : {}
|
|
3073
3141
|
};
|
|
3074
3142
|
},
|
|
3143
|
+
async resumeSession(params) {
|
|
3144
|
+
assertOpen();
|
|
3145
|
+
await requireCredential(config.provider);
|
|
3146
|
+
validateCwd(params.cwd);
|
|
3147
|
+
validateAdditionalDirectories(params.additionalDirectories);
|
|
3148
|
+
syncMcpServers(params.mcpServers, params.cwd);
|
|
3149
|
+
requirePersistence();
|
|
3150
|
+
const record = await restoreSession(params.sessionId, { replay: false, cwd: params.cwd });
|
|
3151
|
+
const options = await configOptions(record);
|
|
3152
|
+
return {
|
|
3153
|
+
modes: modeState(record),
|
|
3154
|
+
...options.length > 0 ? { configOptions: options } : {}
|
|
3155
|
+
};
|
|
3156
|
+
},
|
|
3075
3157
|
async listSessions(params) {
|
|
3076
3158
|
assertOpen();
|
|
3077
3159
|
const persistence = requirePersistence();
|
package/package.json
CHANGED
package/vendor/dsh-runtime.tgz
CHANGED
|
Binary file
|
package/vendor/runtime.json
CHANGED