mixdog 1.2.0 → 1.2.1

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 (201) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/src/defaults/shipped-history.json +4 -0
  4. package/src/defaults/skills/pptx/SKILL.md +6 -6
  5. package/src/defaults/skills/pptx/references/review.md +6 -4
  6. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  7. package/src/defaults/skills/setup/references/actions.md +13 -3
  8. package/src/defaults/skills/visualize/SKILL.md +12 -9
  9. package/src/rules/shared/60-verification.md +3 -1
  10. package/src/rules/shared/70-delivery.md +4 -2
  11. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  12. package/src/runtime/agent/orchestrator/config.mjs +10 -4
  13. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  14. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  15. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  16. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +86 -8
  17. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +5 -1
  18. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +65 -7
  19. package/src/runtime/agent/orchestrator/mcp/client.mjs +128 -44
  20. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  21. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  22. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
  23. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  24. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +19 -11
  25. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  26. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  27. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  28. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
  29. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  30. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  31. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +9 -2
  32. package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
  33. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  34. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  35. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
  36. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
  37. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
  38. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  39. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  42. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +9 -3
  44. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
  45. package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
  46. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
  47. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  48. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  49. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +267 -84
  50. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  51. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  52. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  53. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  54. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
  55. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  56. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  57. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
  58. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  59. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  60. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  61. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  62. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  63. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  64. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  65. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  66. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
  68. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +6 -4
  69. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
  70. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  71. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
  72. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
  74. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
  75. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
  76. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
  77. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  78. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
  79. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  80. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  81. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
  82. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +7 -0
  83. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +19 -12
  84. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  85. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
  86. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  87. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +21 -2
  89. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
  90. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  91. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  92. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
  95. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  96. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
  97. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
  98. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  99. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  100. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  101. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  102. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  104. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  105. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  106. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  107. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  108. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  109. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  110. package/src/runtime/attachments/store.d.mts +3 -0
  111. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  112. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  113. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  114. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  115. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  116. package/src/runtime/memory/lib/memory.mjs +1 -1
  117. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  118. package/src/runtime/shared/atomic-file.mjs +25 -4
  119. package/src/runtime/shared/extension-scopes.mjs +5 -3
  120. package/src/runtime/shared/file-lock.mjs +37 -11
  121. package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
  122. package/src/runtime/shared/llm/model-catalog.mjs +0 -12
  123. package/src/runtime/shared/llm/provider-catalog-registry.mjs +6 -0
  124. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  125. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  126. package/src/runtime/shared/pristine-execution.mjs +28 -4
  127. package/src/runtime/shared/provider-accounts.mjs +124 -20
  128. package/src/runtime/shared/provider-api-key.mjs +12 -0
  129. package/src/runtime/shared/shell-display.mjs +9 -3
  130. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  131. package/src/runtime/shared/skill-mentions.mjs +133 -0
  132. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  133. package/src/runtime/shared/tool-primitives.mjs +35 -2
  134. package/src/runtime/shared/turn-worktree-snapshot.mjs +25 -5
  135. package/src/runtime/shared/worker-requests.mjs +15 -2
  136. package/src/session-runtime/automation-agents.mjs +8 -2
  137. package/src/session-runtime/boot/apis.mjs +55 -0
  138. package/src/session-runtime/boot/config.mjs +5 -2
  139. package/src/session-runtime/boot/facade.mjs +1 -0
  140. package/src/session-runtime/boot/infrastructure.mjs +29 -18
  141. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  142. package/src/session-runtime/conversation-import/service.mjs +369 -0
  143. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
  144. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  145. package/src/session-runtime/mcp-glue/connect.mjs +57 -10
  146. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  147. package/src/session-runtime/mcp-glue/server-catalog.mjs +115 -30
  148. package/src/session-runtime/mcp-glue.mjs +22 -2
  149. package/src/session-runtime/new-session-config.mjs +3 -2
  150. package/src/session-runtime/notification-bus.mjs +9 -2
  151. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  152. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  153. package/src/session-runtime/provider-readiness.mjs +16 -10
  154. package/src/session-runtime/resource-extension-sync.mjs +20 -5
  155. package/src/session-runtime/resource-mcp-api.mjs +101 -77
  156. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  157. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  158. package/src/session-runtime/resource-skills-api.mjs +23 -2
  159. package/src/session-runtime/schedule-session-run.mjs +1 -1
  160. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  161. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  162. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  163. package/src/session-runtime/services/provider-admin.mjs +64 -18
  164. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  165. package/src/session-runtime/setup-tool/executor.mjs +12 -1
  166. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  167. package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
  168. package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
  169. package/src/session-runtime/skills-api.mjs +7 -3
  170. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  171. package/src/session-runtime/webhook-session-run.mjs +16 -4
  172. package/src/skill-script-entry.mjs +36 -0
  173. package/src/standalone/daemon-stored-session-views.mjs +11 -3
  174. package/src/standalone/daemon.mjs +41 -4
  175. package/src/standalone/session-protocol.mjs +5 -0
  176. package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
  177. package/src/tui/App.jsx +9 -1
  178. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  179. package/src/tui/app/app-view.jsx +6 -0
  180. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  181. package/src/tui/app/model-options.mjs +2 -0
  182. package/src/tui/app/skill-mention-palette.mjs +88 -0
  183. package/src/tui/app/slash-palette.mjs +23 -7
  184. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  185. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  186. package/src/tui/app/use-prompt-state.mjs +5 -0
  187. package/src/tui/components/Message.jsx +22 -1
  188. package/src/tui/components/PromptInput.jsx +21 -3
  189. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  190. package/src/tui/components/TranscriptItem.jsx +9 -1
  191. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  192. package/src/tui/components/user-message-skills.mjs +38 -0
  193. package/src/tui/dist/index.mjs +507 -171
  194. package/src/tui/session/queue-helpers.mjs +7 -2
  195. package/src/tui/session/session-api/extensions.mjs +4 -2
  196. package/src/tui/session/session-api/settings.mjs +7 -0
  197. package/src/tui/session/session-flow/drain.mjs +14 -4
  198. package/src/tui/session/tui-steering-persist.mjs +25 -9
  199. package/src/tui/session/turn.mjs +17 -0
  200. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  201. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
