@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.
Files changed (121) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +236 -13
  3. package/extensions/dm-context/package.json +1 -1
  4. package/extensions/dm-context/skills/context-management/SKILL.md +173 -223
  5. package/extensions/dm-context/skills/context-management/references/development-and-troubleshooting.md +75 -0
  6. package/extensions/dm-context/skills/context-management/references/interleaved-async-work.md +143 -0
  7. package/extensions/dm-context/skills/context-management/references/planning-and-execution.md +81 -0
  8. package/extensions/dm-context/skills/context-management/references/repeated-items-and-batch-work.md +69 -0
  9. package/extensions/dm-context/skills/context-management/references/retry-branch-and-pivot.md +80 -0
  10. package/extensions/dm-context/skills/context-management/references/search-research-and-reading.md +103 -0
  11. package/extensions/dm-context/skills/context-management/references/task-switching-and-cleanup.md +73 -0
  12. package/extensions/dm-context/src/context.js +3 -2
  13. package/extensions/dm-context/src/index.js +168 -84
  14. package/extensions/dm-mcp-adapter/OAUTH.md +3 -0
  15. package/extensions/dm-mcp-adapter/THIRD_PARTY_NOTICES.md +4 -0
  16. package/extensions/dm-mcp-adapter/abort.js +35 -0
  17. package/extensions/dm-mcp-adapter/agent-dir.js +45 -0
  18. package/extensions/dm-mcp-adapter/agent-plugin-loader.js +349 -0
  19. package/extensions/dm-mcp-adapter/app-bridge.bundle.js +5843 -0
  20. package/extensions/dm-mcp-adapter/cli.js +354 -0
  21. package/extensions/dm-mcp-adapter/commands.js +619 -0
  22. package/extensions/dm-mcp-adapter/config.js +1060 -0
  23. package/extensions/dm-mcp-adapter/consent-manager.js +60 -0
  24. package/extensions/dm-mcp-adapter/direct-tools.js +544 -0
  25. package/extensions/dm-mcp-adapter/dist/agent-dir.js +45 -0
  26. package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +349 -0
  27. package/extensions/dm-mcp-adapter/dist/config.js +1060 -0
  28. package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +311 -0
  29. package/extensions/dm-mcp-adapter/dist/metadata-cache.js +285 -0
  30. package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +144 -0
  31. package/extensions/dm-mcp-adapter/dist/resource-tools.js +7 -0
  32. package/extensions/dm-mcp-adapter/dist/types.js +247 -0
  33. package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +36 -0
  34. package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +58 -0
  35. package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +26 -0
  36. package/extensions/dm-mcp-adapter/dist/utils.js +307 -0
  37. package/extensions/dm-mcp-adapter/elicitation-handler.js +306 -0
  38. package/extensions/dm-mcp-adapter/error-signal.js +16 -0
  39. package/extensions/dm-mcp-adapter/errors.js +225 -0
  40. package/extensions/dm-mcp-adapter/failure-backoff.js +19 -0
  41. package/extensions/dm-mcp-adapter/glimpse-ui.js +61 -0
  42. package/extensions/dm-mcp-adapter/host-html-template.js +464 -0
  43. package/extensions/dm-mcp-adapter/index.js +1079 -0
  44. package/extensions/dm-mcp-adapter/init.js +624 -0
  45. package/extensions/dm-mcp-adapter/json-schema-validator.js +53 -0
  46. package/extensions/dm-mcp-adapter/lifecycle.js +385 -0
  47. package/extensions/dm-mcp-adapter/logger.js +115 -0
  48. package/extensions/dm-mcp-adapter/mcp-auth-flow.js +824 -0
  49. package/extensions/dm-mcp-adapter/mcp-auth.js +732 -0
  50. package/extensions/dm-mcp-adapter/mcp-bearer-store.js +311 -0
  51. package/extensions/dm-mcp-adapter/mcp-callback-server.js +388 -0
  52. package/extensions/dm-mcp-adapter/mcp-code.js +291 -0
  53. package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +88 -0
  54. package/extensions/dm-mcp-adapter/mcp-oauth-provider.js +490 -0
  55. package/extensions/dm-mcp-adapter/mcp-output-guard.js +386 -0
  56. package/extensions/dm-mcp-adapter/mcp-panel.js +951 -0
  57. package/extensions/dm-mcp-adapter/mcp-probe.js +151 -0
  58. package/extensions/dm-mcp-adapter/mcp-references.js +263 -0
  59. package/extensions/dm-mcp-adapter/mcp-script-worker.mjs +126 -0
  60. package/extensions/dm-mcp-adapter/mcp-setup-panel.js +578 -0
  61. package/extensions/dm-mcp-adapter/mcp-status.js +80 -0
  62. package/extensions/dm-mcp-adapter/mcp-trace.js +215 -0
  63. package/extensions/dm-mcp-adapter/metadata-cache.js +292 -0
  64. package/extensions/dm-mcp-adapter/namespace-tools.js +155 -0
  65. package/extensions/dm-mcp-adapter/npx-resolver.js +465 -0
  66. package/extensions/dm-mcp-adapter/oauth-handler.js +17 -0
  67. package/extensions/dm-mcp-adapter/oauth.js +17 -0
  68. package/extensions/dm-mcp-adapter/onboarding-state.js +62 -0
  69. package/extensions/dm-mcp-adapter/package-mcp-loader.js +144 -0
  70. package/extensions/dm-mcp-adapter/package.json +68 -0
  71. package/extensions/dm-mcp-adapter/panel-keys.js +26 -0
  72. package/extensions/dm-mcp-adapter/prompts.js +258 -0
  73. package/extensions/dm-mcp-adapter/proxy-modes.js +1336 -0
  74. package/extensions/dm-mcp-adapter/request-headers-command.js +293 -0
  75. package/extensions/dm-mcp-adapter/resource-tools.js +7 -0
  76. package/extensions/dm-mcp-adapter/runtime-owner.js +79 -0
  77. package/extensions/dm-mcp-adapter/sampling-handler.js +222 -0
  78. package/extensions/dm-mcp-adapter/sandbox-proxy-template.js +190 -0
  79. package/extensions/dm-mcp-adapter/search-ranking.js +198 -0
  80. package/extensions/dm-mcp-adapter/server-manager.js +1258 -0
  81. package/extensions/dm-mcp-adapter/session-recovery.js +82 -0
  82. package/extensions/dm-mcp-adapter/skills/mcp-scripting/SKILL.md +39 -0
  83. package/extensions/dm-mcp-adapter/state.js +0 -0
  84. package/extensions/dm-mcp-adapter/test-runner.mjs +18 -0
  85. package/extensions/dm-mcp-adapter/tool-approval.js +151 -0
  86. package/extensions/dm-mcp-adapter/tool-metadata.js +247 -0
  87. package/extensions/dm-mcp-adapter/tool-registrar.js +212 -0
  88. package/extensions/dm-mcp-adapter/tool-result-renderer.js +355 -0
  89. package/extensions/dm-mcp-adapter/ts-shape.js +157 -0
  90. package/extensions/dm-mcp-adapter/types.js +264 -0
  91. package/extensions/dm-mcp-adapter/ui-app-bridge-helpers.js +36 -0
  92. package/extensions/dm-mcp-adapter/ui-resource-handler.js +209 -0
  93. package/extensions/dm-mcp-adapter/ui-server.js +815 -0
  94. package/extensions/dm-mcp-adapter/ui-session.js +455 -0
  95. package/extensions/dm-mcp-adapter/ui-stream-types.js +58 -0
  96. package/extensions/dm-mcp-adapter/ui-tool-visibility.js +26 -0
  97. package/extensions/dm-mcp-adapter/unix-socket-transport.js +83 -0
  98. package/extensions/dm-mcp-adapter/utils.js +307 -0
  99. package/extensions/dm-skills-manager/THIRD_PARTY_NOTICES.md +27 -0
  100. package/extensions/dm-skills-manager/extensions/skills-manager/components.js +265 -0
  101. package/extensions/dm-skills-manager/extensions/skills-manager/constants.js +31 -0
  102. package/extensions/dm-skills-manager/extensions/skills-manager/creation-fallback.js +20 -0
  103. package/extensions/dm-skills-manager/extensions/skills-manager/creation.js +145 -0
  104. package/extensions/dm-skills-manager/extensions/skills-manager/dialog.js +738 -0
  105. package/extensions/dm-skills-manager/extensions/skills-manager/dm-ai-compat.js +15 -0
  106. package/extensions/dm-skills-manager/extensions/skills-manager/format.js +125 -0
  107. package/extensions/dm-skills-manager/extensions/skills-manager/glyphs.js +142 -0
  108. package/extensions/dm-skills-manager/extensions/skills-manager/layout.js +93 -0
  109. package/extensions/dm-skills-manager/extensions/skills-manager/paths.js +76 -0
  110. package/extensions/dm-skills-manager/extensions/skills-manager/registry.js +95 -0
  111. package/extensions/dm-skills-manager/extensions/skills-manager/settings.js +93 -0
  112. package/extensions/dm-skills-manager/extensions/skills-manager/startup.js +32 -0
  113. package/extensions/dm-skills-manager/extensions/skills-manager/toggle.js +54 -0
  114. package/extensions/dm-skills-manager/extensions/skills-manager/types.js +14 -0
  115. package/extensions/dm-skills-manager/extensions/skills-manager/ui.js +121 -0
  116. package/extensions/dm-skills-manager/extensions/skills-manager.js +111 -0
  117. package/extensions/dm-skills-manager/package.json +121 -0
  118. package/package.json +13 -1
  119. package/extensions/dm-localllm-provider/detect.test.js +0 -1051
  120. package/extensions/dm-localllm-provider/index.test.js +0 -149
  121. package/extensions/dm-localllm-provider/keychain.test.js +0 -61
