@duckmind/dm-windows-x64 0.63.4 → 0.63.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +236 -13
- package/extensions/dm-context/package.json +1 -1
- package/extensions/dm-context/skills/context-management/SKILL.md +173 -223
- package/extensions/dm-context/skills/context-management/references/development-and-troubleshooting.md +75 -0
- package/extensions/dm-context/skills/context-management/references/interleaved-async-work.md +143 -0
- package/extensions/dm-context/skills/context-management/references/planning-and-execution.md +81 -0
- package/extensions/dm-context/skills/context-management/references/repeated-items-and-batch-work.md +69 -0
- package/extensions/dm-context/skills/context-management/references/retry-branch-and-pivot.md +80 -0
- package/extensions/dm-context/skills/context-management/references/search-research-and-reading.md +103 -0
- package/extensions/dm-context/skills/context-management/references/task-switching-and-cleanup.md +73 -0
- package/extensions/dm-context/src/context.js +3 -2
- package/extensions/dm-context/src/index.js +168 -84
- package/extensions/dm-mcp-adapter/OAUTH.md +3 -0
- package/extensions/dm-mcp-adapter/THIRD_PARTY_NOTICES.md +4 -0
- package/extensions/dm-mcp-adapter/abort.js +35 -0
- package/extensions/dm-mcp-adapter/agent-dir.js +45 -0
- package/extensions/dm-mcp-adapter/agent-plugin-loader.js +349 -0
- package/extensions/dm-mcp-adapter/app-bridge.bundle.js +5843 -0
- package/extensions/dm-mcp-adapter/cli.js +354 -0
- package/extensions/dm-mcp-adapter/commands.js +619 -0
- package/extensions/dm-mcp-adapter/config.js +1060 -0
- package/extensions/dm-mcp-adapter/consent-manager.js +60 -0
- package/extensions/dm-mcp-adapter/direct-tools.js +544 -0
- package/extensions/dm-mcp-adapter/dist/agent-dir.js +45 -0
- package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +349 -0
- package/extensions/dm-mcp-adapter/dist/config.js +1060 -0
- package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +311 -0
- package/extensions/dm-mcp-adapter/dist/metadata-cache.js +285 -0
- package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +144 -0
- package/extensions/dm-mcp-adapter/dist/resource-tools.js +7 -0
- package/extensions/dm-mcp-adapter/dist/types.js +247 -0
- package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +36 -0
- package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +58 -0
- package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +26 -0
- package/extensions/dm-mcp-adapter/dist/utils.js +307 -0
- package/extensions/dm-mcp-adapter/elicitation-handler.js +306 -0
- package/extensions/dm-mcp-adapter/error-signal.js +16 -0
- package/extensions/dm-mcp-adapter/errors.js +225 -0
- package/extensions/dm-mcp-adapter/failure-backoff.js +19 -0
- package/extensions/dm-mcp-adapter/glimpse-ui.js +61 -0
- package/extensions/dm-mcp-adapter/host-html-template.js +464 -0
- package/extensions/dm-mcp-adapter/index.js +1079 -0
- package/extensions/dm-mcp-adapter/init.js +624 -0
- package/extensions/dm-mcp-adapter/json-schema-validator.js +53 -0
- package/extensions/dm-mcp-adapter/lifecycle.js +385 -0
- package/extensions/dm-mcp-adapter/logger.js +115 -0
- package/extensions/dm-mcp-adapter/mcp-auth-flow.js +824 -0
- package/extensions/dm-mcp-adapter/mcp-auth.js +732 -0
- package/extensions/dm-mcp-adapter/mcp-bearer-store.js +311 -0
- package/extensions/dm-mcp-adapter/mcp-callback-server.js +388 -0
- package/extensions/dm-mcp-adapter/mcp-code.js +291 -0
- package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +88 -0
- package/extensions/dm-mcp-adapter/mcp-oauth-provider.js +490 -0
- package/extensions/dm-mcp-adapter/mcp-output-guard.js +386 -0
- package/extensions/dm-mcp-adapter/mcp-panel.js +951 -0
- package/extensions/dm-mcp-adapter/mcp-probe.js +151 -0
- package/extensions/dm-mcp-adapter/mcp-references.js +263 -0
- package/extensions/dm-mcp-adapter/mcp-script-worker.mjs +126 -0
- package/extensions/dm-mcp-adapter/mcp-setup-panel.js +578 -0
- package/extensions/dm-mcp-adapter/mcp-status.js +80 -0
- package/extensions/dm-mcp-adapter/mcp-trace.js +215 -0
- package/extensions/dm-mcp-adapter/metadata-cache.js +292 -0
- package/extensions/dm-mcp-adapter/namespace-tools.js +155 -0
- package/extensions/dm-mcp-adapter/npx-resolver.js +465 -0
- package/extensions/dm-mcp-adapter/oauth-handler.js +17 -0
- package/extensions/dm-mcp-adapter/oauth.js +17 -0
- package/extensions/dm-mcp-adapter/onboarding-state.js +62 -0
- package/extensions/dm-mcp-adapter/package-mcp-loader.js +144 -0
- package/extensions/dm-mcp-adapter/package.json +68 -0
- package/extensions/dm-mcp-adapter/panel-keys.js +26 -0
- package/extensions/dm-mcp-adapter/prompts.js +258 -0
- package/extensions/dm-mcp-adapter/proxy-modes.js +1336 -0
- package/extensions/dm-mcp-adapter/request-headers-command.js +293 -0
- package/extensions/dm-mcp-adapter/resource-tools.js +7 -0
- package/extensions/dm-mcp-adapter/runtime-owner.js +79 -0
- package/extensions/dm-mcp-adapter/sampling-handler.js +222 -0
- package/extensions/dm-mcp-adapter/sandbox-proxy-template.js +190 -0
- package/extensions/dm-mcp-adapter/search-ranking.js +198 -0
- package/extensions/dm-mcp-adapter/server-manager.js +1258 -0
- package/extensions/dm-mcp-adapter/session-recovery.js +82 -0
- package/extensions/dm-mcp-adapter/skills/mcp-scripting/SKILL.md +39 -0
- package/extensions/dm-mcp-adapter/state.js +0 -0
- package/extensions/dm-mcp-adapter/test-runner.mjs +18 -0
- package/extensions/dm-mcp-adapter/tool-approval.js +151 -0
- package/extensions/dm-mcp-adapter/tool-metadata.js +247 -0
- package/extensions/dm-mcp-adapter/tool-registrar.js +212 -0
- package/extensions/dm-mcp-adapter/tool-result-renderer.js +355 -0
- package/extensions/dm-mcp-adapter/ts-shape.js +157 -0
- package/extensions/dm-mcp-adapter/types.js +264 -0
- package/extensions/dm-mcp-adapter/ui-app-bridge-helpers.js +36 -0
- package/extensions/dm-mcp-adapter/ui-resource-handler.js +209 -0
- package/extensions/dm-mcp-adapter/ui-server.js +815 -0
- package/extensions/dm-mcp-adapter/ui-session.js +455 -0
- package/extensions/dm-mcp-adapter/ui-stream-types.js +58 -0
- package/extensions/dm-mcp-adapter/ui-tool-visibility.js +26 -0
- package/extensions/dm-mcp-adapter/unix-socket-transport.js +83 -0
- package/extensions/dm-mcp-adapter/utils.js +307 -0
- package/extensions/dm-skills-manager/THIRD_PARTY_NOTICES.md +27 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/components.js +265 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/constants.js +31 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/creation-fallback.js +20 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/creation.js +145 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/dialog.js +738 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/dm-ai-compat.js +15 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/format.js +125 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/glyphs.js +142 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/layout.js +93 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/paths.js +76 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/registry.js +95 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/settings.js +93 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/startup.js +32 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/toggle.js +54 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/types.js +14 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/ui.js +121 -0
- package/extensions/dm-skills-manager/extensions/skills-manager.js +111 -0
- package/extensions/dm-skills-manager/package.json +121 -0
- package/package.json +13 -1
- package/extensions/dm-localllm-provider/detect.test.js +0 -1051
- package/extensions/dm-localllm-provider/index.test.js +0 -149
- package/extensions/dm-localllm-provider/keychain.test.js +0 -61
|
@@ -1,9 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Type
|
|
3
|
+
} from "@duckmind/dm-ai";
|
|
2
4
|
import { formatTokens } from "./utils.js";
|
|
3
|
-
const InternalTools = ["
|
|
5
|
+
const InternalTools = ["context_checkpoint", "context_timeline", "context_compact"];
|
|
6
|
+
const DmContextCustomMessageType = "dm-context";
|
|
7
|
+
const AcmEnableFollowUp = "Agentic context management is now enabled";
|
|
4
8
|
let CommandCtx = null;
|
|
5
|
-
let
|
|
9
|
+
let CompactParams = null;
|
|
6
10
|
const isInternal = (name) => InternalTools.includes(name);
|
|
11
|
+
const formatContextUsage = (usage, includeTokens = false) => {
|
|
12
|
+
if (usage?.percent == null)
|
|
13
|
+
return "Unknown";
|
|
14
|
+
const percent = `${usage.percent.toFixed(1)}%`;
|
|
15
|
+
if (!includeTokens || usage.tokens == null)
|
|
16
|
+
return percent;
|
|
17
|
+
return `${percent} (${formatTokens(usage.tokens)}/${formatTokens(usage.contextWindow)})`;
|
|
18
|
+
};
|
|
19
|
+
const PassiveCompactionEntryTypes = new Set([
|
|
20
|
+
"custom",
|
|
21
|
+
"label",
|
|
22
|
+
"session_info",
|
|
23
|
+
"model_change",
|
|
24
|
+
"thinking_level_change"
|
|
25
|
+
]);
|
|
26
|
+
export const didConversationAdvance = (branch, compactTurnLeaf) => {
|
|
27
|
+
if (!compactTurnLeaf)
|
|
28
|
+
return true;
|
|
29
|
+
const compactTurnIndex = branch.findIndex((entry) => entry.id === compactTurnLeaf);
|
|
30
|
+
if (compactTurnIndex === -1)
|
|
31
|
+
return true;
|
|
32
|
+
return branch.slice(compactTurnIndex + 1).some((entry) => !PassiveCompactionEntryTypes.has(entry.type));
|
|
33
|
+
};
|
|
7
34
|
const resolveTargetId = (sm, target) => {
|
|
8
35
|
if (target.toLowerCase() === "root") {
|
|
9
36
|
const tree = sm.getTree();
|
|
@@ -21,18 +48,21 @@ const resolveTargetId = (sm, target) => {
|
|
|
21
48
|
}
|
|
22
49
|
return target;
|
|
23
50
|
};
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
51
|
+
const ContextTimelineDescription = "Inspect the active conversation path as a structural map: checkpoints, summaries/compactions, branch points, user turns, and current position. Use when orientation or compact target selection depends on the shape of history.";
|
|
52
|
+
const ContextTimelineParams = Type.Object({
|
|
53
|
+
limit: Type.Optional(Type.Number({ description: "Maximum visible timeline entries (default: 50)." })),
|
|
54
|
+
verbose: Type.Optional(Type.Boolean({ description: "If true, show all messages including internal context-tool traffic. If false (default), collapse to structural milestones." }))
|
|
27
55
|
});
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
56
|
+
const ContextCompactDescription = "Create a summarized continuation branch from an earlier checkpoint or history node. The selected target is the branch point; the summary must restore the useful state from the compacted path after that target. This changes conversation history only; it does not modify or roll back disk files or external systems.";
|
|
57
|
+
const ContextCompactParams = Type.Object({
|
|
58
|
+
target: Type.String({ description: "Checkpoint name, history node ID, or root to use as the branch point for the summarized continuation." }),
|
|
59
|
+
summary: Type.String({ description: "Handoff summary injected into the new continuation branch. Restore current task/state, decisions/constraints, important external side effects (changed files, processes, browser/tickets/remote state), validation status, source anchors/evidence/open questions likely needed soon, and explicit next step. Do not rely on backupCheckpoint for details needed in the next phase." }),
|
|
60
|
+
backupCheckpoint: Type.Optional(Type.String({ description: "Optional checkpoint name to label the current conversation state before branching. This is only a recovery pointer; the summary must still contain the state needed to continue." }))
|
|
32
61
|
});
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
const ContextCheckpointDescription = "Create a named anchor by labeling a conversation history node. This does not branch, summarize, or affect external state; it only makes the point easy to find later in timeline or compact target selection.";
|
|
63
|
+
const ContextCheckpointParams = Type.Object({
|
|
64
|
+
name: Type.String({ description: "Unique semantic anchor name that encodes the task and phase/purpose, e.g. parser-fix-start or timeout-investigation-search. Avoid generic names like start, checkpoint-1, or retry." }),
|
|
65
|
+
target: Type.Optional(Type.String({ description: "Optional history node ID or checkpoint name to label. Defaults to the current meaningful position near the conversation head." }))
|
|
36
66
|
});
|
|
37
67
|
export default function src_default(pi) {
|
|
38
68
|
pi.registerCommand("acm", {
|
|
@@ -40,41 +70,38 @@ export default function src_default(pi) {
|
|
|
40
70
|
handler: async (args, ctx) => {
|
|
41
71
|
CommandCtx = ctx;
|
|
42
72
|
ctx.ui.notify("Agentic Context Management enabled.", "info");
|
|
43
|
-
pi.sendMessage({
|
|
44
|
-
customType: "dm-context",
|
|
45
|
-
content: "use context-management skill",
|
|
46
|
-
display: false
|
|
47
|
-
}, {
|
|
48
|
-
deliverAs: "followUp"
|
|
49
|
-
});
|
|
50
73
|
if (args) {
|
|
51
|
-
pi.sendUserMessage(args);
|
|
74
|
+
pi.sendUserMessage(args, { deliverAs: "followUp" });
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
77
|
});
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
const findCheckpointInTree = (sm, nodes, checkpointName) => {
|
|
79
|
+
const stack = [...nodes].reverse();
|
|
80
|
+
while (stack.length > 0) {
|
|
81
|
+
const n = stack.pop();
|
|
82
|
+
if (sm.getLabel(n.entry.id) === checkpointName)
|
|
58
83
|
return n.entry.id;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
if (n.children?.length) {
|
|
85
|
+
for (let i = n.children.length - 1;i >= 0; i--) {
|
|
86
|
+
stack.push(n.children[i]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
62
89
|
}
|
|
63
90
|
return null;
|
|
64
91
|
};
|
|
65
92
|
pi.registerTool({
|
|
66
|
-
name: "
|
|
67
|
-
label: "Context
|
|
68
|
-
description:
|
|
69
|
-
parameters:
|
|
93
|
+
name: "context_checkpoint",
|
|
94
|
+
label: "Context Checkpoint",
|
|
95
|
+
description: ContextCheckpointDescription,
|
|
96
|
+
parameters: ContextCheckpointParams,
|
|
70
97
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
71
98
|
const sm = ctx.sessionManager;
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
99
|
+
const existingCheckpointId = findCheckpointInTree(sm, sm.getTree(), params.name);
|
|
100
|
+
if (existingCheckpointId) {
|
|
74
101
|
return {
|
|
75
102
|
content: [{
|
|
76
103
|
type: "text",
|
|
77
|
-
text: `Error:
|
|
104
|
+
text: `Error: Checkpoint '${params.name}' already exists at ${existingCheckpointId}. Checkpoint names must be unique. Use a different name or remove the existing one first.`
|
|
78
105
|
}],
|
|
79
106
|
details: {}
|
|
80
107
|
};
|
|
@@ -106,14 +133,20 @@ export default function src_default(pi) {
|
|
|
106
133
|
id = sm.getLeafId() ?? "";
|
|
107
134
|
}
|
|
108
135
|
pi.setLabel(id, params.name);
|
|
109
|
-
return {
|
|
136
|
+
return {
|
|
137
|
+
content: [{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: `Created checkpoint '${params.name}' at ${id}.`
|
|
140
|
+
}],
|
|
141
|
+
details: {}
|
|
142
|
+
};
|
|
110
143
|
}
|
|
111
144
|
});
|
|
112
145
|
pi.registerTool({
|
|
113
|
-
name: "
|
|
114
|
-
label: "Context
|
|
115
|
-
description:
|
|
116
|
-
parameters:
|
|
146
|
+
name: "context_timeline",
|
|
147
|
+
label: "Context Timeline",
|
|
148
|
+
description: ContextTimelineDescription,
|
|
149
|
+
parameters: ContextTimelineParams,
|
|
117
150
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
118
151
|
const sm = ctx.sessionManager;
|
|
119
152
|
const branch = sm.getBranch();
|
|
@@ -137,7 +170,7 @@ export default function src_default(pi) {
|
|
|
137
170
|
return e.summary || "[No summary provided]";
|
|
138
171
|
}
|
|
139
172
|
if (entry.type === "label") {
|
|
140
|
-
return `
|
|
173
|
+
return `checkpoint: ${entry.label}`;
|
|
141
174
|
}
|
|
142
175
|
if (entry.type === "message") {
|
|
143
176
|
const msg = entry.message;
|
|
@@ -234,7 +267,7 @@ export default function src_default(pi) {
|
|
|
234
267
|
}
|
|
235
268
|
const id = entry.id;
|
|
236
269
|
const isRoot = branch.length > 0 && entry.id === branch[0].id;
|
|
237
|
-
const meta = [isRoot ? "ROOT" : null, isHead ? "HEAD" : null, label ? `
|
|
270
|
+
const meta = [isRoot ? "ROOT" : null, isHead ? "HEAD" : null, label ? `checkpoint: ${label}` : null].filter(Boolean).join(", ");
|
|
238
271
|
const body = content.length > 100 ? content.slice(0, 100) + "..." : content;
|
|
239
272
|
const marker = isHead ? "*" : role === "USER" ? "•" : "|";
|
|
240
273
|
lines.push(`${marker} ${id}${meta ? ` (${meta})` : ""} [${role}] ${body}`);
|
|
@@ -242,26 +275,24 @@ export default function src_default(pi) {
|
|
|
242
275
|
if (hiddenCount > 0) {
|
|
243
276
|
lines.push(` : ... (${hiddenCount} hidden messages) ...`);
|
|
244
277
|
}
|
|
245
|
-
const
|
|
246
|
-
let
|
|
247
|
-
|
|
248
|
-
usageStr = `${usage.percent.toFixed(1)}% (${formatTokens(usage.tokens)}/${formatTokens(usage.contextWindow)})`;
|
|
249
|
-
}
|
|
250
|
-
let stepsSinceTag = 0;
|
|
251
|
-
let nearestTagName = "None";
|
|
278
|
+
const usageStr = formatContextUsage(ctx.getContextUsage(), true);
|
|
279
|
+
let stepsSinceCheckpoint = 0;
|
|
280
|
+
let nearestCheckpointName = "None";
|
|
252
281
|
for (let i = branch.length - 1;i >= 0; i--) {
|
|
253
282
|
const id = branch[i].id;
|
|
254
283
|
const label = sm.getLabel(id);
|
|
255
284
|
if (label) {
|
|
256
|
-
|
|
285
|
+
nearestCheckpointName = label;
|
|
257
286
|
break;
|
|
258
287
|
}
|
|
259
|
-
|
|
288
|
+
stepsSinceCheckpoint++;
|
|
260
289
|
}
|
|
290
|
+
const compactCue = nearestCheckpointName === "None" ? "create a checkpoint before the next noisy phase" : `if this segment has produced a stable result and another phase remains, compact to '${nearestCheckpointName}' with a handoff summary before continuing`;
|
|
261
291
|
const hud = [
|
|
262
292
|
`[Context Dashboard]`,
|
|
263
293
|
`• Context Usage: ${usageStr}`,
|
|
264
|
-
`• Segment Size: ${
|
|
294
|
+
`• Segment Size: ${stepsSinceCheckpoint} steps since last checkpoint '${nearestCheckpointName}'`,
|
|
295
|
+
`• Compact Cue: ${compactCue}`,
|
|
265
296
|
`---------------------------------------------------`
|
|
266
297
|
].join(`
|
|
267
298
|
`);
|
|
@@ -271,13 +302,16 @@ export default function src_default(pi) {
|
|
|
271
302
|
}
|
|
272
303
|
});
|
|
273
304
|
pi.registerTool({
|
|
274
|
-
name: "
|
|
275
|
-
label: "Context
|
|
276
|
-
description:
|
|
277
|
-
parameters:
|
|
305
|
+
name: "context_compact",
|
|
306
|
+
label: "Context Compact",
|
|
307
|
+
description: ContextCompactDescription,
|
|
308
|
+
parameters: ContextCompactParams,
|
|
278
309
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
279
310
|
if (!CommandCtx) {
|
|
280
|
-
|
|
311
|
+
const editorText = ctx.ui.getEditorText();
|
|
312
|
+
const followUp = editorText ? `${AcmEnableFollowUp}
|
|
313
|
+
${editorText}` : AcmEnableFollowUp;
|
|
314
|
+
ctx.ui.setEditorText(`/acm ${followUp}`);
|
|
281
315
|
return {
|
|
282
316
|
content: [{
|
|
283
317
|
type: "text",
|
|
@@ -287,52 +321,102 @@ export default function src_default(pi) {
|
|
|
287
321
|
};
|
|
288
322
|
}
|
|
289
323
|
const sm = ctx.sessionManager;
|
|
324
|
+
const usageBeforeText = formatContextUsage(ctx.getContextUsage());
|
|
290
325
|
const tid = resolveTargetId(sm, params.target);
|
|
291
326
|
const currentLeaf = sm.getLeafId();
|
|
292
327
|
if (currentLeaf === tid) {
|
|
293
328
|
return { content: [{ type: "text", text: `Already at target ${tid}` }], details: {} };
|
|
294
329
|
}
|
|
295
|
-
if (params.
|
|
296
|
-
pi.setLabel(currentLeaf, params.
|
|
330
|
+
if (params.backupCheckpoint && currentLeaf) {
|
|
331
|
+
pi.setLabel(currentLeaf, params.backupCheckpoint);
|
|
297
332
|
}
|
|
298
333
|
const currentLabel = currentLeaf ? sm.getLabel(currentLeaf) : undefined;
|
|
299
|
-
const origin = currentLabel ? `
|
|
300
|
-
const enrichedMessage = `(summary from ${origin})
|
|
301
|
-
${params.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return { content: [{ type: "text", text: "checkout start" }], details: {} };
|
|
334
|
+
const origin = currentLabel ? `checkpoint: ${currentLabel}` : currentLeaf || "unknown";
|
|
335
|
+
const enrichedMessage = `(handoff summary from ${origin})
|
|
336
|
+
${params.summary}`;
|
|
337
|
+
CompactParams = params;
|
|
338
|
+
CompactParams.tid = tid;
|
|
339
|
+
CompactParams.enrichedMessage = enrichedMessage;
|
|
340
|
+
CompactParams.usageBeforeText = usageBeforeText;
|
|
341
|
+
return { content: [{ type: "text", text: "compact start" }], details: {} };
|
|
308
342
|
}
|
|
309
343
|
});
|
|
310
|
-
pi.on("turn_end", async (
|
|
311
|
-
if (!
|
|
344
|
+
pi.on("turn_end", async (_event, ctx) => {
|
|
345
|
+
if (!CompactParams) {
|
|
312
346
|
return;
|
|
313
347
|
}
|
|
314
348
|
ctx.abort();
|
|
315
349
|
});
|
|
316
350
|
pi.on("agent_end", async (_event, ctx) => {
|
|
317
|
-
if (!
|
|
351
|
+
if (!CompactParams) {
|
|
318
352
|
return;
|
|
319
353
|
}
|
|
320
354
|
if (!CommandCtx) {
|
|
321
355
|
return;
|
|
322
356
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
357
|
+
const sm = ctx.sessionManager;
|
|
358
|
+
const compactParams = CompactParams;
|
|
359
|
+
const commandCtx = CommandCtx;
|
|
360
|
+
CompactParams = null;
|
|
361
|
+
const compactTurnLeaf = sm.getLeafId();
|
|
362
|
+
setTimeout(async () => {
|
|
363
|
+
try {
|
|
364
|
+
await commandCtx.waitForIdle();
|
|
365
|
+
const branch = sm.getBranch();
|
|
366
|
+
if (didConversationAdvance(branch, compactTurnLeaf)) {
|
|
367
|
+
commandCtx.ui.notify("context_compact cancelled: conversation advanced before compaction completed.", "warning");
|
|
368
|
+
pi.sendMessage({
|
|
369
|
+
customType: DmContextCustomMessageType,
|
|
370
|
+
content: [
|
|
371
|
+
"context_compact cancelled: conversation advanced before the summary branch was created.",
|
|
372
|
+
"No compaction was applied; continue from the current path. If still useful, inspect timeline and retry with an updated summary."
|
|
373
|
+
].join(`
|
|
374
|
+
`),
|
|
375
|
+
display: false
|
|
376
|
+
}, {
|
|
377
|
+
triggerTurn: true,
|
|
378
|
+
deliverAs: "followUp"
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const nid = sm.branchWithSummary(compactParams.tid, compactParams.enrichedMessage);
|
|
383
|
+
compactParams.nid = nid;
|
|
384
|
+
sm.branch(compactParams.tid);
|
|
385
|
+
await commandCtx.navigateTree(compactParams.nid, {
|
|
386
|
+
summarize: false
|
|
387
|
+
});
|
|
388
|
+
const usageAfter = commandCtx.getContextUsage();
|
|
389
|
+
commandCtx.ui.notify([
|
|
390
|
+
`Compacted to ${compactParams.target}${compactParams.target === compactParams.tid ? "" : `(${compactParams.tid})`}`,
|
|
391
|
+
`Context Usage: ${compactParams.usageBeforeText} -> ${formatContextUsage(usageAfter)}`,
|
|
392
|
+
`Backup checkpoint created: ${compactParams.backupCheckpoint || "none"}`,
|
|
393
|
+
`Summary: ${compactParams.enrichedMessage}`
|
|
394
|
+
].join(`
|
|
395
|
+
`), "info");
|
|
396
|
+
pi.sendMessage({
|
|
397
|
+
customType: DmContextCustomMessageType,
|
|
398
|
+
content: "context_compact complete. A handoff summary of your previous conversation path was injected above. Read it to understand your new state. Execute the Next Step from the summary",
|
|
399
|
+
display: false
|
|
400
|
+
}, {
|
|
401
|
+
triggerTurn: true,
|
|
402
|
+
deliverAs: "followUp"
|
|
403
|
+
});
|
|
404
|
+
} catch (err) {
|
|
405
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
406
|
+
commandCtx.ui.notify(`context_compact failed: ${message}`, "error");
|
|
407
|
+
pi.sendMessage({
|
|
408
|
+
customType: DmContextCustomMessageType,
|
|
409
|
+
content: [
|
|
410
|
+
`context_compact failed: ${message}`,
|
|
411
|
+
"No compaction was applied; continue from the current path. Retry only with a fresh timeline/summary."
|
|
412
|
+
].join(`
|
|
413
|
+
`),
|
|
414
|
+
display: false
|
|
415
|
+
}, {
|
|
416
|
+
triggerTurn: true,
|
|
417
|
+
deliverAs: "followUp"
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}, 0);
|
|
337
421
|
});
|
|
338
422
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function throwIfAborted(signal) {
|
|
2
|
+
if (!signal?.aborted)
|
|
3
|
+
return;
|
|
4
|
+
throw signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "MCP request aborted"));
|
|
5
|
+
}
|
|
6
|
+
export async function abortable(promise, signal) {
|
|
7
|
+
if (!signal)
|
|
8
|
+
return promise;
|
|
9
|
+
throwIfAborted(signal);
|
|
10
|
+
return await new Promise((resolve, reject) => {
|
|
11
|
+
let settled = false;
|
|
12
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
13
|
+
const onAbort = () => {
|
|
14
|
+
if (settled)
|
|
15
|
+
return;
|
|
16
|
+
settled = true;
|
|
17
|
+
cleanup();
|
|
18
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "MCP request aborted")));
|
|
19
|
+
};
|
|
20
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
21
|
+
promise.then((value) => {
|
|
22
|
+
if (settled)
|
|
23
|
+
return;
|
|
24
|
+
settled = true;
|
|
25
|
+
cleanup();
|
|
26
|
+
resolve(value);
|
|
27
|
+
}, (error) => {
|
|
28
|
+
if (settled)
|
|
29
|
+
return;
|
|
30
|
+
settled = true;
|
|
31
|
+
cleanup();
|
|
32
|
+
reject(error);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
export function getConfigDirName() {
|
|
5
|
+
const configDir = readPiConfig()?.configDir;
|
|
6
|
+
return typeof configDir === "string" && configDir.trim() ? configDir.trim() : ".dm";
|
|
7
|
+
}
|
|
8
|
+
export function getAgentDir() {
|
|
9
|
+
const piConfig = readPiConfig();
|
|
10
|
+
const name = piConfig?.name;
|
|
11
|
+
const appName = typeof name === "string" && name.trim() ? name.trim() : "pi";
|
|
12
|
+
const configured = process.env[`${appName.toUpperCase()}_CODING_AGENT_DIR`]?.trim();
|
|
13
|
+
if (!configured) {
|
|
14
|
+
return join(homedir(), getConfigDirName(), "agent");
|
|
15
|
+
}
|
|
16
|
+
if (configured === "~") {
|
|
17
|
+
return homedir();
|
|
18
|
+
}
|
|
19
|
+
if (configured.startsWith("~/")) {
|
|
20
|
+
return resolve(homedir(), configured.slice(2));
|
|
21
|
+
}
|
|
22
|
+
return resolve(configured);
|
|
23
|
+
}
|
|
24
|
+
export function getAgentPath(...segments) {
|
|
25
|
+
return join(getAgentDir(), ...segments);
|
|
26
|
+
}
|
|
27
|
+
function readPiConfig() {
|
|
28
|
+
const dir = process.env.PI_PACKAGE_DIR?.trim();
|
|
29
|
+
if (!dir)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
const manifest = JSON.parse(readFileSync(join(resolve(dir), "package.json"), "utf8"));
|
|
33
|
+
return manifest.piConfig;
|
|
34
|
+
} catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function getAppName() {
|
|
39
|
+
const name = readPiConfig()?.name;
|
|
40
|
+
return typeof name === "string" && name.trim() ? name.trim() : "pi";
|
|
41
|
+
}
|
|
42
|
+
export function getAppClientUri() {
|
|
43
|
+
const uri = readPiConfig()?.clientUri;
|
|
44
|
+
return typeof uri === "string" && uri.trim() ? uri.trim() : undefined;
|
|
45
|
+
}
|