@@ -45,7 +45,10 @@ async function scanSourceWindows(path, intervals, signal) {
45
45
  let parts = [];
46
46
  let partBytes = 0;
47
47
  const lines = new Map();
48
- const buffer = Buffer.allocUnsafe(64 * 1024);
48
+ const minChunkBytes = 64 * 1024;
49
+ const maxChunkBytes = 1024 * 1024;
50
+ let chunkBytes = minChunkBytes;
51
+ let buffer = Buffer.allocUnsafe(chunkBytes);
49
52
  const deadline = Date.now() + READ_STREAM_TIMEOUT_MS;
50
53
  const check = () => {
51
54
  signal?.throwIfAborted();
@@ -70,7 +73,11 @@ async function scanSourceWindows(path, intervals, signal) {
70
73
  };
71
74
  while (interval < intervals.length) {
72
75
  check();
73
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
76
+ if (buffer.length < chunkBytes) buffer = Buffer.allocUnsafe(chunkBytes);
77
+ // Consume every byte inside the budget, even when a seeking chunk would
78
+ // cross it. The one-byte EOF probe never becomes usable source text.
79
+ const readBytes = Math.min(chunkBytes, Math.max(1, READ_MAX_SCAN_BYTES - scanned));
80
+ const { bytesRead } = await handle.read(buffer, 0, readBytes, position);
74
81
  check();
75
82
  if (!bytesRead) {
76
83
  if (partBytes) save(Buffer.alloc(0), true);
@@ -95,6 +102,12 @@ async function scanSourceWindows(path, intervals, signal) {
95
102
  start = end + 1;
96
103
  }
97
104
  position += bytesRead;
105
+ // Deep windows still validate coordinates from current bytes. Amortize
106
+ // positional IO while skipping, then return to small reads in a window.
107
+ chunkBytes =
108
+ interval < intervals.length && line < intervals[interval].start
109
+ ? Math.min(maxChunkBytes, chunkBytes * 2)
110
+ : minChunkBytes;
98
111
  }
99
112
  // Publication checks after its async stats, immediately before committing.
100
113
  // An unpublished result still owes a final deadline/cancellation check.
@@ -254,8 +254,8 @@ function absolutePaths(lines, rgCwd) {
254
254
  return lines.map((line) => (isAbsolute(line) ? line : resolveAgainstCwd(line, rgCwd)));
255
255
  }
256
256
 
257
- function missingGlobRoot(scan, root, rgCwd, err) {
258
- const hint = buildNotFoundHint(scan.workDir, rgCwd, 'Search', err?.code);
257
+ async function missingGlobRoot(scan, root, rgCwd, err) {
258
+ const hint = await buildNotFoundHint(scan.workDir, rgCwd, 'Search', err?.code);
259
259
  return {
260
260
  error: `path does not exist: ${normalizeOutputPath(rgCwd)} (${err?.code || 'ENOENT'})${finalizeReadFamilyEnoentTail(hint, root, err?.code)}`,
261
261
  paths: [],
@@ -422,14 +422,14 @@ function globMoreSuffix({ rgWindowIncomplete, remaining, totalBeforeOffset, next
422
422
  return '';
423
423
  }
424
424
 
425
- function globEmptyDiagnostic(scan, { totalBeforeOffset }) {
425
+ async function globEmptyDiagnostic(scan, { totalBeforeOffset }) {
426
426
  const { patterns, basePath, baseEntries, workDir, statCache, offset } = scan;
427
427
  const patternStr = patterns.length === 1 ? JSON.stringify(patterns[0]) : JSON.stringify(patterns);
428
428
  const baseLabel = normalizeOutputPath(basePath);
429
429
  if (totalBeforeOffset > 0 && offset >= totalBeforeOffset) {
430
430
  return `(no entries after offset=${offset}; total=${totalBeforeOffset}) pattern=${patternStr} path=${baseLabel}`;
431
431
  }
432
- const diagnostic = basePathDiagnostic(
432
+ const diagnostic = await basePathDiagnostic(
433
433
  baseEntries.map((e) => e.root),
434
434
  workDir,
435
435
  statCache
@@ -473,7 +473,7 @@ async function scanGlobGroups(scan) {
473
473
  const shown = await directAbsoluteMatches({ patterns, headLimit, offset });
474
474
  if (shown.length > 0) return patternCapNote + shown.join('\n');
475
475
  }
476
- const out = patternCapNote + renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss });
476
+ const out = patternCapNote + (await renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }));
477
477
  settleGlobScanCache(scan, merged, out, remaining);
478
478
  reportToolProgress(options, `found ${totalBeforeOffset} files`);
479
479
  return out;
@@ -482,7 +482,7 @@ async function scanGlobGroups(scan) {
482
482
  // The page body with its more/integrity suffixes. Empty partials are not
483
483
  // proof of absence: diagnostics are preserved even when there is no path
484
484
  // body, rather than silently dropping the integrity suffix.
485
- function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }) {
485
+ async function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }) {
486
486
  const { offset } = scan;
487
487
  const errSuffix = globIntegritySuffix(merged);
488
488
  const moreSuffix = globMoreSuffix({
@@ -492,7 +492,7 @@ function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, mi
492
492
  nextOffset: offset + capped.length,
493
493
  });
494
494
  if (capped.length > 0) return `${capped.join('\n')}${moreSuffix}${errSuffix}`;
495
- const emptyDiag = miss ? globEmptyDiagnostic(scan, { totalBeforeOffset }) : '';
495
+ const emptyDiag = miss ? await globEmptyDiagnostic(scan, { totalBeforeOffset }) : '';
496
496
  const emptyBody = (emptyDiag || '(no files found)').replace(
497
497
  '(no files found)',
498
498
  merged.rgStdoutPartial || merged.rgWindowIncomplete ? '(no files found yet)' : '(no files found)'
@@ -608,9 +608,12 @@ export async function executeGlobTool(args, workDir, options = {}) {
608
608
  };
609
609
  return await runScopedSearchInFlight(
610
610
  cacheKey,
611
- ({ signal: sharedSignal, scopedCacheOutcome }) => scanGlobGroups({
612
- ...scan, sharedSignal, options: { ...options, scopedCacheOutcome },
613
- }),
611
+ ({ signal: sharedSignal, scopedCacheOutcome }) =>
612
+ scanGlobGroups({
613
+ ...scan,
614
+ sharedSignal,
615
+ options: { ...options, scopedCacheOutcome },
616
+ }),
614
617
  { signal: options?.signal || options?.abortSignal || null, scopes },
615
618
  options?.scopedCacheOutcome
616
619
  );
@@ -211,7 +211,7 @@ async function runGrepSearch(request, sharedSignal) {
211
211
 
212
212
  async function grepMissingPathResult({ workDir, grepResolvedPath, searchPath, executeChildBuiltinTool }, err) {
213
213
  const msg = `Error: path does not exist: ${normalizeOutputPath(grepResolvedPath)} (${err?.code || 'ENOENT'})`;
214
- let hint = buildNotFoundHintBody(workDir, grepResolvedPath, 'Search', err?.code, {});
214
+ let hint = await buildNotFoundHintBody(workDir, grepResolvedPath, 'Search', err?.code, {});
215
215
  if (!hint) hint = await _suggestIndexedPaths(grepResolvedPath, executeChildBuiltinTool, workDir);
216
216
  return msg + finalizeReadFamilyEnoentTail(appendNotFoundCwdNote(hint, workDir, err?.code), searchPath, err?.code);
217
217
  }
@@ -65,10 +65,10 @@ const ENOENT_ABSENT =
65
65
 
66
66
  // Share diagnostic lookup results within one invocation without changing
67
67
  // the requested operand or executing a replacement operation.
68
- function cachedFileByBasename(workDir, missingPath, cache) {
68
+ async function cachedFileByBasename(workDir, missingPath, cache) {
69
69
  if (!cache) return findFileByBasename(workDir, missingPath);
70
70
  if (!('fileHits' in cache)) cache.fileHits = findFileByBasename(workDir, missingPath);
71
- return cache.fileHits;
71
+ return await cache.fileHits;
72
72
  }
73
73
 
74
74
  // A missing ABSOLUTE path that resolves outside the project cannot be
@@ -180,15 +180,15 @@ export function appendNotFoundCwdNote(hint, workDir, errCode = 'ENOENT') {
180
180
  return `${hint} (relative paths resolve from ${normalizeOutputPath(workDir)})`;
181
181
  }
182
182
 
183
- export function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
183
+ export async function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
184
184
  return appendNotFoundCwdNote(
185
- buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode, cache),
185
+ await buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode, cache),
186
186
  workDir,
187
187
  errCode
188
188
  );
189
189
  }
190
190
 
191
- export function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
191
+ export async function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
192
192
  if (!NOT_FOUND_CODES.has(String(errCode || 'ENOENT'))) return '';
193
193
  // `C:tmpsmp` shape: a Windows path whose separators were eaten by JSON
194
194
  // escaping (`"C:\\tmp\\smp"` written as `"C:\tmp\smp"`). Name the cause —
@@ -200,12 +200,12 @@ export function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode
200
200
  if (missingPathIsOutsideProject(workDir, missingPath)) {
201
201
  return outsideProjectNotFoundHint(missingPath, actionVerb);
202
202
  }
203
- const elsewhere = cachedFileByBasename(workDir, missingPath, cache);
203
+ const elsewhere = await cachedFileByBasename(workDir, missingPath, cache);
204
204
  if (elsewhere.length) {
205
205
  return ` Not found at this path; the same filename exists at: ${elsewhere.map((p) => `"${normalizeOutputPath(p)}"`).join(', ')}. ${actionVerb} that path directly.`;
206
206
  }
207
207
  if (isDirectoryPathGuess(missingPath)) {
208
- const dirHits = findDirectoryByBasename(workDir, missingPath, { limit: 5 });
208
+ const dirHits = await findDirectoryByBasename(workDir, missingPath, { limit: 5 });
209
209
  if (dirHits.length > 1) {
210
210
  return ` Not found at this path; same-named directories exist at: ${dirHits
211
211
  .slice(0, 5)
@@ -268,9 +268,9 @@ export function uncRefusalMessage(toolName, original, resolved) {
268
268
  return `Error: ${toolName} refuses UNC/SMB path ${JSON.stringify(shown)}; remote share access is blocked to prevent NTLM credential leaks`;
269
269
  }
270
270
 
271
- export function basePathDiagnostic(basePaths, workDir, statCache = null) {
272
- return basePaths
273
- .map((basePath) => {
271
+ export async function basePathDiagnostic(basePaths, workDir, statCache = null) {
272
+ const parts = await Promise.all(
273
+ basePaths.map(async (basePath) => {
274
274
  const resolved = resolveSearchScope(basePath, workDir);
275
275
  // Reuse the caller's call-scoped stat cache (glob preflight + per-group
276
276
  // rg runs already stat'd this same resolved root) instead of re-stating.
@@ -291,8 +291,13 @@ export function basePathDiagnostic(basePaths, workDir, statCache = null) {
291
291
  }
292
292
  return (
293
293
  `${normalizeOutputPath(basePath)}: path does not exist (${err?.code || 'ENOENT'})` +
294
- finalizeReadFamilyEnoentTail(buildNotFoundHint(workDir, resolved, 'Search', err?.code), basePath, err?.code)
294
+ finalizeReadFamilyEnoentTail(
295
+ await buildNotFoundHint(workDir, resolved, 'Search', err?.code),
296
+ basePath,
297
+ err?.code
298
+ )
295
299
  );
296
300
  })
297
- .join('; ');
301
+ );
302
+ return parts.join('; ');
298
303
  }
@@ -79,10 +79,10 @@ export function resolveAggregateAnchorRoot(name, args, baseCwd, { explicitCwdArg
79
79
  * One `file` anchor: an error string when it does not exist, otherwise the
80
80
  * root it re-homes the call to (null keeps the current cwd).
81
81
  */
82
- export function resolveFileAnchorRoot(name, args, fileArg, baseCwd) {
82
+ export async function resolveFileAnchorRoot(name, args, fileArg, baseCwd) {
83
83
  const abs = _absFrom(baseCwd, fileArg);
84
84
  if (!existsSync(abs)) {
85
- const elsewhere = findFileByBasename(pathResolve(baseCwd), abs);
85
+ const elsewhere = await findFileByBasename(pathResolve(baseCwd), abs);
86
86
  const hint = elsewhere.length
87
87
  ? ` Same filename exists at: ${elsewhere.map((p) => `"${toDisplayPath(p, baseCwd).replace(/\\/g, '/')}"`).join(', ')}. Use that path.`
88
88
  : '';
@@ -345,7 +345,7 @@ async function executeCodeGraphToolRaw(name, rawArgs, cwd, signal = null, option
345
345
  args = resolved.args;
346
346
  }
347
347
  if (fileArg && !hasAggregateFileArgs) {
348
- const anchor = resolveFileAnchorRoot(name, args, fileArg, baseCwd);
348
+ const anchor = await resolveFileAnchorRoot(name, args, fileArg, baseCwd);
349
349
  if (anchor.error) return anchor.error;
350
350
  if (anchor.root) effectiveCwd = anchor.root;
351
351
  }
@@ -47,7 +47,7 @@ export async function prepareCodexBatch({ patchStr, requestedFormat, basePath, p
47
47
  let v4aRenamePlan = null;
48
48
  if (isV4APatchInput(patchStr, requestedFormat)) {
49
49
  try {
50
- const parsedSections = preParsedV4ASections || rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath);
50
+ const parsedSections = preParsedV4ASections || (await rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath));
51
51
  const allSections = coalesceCompatibleV4ASections(parsedSections, basePath);
52
52
  v4aRenamePlan = await planV4ARenameSections(allSections, basePath);
53
53
  inputPatchStr = await convertV4ASectionsToUnifiedPatch(v4aRenamePlan.remainingSections, basePath, v4aConvertOpts);
@@ -56,7 +56,7 @@ export async function prepareCodexBatch({ patchStr, requestedFormat, basePath, p
56
56
  }
57
57
  } else if (requestedFormat !== 'unified' && hasUnifiedBareV4AHunk(patchStr)) {
58
58
  try {
59
- const sections = rewriteV4AReadRedirects(parseUnifiedBareV4APatch(patchStr), basePath);
59
+ const sections = await rewriteV4AReadRedirects(parseUnifiedBareV4APatch(patchStr), basePath);
60
60
  inputPatchStr = await convertV4ASectionsToUnifiedPatch(sections, basePath, v4aConvertOpts);
61
61
  } catch (err) {
62
62
  throw new Error(`apply_patch: bare @@ parse failed — ${err?.message || String(err)}`);
@@ -82,7 +82,7 @@ export async function prepareCodexBatch({ patchStr, requestedFormat, basePath, p
82
82
  );
83
83
  }
84
84
  try {
85
- const sections = rewriteV4AReadRedirects(parseUnifiedCountedAsV4APatch(patchStr), basePath);
85
+ const sections = await rewriteV4AReadRedirects(parseUnifiedCountedAsV4APatch(patchStr), basePath);
86
86
  inputPatchStr = await convertV4ASectionsToUnifiedPatch(sections, basePath, v4aConvertOpts);
87
87
  parsed = parsePatch(prepareInput(inputPatchStr));
88
88
  } catch (fallbackErr) {
@@ -91,7 +91,7 @@ export async function prepareCodexBatch({ patchStr, requestedFormat, basePath, p
91
91
  );
92
92
  }
93
93
  }
94
- parsed = rewriteParsedReadRedirects(parsed, basePath);
94
+ parsed = await rewriteParsedReadRedirects(parsed, basePath);
95
95
  if (!Array.isArray(parsed) || parsed.length === 0) {
96
96
  return { error: 'Error: patch contained no file sections' };
97
97
  }
@@ -7,11 +7,11 @@ import { pathKey, resolveV4AEntryPath } from '../paths.mjs';
7
7
  import { rewriteV4AReadRedirects } from '../read-redirects.mjs';
8
8
  import { isV4ARenameSection } from '../v4a-convert.mjs';
9
9
 
10
- export function selectApplyPatchRoute({ args, patchStr, requestedFormat, basePath }) {
10
+ export async function selectApplyPatchRoute({ args, patchStr, requestedFormat, basePath }) {
11
11
  let preParsedV4ASections = null;
12
12
  if (isV4APatchInput(patchStr, requestedFormat)) {
13
13
  try {
14
- preParsedV4ASections = rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath);
14
+ preParsedV4ASections = await rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath);
15
15
  } catch {
16
16
  // The selected execution path reports the authoritative parse error.
17
17
  }
@@ -34,7 +34,7 @@ async function apply_patch(rawArgs, cwd, options = {}) {
34
34
  }
35
35
  const rejectedV4AHunks = [];
36
36
  const v4aConvertOpts = { rejectPartial, rejectedHunks: rejectedV4AHunks, fuzzy, dryRun, readStateScope };
37
- const route = selectApplyPatchRoute({ args, patchStr, requestedFormat, basePath });
37
+ const route = await selectApplyPatchRoute({ args, patchStr, requestedFormat, basePath });
38
38
  if (route.useSequence) {
39
39
  const seqOut = await applyPatchSequence(patchStr, requestedFormat, basePath, {
40
40
  v4aConvertOpts,
@@ -6,7 +6,7 @@ import { resolve as pathResolve, relative as pathRelative, isAbsolute } from 'no
6
6
  import { findBySuffixStrip, findFileByBasename } from '../builtin/path-diagnostics.mjs';
7
7
  import { resolveEntryPath, resolveV4AEntryPath, classifyEntry, pathKey } from './paths.mjs';
8
8
 
9
- function uniqueExistingPatchTarget(basePath, requestedFullPath) {
9
+ async function uniqueExistingPatchTarget(basePath, requestedFullPath) {
10
10
  // Auto-relocation is only valid for a missing target lexically inside the
11
11
  // patch root. An explicit outside-root path must never be pulled back into
12
12
  // the project merely because a basename happens to match.
@@ -23,14 +23,14 @@ function uniqueExistingPatchTarget(basePath, requestedFullPath) {
23
23
  };
24
24
  const suffix = asFile(findBySuffixStrip(basePath, requestedFullPath));
25
25
  if (suffix) return suffix;
26
- const basenameHits = findFileByBasename(basePath, requestedFullPath, { limit: 2 });
26
+ const basenameHits = await findFileByBasename(basePath, requestedFullPath, { limit: 2 });
27
27
  return basenameHits.length === 1 ? asFile(basenameHits[0]) : null;
28
28
  }
29
29
 
30
- function redirectedPatchPath(requestedFullPath, basePath) {
30
+ async function redirectedPatchPath(requestedFullPath, basePath) {
31
31
  // An existing requested path always wins over relocation.
32
32
  if (!requestedFullPath || existsSync(requestedFullPath)) return requestedFullPath;
33
- return uniqueExistingPatchTarget(basePath, requestedFullPath) || requestedFullPath;
33
+ return (await uniqueExistingPatchTarget(basePath, requestedFullPath)) || requestedFullPath;
34
34
  }
35
35
 
36
36
  export function patchHeaderPathForResolved(basePath, fullPath) {
@@ -41,33 +41,37 @@ export function patchHeaderPathForResolved(basePath, fullPath) {
41
41
  return fullPath;
42
42
  }
43
43
 
44
- export function rewriteV4AReadRedirects(sections, basePath) {
45
- return (sections || []).map((section) => {
46
- if (!section || section.kind === 'add' || !section.path) return section;
47
- const requested = resolveV4AEntryPath(basePath, section.path);
48
- const redirected = redirectedPatchPath(requested, basePath);
49
- if (pathKey(redirected) === pathKey(requested)) return section;
50
- return { ...section, path: patchHeaderPathForResolved(basePath, redirected) };
51
- });
44
+ export async function rewriteV4AReadRedirects(sections, basePath) {
45
+ return Promise.all(
46
+ (sections || []).map(async (section) => {
47
+ if (!section || section.kind === 'add' || !section.path) return section;
48
+ const requested = resolveV4AEntryPath(basePath, section.path);
49
+ const redirected = await redirectedPatchPath(requested, basePath);
50
+ if (pathKey(redirected) === pathKey(requested)) return section;
51
+ return { ...section, path: patchHeaderPathForResolved(basePath, redirected) };
52
+ })
53
+ );
52
54
  }
53
55
 
54
- export function rewriteParsedReadRedirects(parsed, basePath) {
55
- return (parsed || []).map((entry) => {
56
- const kind = classifyEntry(entry);
57
- if (kind === 'create' || !entry?.oldFileName) return entry;
58
- const requested = resolveEntryPath(basePath, entry.oldFileName);
59
- const redirected = redirectedPatchPath(requested, basePath);
60
- if (pathKey(redirected) === pathKey(requested)) return entry;
61
- const rewritten = {
62
- ...entry,
63
- oldFileName: patchHeaderPathForResolved(basePath, redirected),
64
- };
65
- if (kind === 'modify' && entry.newFileName) {
66
- const newRequested = resolveEntryPath(basePath, entry.newFileName);
67
- if (pathKey(newRequested) === pathKey(requested)) {
68
- rewritten.newFileName = rewritten.oldFileName;
56
+ export async function rewriteParsedReadRedirects(parsed, basePath) {
57
+ return Promise.all(
58
+ (parsed || []).map(async (entry) => {
59
+ const kind = classifyEntry(entry);
60
+ if (kind === 'create' || !entry?.oldFileName) return entry;
61
+ const requested = resolveEntryPath(basePath, entry.oldFileName);
62
+ const redirected = await redirectedPatchPath(requested, basePath);
63
+ if (pathKey(redirected) === pathKey(requested)) return entry;
64
+ const rewritten = {
65
+ ...entry,
66
+ oldFileName: patchHeaderPathForResolved(basePath, redirected),
67
+ };
68
+ if (kind === 'modify' && entry.newFileName) {
69
+ const newRequested = resolveEntryPath(basePath, entry.newFileName);
70
+ if (pathKey(newRequested) === pathKey(requested)) {
71
+ rewritten.newFileName = rewritten.oldFileName;
72
+ }
69
73
  }
70
- }
71
- return rewritten;
72
- });
74
+ return rewritten;
75
+ })
76
+ );
73
77
  }
@@ -84,10 +84,10 @@ function parsedEntryUnit(entry, basePath) {
84
84
  // Parse the patch body into V4A-style sections (V4A envelope, bare-@@ hunks,
85
85
  // or counted-unified salvaged as V4A), or into parsed unified entries for a
86
86
  // plain unified diff.
87
- function parsePatchSections(patchStr, requestedFormat, basePath, { coalesceByFile }) {
87
+ async function parsePatchSections(patchStr, requestedFormat, basePath, { coalesceByFile }) {
88
88
  if (isV4APatchInput(patchStr, requestedFormat)) {
89
89
  try {
90
- const parsedSections = rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath);
90
+ const parsedSections = await rewriteV4AReadRedirects(parseV4APatch(patchStr), basePath);
91
91
  return { sections: coalesceByFile ? coalesceCompatibleV4ASections(parsedSections, basePath) : parsedSections };
92
92
  } catch (err) {
93
93
  throw new Error(`apply_patch: V4A parse failed — ${err?.message || String(err)}`);
@@ -98,7 +98,7 @@ function parsePatchSections(patchStr, requestedFormat, basePath, { coalesceByFil
98
98
  // section's conversion into its own unit (ordered-stop preserved) rather
99
99
  // than converting the whole patch up front.
100
100
  try {
101
- return { sections: rewriteV4AReadRedirects(parseUnifiedBareV4APatch(patchStr), basePath) };
101
+ return { sections: await rewriteV4AReadRedirects(parseUnifiedBareV4APatch(patchStr), basePath) };
102
102
  } catch (err) {
103
103
  throw new Error(`apply_patch: bare @@ parse failed — ${err?.message || String(err)}`);
104
104
  }
@@ -116,18 +116,18 @@ function parsePatchSections(patchStr, requestedFormat, basePath, { coalesceByFil
116
116
  // V4A-style sections and defer per-section conversion — same ordered-stop
117
117
  // guarantee as the V4A path (no whole-patch up-front convert).
118
118
  try {
119
- return { sections: rewriteV4AReadRedirects(parseUnifiedCountedAsV4APatch(patchStr), basePath) };
119
+ return { sections: await rewriteV4AReadRedirects(parseUnifiedCountedAsV4APatch(patchStr), basePath) };
120
120
  } catch (fallbackErr) {
121
121
  throw new Error(
122
122
  `apply_patch: parse failed — ${err?.message || String(err)}; V4A fallback failed — ${fallbackErr?.message || String(fallbackErr)}`
123
123
  );
124
124
  }
125
125
  }
126
- return { parsed: rewriteParsedReadRedirects(parsed, basePath) };
126
+ return { parsed: await rewriteParsedReadRedirects(parsed, basePath) };
127
127
  }
128
128
 
129
- export function buildPatchUnits(patchStr, requestedFormat, basePath, ctx) {
130
- const { sections, parsed } = parsePatchSections(patchStr, requestedFormat, basePath, ctx);
129
+ export async function buildPatchUnits(patchStr, requestedFormat, basePath, ctx) {
130
+ const { sections, parsed } = await parsePatchSections(patchStr, requestedFormat, basePath, ctx);
131
131
  if (sections) return sections.map((section) => sectionUnit(section, basePath, ctx));
132
132
  return (parsed || []).map((entry) => parsedEntryUnit(entry, basePath));
133
133
  }
@@ -30,7 +30,7 @@ export async function applyPatchSequence(patchStr, requestedFormat, basePath, ct
30
30
  coalesceByFile = false,
31
31
  } = ctx;
32
32
 
33
- const units = buildPatchUnits(patchStr, requestedFormat, basePath, {
33
+ const units = await buildPatchUnits(patchStr, requestedFormat, basePath, {
34
34
  v4aConvertOpts,
35
35
  dryRun,
36
36
  readStateScope,
@@ -18,6 +18,7 @@ import { randomUUID } from 'node:crypto';
18
18
  import { getPluginData } from '../config.mjs';
19
19
  import { scrubLoaderVars, scrubProviderSecrets, scrubRuntimeRootVars } from './env-scrub.mjs';
20
20
  import { spawnShellWithRetry } from './lib/shell-spawn-retry.mjs';
21
+ import { ensureSkillLauncherDir } from '../../../shared/skill-script-runtime.mjs';
21
22
 
22
23
  const SNAPSHOT_TIMEOUT_MS = 10_000;
23
24
 
@@ -146,6 +147,9 @@ echo "# Shell options" >> "$SNAPSHOT_FILE"
146
147
  ${optDump}
147
148
  echo "# Aliases" >> "$SNAPSHOT_FILE"
148
149
  ${aliasDump}
150
+ # The sourced PATH replaces the model shell's own, so it carries the
151
+ # skill-launcher entry buildShellSpawnEnv prepends (mixdog-skill-node).
152
+ PATH=${_shellQuote(ensureSkillLauncherDir())}:"$PATH"
149
153
  # PATH may contain $, backticks, or quotes that would re-expand if we
150
154
  # emitted it inside double quotes. Emit a shell-safe single-quoted PATH
151
155
  # export line via printf %q-style escaping inside the dump shell.
@@ -0,0 +1,3 @@
1
+ export function readAttachmentBuffer(value: string | { attachmentRef?: string }): Buffer;
2
+ export function isPermanentAttachmentError(error: unknown): boolean;
3
+ export function isAttachmentReference(value: unknown): boolean;
@@ -97,7 +97,7 @@ export function createOwnedStarter({
97
97
  state.automationRunning = true;
98
98
  }
99
99
  // DEGRADED MODE: a messaging connect failure — packaged runtime without
100
- // discord.js, bad token, gateway outage — must not take schedules/webhooks
100
+ // the provider library, bad token, gateway outage — must not take schedules/webhooks
101
101
  // down with it. Sends stay dead (connected=false) but session runs, the
102
102
  // webhook server and the relay tunnel keep working.
103
103
  async function degradeToAutomation() {
@@ -27,6 +27,14 @@ ${payload}
27
27
  <<<${_UNTRUSTED}_END>>>`;
28
28
  }
29
29
 
30
+ /** The run's model-visible prompt (endpoint instructions + fenced external
31
+ * payload) and its trusted skill-selection input: the endpoint-authored
32
+ * instructions alone, so nothing in the payload or headers selects a skill. */
33
+ function webhookRunPrompt(instructions, body, headers) {
34
+ const trusted = String(instructions ?? '');
35
+ return { prompt: `${trusted}\n\n${buildFencedPayload(body, headers)}`, skillSelection: trusted };
36
+ }
37
+
30
38
  function trackDispatch({ name, deliveryId, dispatchP, controller }) {
31
39
  let settled = false;
32
40
  const timeoutHandle = setTimeout(
@@ -89,6 +97,10 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
89
97
  workflow: extra.workflow || null,
90
98
  attachments: extra.attachments || null,
91
99
  delivery: extra.delivery || null,
100
+ // Trusted skill-selection input: only the endpoint-authored
101
+ // instructions, never the fenced external body/headers that share
102
+ // the model-visible prompt.
103
+ skillSelection: String(extra.skillSelection ?? ''),
92
104
  context: {
93
105
  source: 'webhook',
94
106
  endpoint: name,
@@ -105,14 +117,14 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
105
117
  async function dispatchEndpoint({ name, body, headers, res, deliveryId, endpoint }) {
106
118
  try {
107
119
  const { model, instructions, cwd, workflow, attachments, delivery } = endpoint;
108
- const payloadContent = buildFencedPayload(body, headers);
120
+ const run = webhookRunPrompt(instructions, body, headers);
109
121
  if (!getBridgeDispatch()) throw new Error(`[webhook] session dispatch requires bridgeDispatch`);
110
- const fullPrompt = `${instructions}\n\n${payloadContent}`;
111
- await dispatchSessionRun(name, model || null, fullPrompt, headers, deliveryId, res, {
122
+ await dispatchSessionRun(name, model || null, run.prompt, headers, deliveryId, res, {
112
123
  cwd: cwd || null,
113
124
  workflow: workflow || null,
114
125
  attachments: attachments || null,
115
126
  delivery: delivery || null,
127
+ skillSelection: run.skillSelection,
116
128
  });
117
129
  return true;
118
130
  } catch (err) {
@@ -125,4 +137,4 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
125
137
  return { dispatchEndpoint };
126
138
  }
127
139
 
128
- export { buildFencedPayload, createWebhookSessionDispatcher };
140
+ export { buildFencedPayload, createWebhookSessionDispatcher, webhookRunPrompt };
@@ -218,20 +218,23 @@ function wireWebhookHandlers() {
218
218
  webhookServer.setEventPipeline(eventPipeline);
219
219
  // Webhook fires run as sessions (schedules parity); the Automations
220
220
  // session row is the only surface (channel relay retired).
221
- webhookServer.setBridgeDispatch(async ({ prompt, model, cwd, workflow, attachments, context, signal }) => {
222
- const { runWebhookSession } = await import('../../../session-runtime/webhook-session-run.mjs');
223
- const run = await runWebhookSession({
224
- name: context?.endpoint || 'webhook',
225
- model: model || null,
226
- cwd: cwd || null,
227
- workflow: workflow || null,
228
- attachments: attachments || null,
229
- // Dispatch-timeout cancellation only works if the signal reaches the run.
230
- signal: signal || null,
231
- prompt,
232
- });
233
- return run;
234
- });
221
+ webhookServer.setBridgeDispatch(
222
+ async ({ prompt, skillSelection, model, cwd, workflow, attachments, context, signal }) => {
223
+ const { runWebhookSession } = await import('../../../session-runtime/webhook-session-run.mjs');
224
+ const run = await runWebhookSession({
225
+ name: context?.endpoint || 'webhook',
226
+ model: model || null,
227
+ cwd: cwd || null,
228
+ workflow: workflow || null,
229
+ attachments: attachments || null,
230
+ // Dispatch-timeout cancellation only works if the signal reaches the run.
231
+ signal: signal || null,
232
+ prompt,
233
+ skillSelection,
234
+ });
235
+ return run;
236
+ }
237
+ );
235
238
  }
236
239
  function wireEventQueueHandlers(eventQueue) {
237
240
  if (!eventQueue) return;
@@ -17,8 +17,6 @@ export const INPUT_GROUP_SECURITY_NOTE =
17
17
  export const GNOME_EXTENSION_UUID = 'mixdog-windows@mixdog.io';
18
18
  /** The oldest helper interface this app can use (2 added Screenshot and StopRequested, 3 the in-shell indicator). */
19
19
  export const MIN_WINDOW_HELPER_VERSION = 3;
20
- export const WINDOW_CONTROL_HINT =
21
- 'wayland_window_control_unavailable: this compositor offers no window control to other programs; on GNOME the Mixdog Windows extension (mixdog-windows@mixdog.io) provides it and must be enabled and loaded';
22
20
  export const WINDOW_HELPER_RELOGIN_HINT =
23
21
  'wayland_window_control_unavailable: the Mixdog Windows GNOME extension is installed and enabled but GNOME Shell has not loaded it yet; the user must sign out and sign in again once';
24
22
  export const WINDOW_HELPER_ENABLE_HINT =