@@ -1,9 +1,36 @@
1
- import { Type } from "@sinclair/typebox";
1
+ import {
2
+ Type
3
+ } from "@duckmind/dm-ai";
2
4
  import { formatTokens } from "./utils.js";
3
- const InternalTools = ["context_tag", "context_log", "context_checkout"];
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 CheckoutParams = null;
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 ContextLogParams = Type.Object({
25
- limit: Type.Optional(Type.Number({ description: "History limit for visible entries (default: 50)." })),
26
- verbose: Type.Optional(Type.Boolean({ description: "If true, show ALL messages. If false (default), collapses intermediate AI steps and only shows 'milestones': User messages, Tags, Branch Points, and Summaries." }))
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 ContextCheckoutParams = Type.Object({
29
- target: Type.String({ description: "Where to jump/squash to. Can be a tag name (e.g., 'task-start'), a commit ID, or 'root'. This is the base for your new branch." }),
30
- message: Type.String({ description: "The 'Carryover Message' for the new branch. A summary of your *current* progress/lessons that you want to bring with you to the new state. This ensures you don't lose key information when switching contexts. Good summary message: '[Status] + [Reason] + [Important Changes] + [Carryover Data]'" }),
31
- backupTag: Type.Optional(Type.String({ description: "Optional tag name to apply to the CURRENT state before checking out. Use this to create an automatic backup of the history you are about to leave/squash." }))
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 ContextTagParams = Type.Object({
34
- name: Type.String({ description: "The tag/milestone name. Use meaningful names." }),
35
- target: Type.Optional(Type.String({ description: "The commit ID to tag. Defaults to HEAD (current state)." }))
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 findTagInTree = (sm, nodes, tagName) => {
56
- for (const n of nodes) {
57
- if (sm.getLabel(n.entry.id) === tagName)
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
- const r = findTagInTree(sm, n.children, tagName);
60
- if (r)
61
- return r;
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: "context_tag",
67
- label: "Context Tag",
68
- description: "Creates a 'Save Point' (Bookmark) in the history. Use this before trying risky changes or when a feature is stable. 'Untagged progress is risky'.",
69
- parameters: ContextTagParams,
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 existingTagId = findTagInTree(sm, sm.getTree(), params.name);
73
- if (existingTagId) {
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: Tag '${params.name}' already exists at ${existingTagId}. Tag names must be unique. Use a different name or delete the existing tag first.`
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 { content: [{ type: "text", text: `Created tag '${params.name}' at ${id}` }], details: {} };
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: "context_log",
114
- label: "Context Log",
115
- description: "Show the entire history structure (status, message, tags, milestones). Analogous to 'git log --graph --oneline --decorate'",
116
- parameters: ContextLogParams,
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 `tag: ${entry.label}`;
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 ? `tag: ${label}` : null].filter(Boolean).join(", ");
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 usage = await ctx.getContextUsage();
246
- let usageStr = "Unknown";
247
- if (usage) {
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
- nearestTagName = label;
285
+ nearestCheckpointName = label;
257
286
  break;
258
287
  }
259
- stepsSinceTag++;
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: ${stepsSinceTag} steps since last tag '${nearestTagName}'`,
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: "context_checkout",
275
- label: "Context Checkout",
276
- description: "Navigate to ANY point in the conversation history. This checkout only resets *conversation history*, NOT disk files. ALWAYS provide a detailed 'message' to bridge context.",
277
- parameters: ContextCheckoutParams,
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
- ctx.ui.setEditorText(`/acm ${ctx.ui.getEditorText() || "continue"}`);
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.backupTag && currentLeaf) {
296
- pi.setLabel(currentLeaf, params.backupTag);
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 ? `tag: ${currentLabel}` : currentLeaf || "unknown";
300
- const enrichedMessage = `(summary from ${origin})
301
- ${params.message}`;
302
- const nid = await sm.branchWithSummary(tid, enrichedMessage);
303
- CheckoutParams = params;
304
- CheckoutParams.nid = nid;
305
- CheckoutParams.tid = tid;
306
- CheckoutParams.enrichedMessage = enrichedMessage;
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 (event, ctx) => {
311
- if (!CheckoutParams) {
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 (!CheckoutParams) {
351
+ if (!CompactParams) {
318
352
  return;
319
353
  }
320
354
  if (!CommandCtx) {
321
355
  return;
322
356
  }
323
- await CommandCtx.navigateTree(CheckoutParams.nid, {
324
- summarize: false
325
- });
326
- ctx.ui.notify(`Checked out ${CheckoutParams.target}${CheckoutParams.target === CheckoutParams.tid ? "" : `(${CheckoutParams.tid})`}
327
- Backup tag created: ${CheckoutParams.backupTag || "none"}
328
- message: ${CheckoutParams.enrichedMessage}`, "info");
329
- CheckoutParams = null;
330
- pi.sendMessage({
331
- customType: "dm-context",
332
- content: "context_checkout complete. A summary of your previous branch was injected above. Read it to understand your new state. Execute the 'Next Step' from the summary",
333
- display: false
334
- }, {
335
- triggerTurn: true
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,3 @@
1
+ # DM MCP Adapter OAuth
2
+
3
+ Complete the provider authorization flow shown by DM, then return to the active DM session.
@@ -0,0 +1,4 @@
1
+ # Third-party notice
2
+
3
+ This managed extension is adapted from `nicobailon/pi-mcp-adapter` at `10a45367e033a32026987a75d6f401e37340c86f` (upstream tag `v2.32.1`) under MIT.
4
+ The upstream package name and repository are retained solely as technical provenance.
@@ -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
+ }