mixdog 0.9.151 → 0.9.152

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 (175) hide show
  1. package/README.md +110 -68
  2. package/package.json +3 -2
  3. package/scripts/tool-stress.mjs +0 -2
  4. package/src/defaults/skills/setup/SKILL.md +18 -27
  5. package/src/headless-exec.mjs +3 -10
  6. package/src/headless-exec.test.mjs +2 -7
  7. package/src/runtime/agent/orchestrator/config.mjs +6 -66
  8. package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
  9. package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
  10. package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
  11. package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
  12. package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
  13. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
  14. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
  15. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  16. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
  17. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
  18. package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
  19. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -2
  20. package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
  21. package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
  22. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
  23. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
  24. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
  26. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
  27. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -0
  28. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -2
  29. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
  30. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +4 -7
  31. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -0
  32. package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +235 -0
  33. package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
  34. package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
  35. package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
  36. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +14 -4
  37. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
  38. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +24 -1
  39. package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +161 -0
  40. package/src/runtime/agent/orchestrator/session/context-utils.mjs +14 -4
  41. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -1
  43. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +31 -18
  44. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -0
  45. package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
  46. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +6 -1
  47. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +1 -3
  48. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +556 -0
  49. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +457 -0
  50. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +105 -186
  51. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
  52. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +2 -0
  53. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -0
  54. package/src/runtime/agent/orchestrator/session/store.mjs +23 -1
  55. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
  56. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
  57. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
  58. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
  59. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
  60. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
  61. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
  62. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
  63. package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
  64. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
  65. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
  66. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
  67. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
  69. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
  70. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
  71. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
  72. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
  73. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
  74. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
  75. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  76. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +8 -0
  77. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
  78. package/src/runtime/browser-bridge/client.mjs +103 -0
  79. package/src/runtime/browser-bridge/tool-defs.mjs +42 -0
  80. package/src/runtime/computer-bridge/client.mjs +101 -0
  81. package/src/runtime/computer-bridge/tool-defs.mjs +53 -0
  82. package/src/runtime/media/store.mjs +18 -8
  83. package/src/runtime/media/store.test.mjs +14 -0
  84. package/src/runtime/memory/index.mjs +1 -1
  85. package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
  86. package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
  87. package/src/runtime/shared/child-spawn-gate.mjs +17 -0
  88. package/src/runtime/shared/child-spawn-remote.mjs +69 -7
  89. package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
  90. package/src/runtime/shared/pristine-execution-contract.json +2 -1
  91. package/src/runtime/shared/provider-api-key.mjs +1 -0
  92. package/src/runtime/shared/session-runtime-health.mjs +91 -0
  93. package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
  94. package/src/runtime/shared/skill-document.mjs +86 -0
  95. package/src/runtime/shared/skill-document.test.mjs +81 -0
  96. package/src/runtime/shared/tool-surface.mjs +18 -0
  97. package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
  98. package/src/runtime/shared/turn-snapshot.mjs +32 -9
  99. package/src/runtime/shared/user-cwd.mjs +33 -14
  100. package/src/runtime/shared/user-cwd.test.mjs +43 -0
  101. package/src/session-runtime/cwd-plugins.mjs +26 -26
  102. package/src/session-runtime/cwd-plugins.test.mjs +75 -0
  103. package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
  104. package/src/session-runtime/global-extensions.mjs +26 -0
  105. package/src/session-runtime/global-extensions.test.mjs +28 -0
  106. package/src/session-runtime/goal-runtime.mjs +721 -0
  107. package/src/session-runtime/goal-runtime.test.mjs +108 -0
  108. package/src/session-runtime/lifecycle-api.mjs +18 -4
  109. package/src/session-runtime/lifecycle-api.test.mjs +21 -0
  110. package/src/session-runtime/mcp-glue.mjs +49 -47
  111. package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
  112. package/src/session-runtime/plugin-mcp.mjs +96 -22
  113. package/src/session-runtime/prewarm.mjs +1 -10
  114. package/src/session-runtime/resource-api-global.test.mjs +69 -0
  115. package/src/session-runtime/resource-api.mjs +173 -58
  116. package/src/session-runtime/runtime-core.mjs +148 -20
  117. package/src/session-runtime/runtime-tunables.mjs +6 -8
  118. package/src/session-runtime/session-turn-api.mjs +22 -11
  119. package/src/session-runtime/skills-api.mjs +79 -25
  120. package/src/session-runtime/skills-api.test.mjs +99 -0
  121. package/src/session-runtime/tool-catalog-schema.mjs +0 -5
  122. package/src/session-runtime/tool-catalog.mjs +4 -13
  123. package/src/session-runtime/tool-policy-surface.test.mjs +33 -11
  124. package/src/session-runtime/tool-surface.mjs +12 -0
  125. package/src/standalone/agent-dispatch-broker.mjs +5 -9
  126. package/src/standalone/agent-tool/notify.mjs +6 -8
  127. package/src/standalone/agent-tool/notify.test.mjs +37 -0
  128. package/src/standalone/daemon.mjs +31 -15
  129. package/src/standalone/hook-bus/config.mjs +1 -0
  130. package/src/standalone/plugin-admin.mjs +17 -0
  131. package/src/standalone/plugin-admin.test.mjs +7 -0
  132. package/src/standalone/provider-admin.mjs +1 -0
  133. package/src/standalone/session-protocol.mjs +5 -0
  134. package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
  135. package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
  136. package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
  137. package/src/standalone/session-runtime-host-factory.mjs +17 -0
  138. package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
  139. package/src/standalone/session-runtime-host-health.test.mjs +5 -0
  140. package/src/standalone/session-runtime-host.mjs +696 -58
  141. package/src/standalone/session-runtime-inline-host.mjs +351 -0
  142. package/src/standalone/session-runtime-inline-host.test.mjs +83 -0
  143. package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
  144. package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
  145. package/src/standalone/session-runtime-shard-host.test.mjs +569 -0
  146. package/src/standalone/session-runtime-shard-router.mjs +149 -0
  147. package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
  148. package/src/standalone/session-runtime-worker.mjs +476 -18
  149. package/src/standalone/session-service.mjs +81 -6
  150. package/src/tui/app/app-view.jsx +1 -0
  151. package/src/tui/app/core-memory-picker.mjs +2 -0
  152. package/src/tui/app/extension-pickers.mjs +1 -1
  153. package/src/tui/app/maintenance-pickers.mjs +3 -3
  154. package/src/tui/app/model-options.mjs +2 -0
  155. package/src/tui/app/model-picker.mjs +14 -4
  156. package/src/tui/app/panel-handoff.test.mjs +311 -0
  157. package/src/tui/app/project-picker.mjs +1 -0
  158. package/src/tui/app/route-pickers.mjs +21 -6
  159. package/src/tui/app/settings-picker.mjs +26 -3
  160. package/src/tui/app/slash-commands.mjs +1 -0
  161. package/src/tui/app/slash-dispatch.mjs +73 -22
  162. package/src/tui/app/theme-effort-pickers.mjs +7 -2
  163. package/src/tui/components/Picker.jsx +10 -1
  164. package/src/tui/dist/index.mjs +163 -44
  165. package/src/tui/session/context-state.mjs +1 -0
  166. package/src/tui/session/goal-continuation.mjs +110 -0
  167. package/src/tui/session/goal-continuation.test.mjs +84 -0
  168. package/src/tui/session/queue-helpers.mjs +5 -2
  169. package/src/tui/session/session-action-surface.test.mjs +108 -0
  170. package/src/tui/session/session-api-ext.mjs +39 -8
  171. package/src/tui/session/session-api.mjs +58 -0
  172. package/src/tui/session/session-flow.mjs +6 -0
  173. package/src/tui/session/turn.mjs +1 -0
  174. package/src/tui/session-local.mjs +15 -1
  175. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
@@ -669,7 +669,8 @@ var init_provider_api_key = __esm({
669
669
  gemini: "GEMINI_API_KEY",
670
670
  deepseek: "DEEPSEEK_API_KEY",
671
671
  xai: "XAI_API_KEY",
672
- "opencode-go": "OPENCODE_API_KEY"
672
+ "opencode-go": "OPENCODE_API_KEY",
673
+ openrouter: "OPENROUTER_API_KEY"
673
674
  });
674
675
  AGENT_PROVIDER_ENV_ALIASES = Object.freeze({
675
676
  xai: ["GROK_API_KEY"]
@@ -3873,6 +3874,10 @@ function displayToolName(name, args = {}) {
3873
3874
  case "web_fetch":
3874
3875
  case "fetch":
3875
3876
  return "Fetch";
3877
+ case "browser":
3878
+ return "Browser";
3879
+ case "computer":
3880
+ return "Computer";
3876
3881
  case "list_mcp_resources":
3877
3882
  case "list_mcp_resource_templates":
3878
3883
  case "cwd":
@@ -3993,6 +3998,16 @@ function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}) {
3993
3998
  return formatCountedUnit(collectionCount(a.url, a.uri), "URL", "URLs");
3994
3999
  }
3995
4000
  return truncateToolText(a.url || a.uri || "", max);
4001
+ case "browser":
4002
+ return compactParts([
4003
+ String(a.action || ""),
4004
+ a.url ? truncateToolText(a.url, max) : a.ref ? String(a.ref) : ""
4005
+ ]);
4006
+ case "computer":
4007
+ return compactParts([
4008
+ String(a.action || ""),
4009
+ a.window ? truncateToolText(a.window, max) : a.ref ? String(a.ref) : a.app ? truncateToolText(a.app, max) : ""
4010
+ ]);
3996
4011
  case "read_mcp_resource":
3997
4012
  return truncateToolText(a.uri || "", max);
3998
4013
  case "list_mcp_resources":
@@ -4257,6 +4272,10 @@ function toolWorkUnit(name, args = {}, category = "") {
4257
4272
  return unitDescriptor("Web Research", { count: queryCount(a, "query", "queries", "keywords") || 1, noun: "query", pluralNoun: "queries" });
4258
4273
  case "web_fetch":
4259
4274
  return unitDescriptor("Web Research", { count: queryCount(a, "url", "urls", "uri", "uris") || 1, active: "Fetching", done: "Fetched", noun: "URL", pluralNoun: "URLs" });
4275
+ case "browser":
4276
+ return unitDescriptor("Browser", { count: 1, active: "Browsing", done: "Browsed", noun: "action" });
4277
+ case "computer":
4278
+ return unitDescriptor("Computer", { count: 1, active: "Operating", done: "Operated", noun: "action" });
4260
4279
  case "fetch": {
4261
4280
  const fetchLimit = Number(a.limit ?? a.messages);
4262
4281
  const fetchCount = Number.isFinite(fetchLimit) && fetchLimit > 0 ? Math.floor(fetchLimit) : queryCount(a, "messages") || 1;
@@ -7464,6 +7483,10 @@ function Picker({
7464
7483
  indexMode = "auto",
7465
7484
  fillHeight = false,
7466
7485
  visibleCount = MAX_VISIBLE,
7486
+ // Loading panels reserve the same content-row geometry as the real list.
7487
+ // This prevents an `(empty)` row from being replaced by a multi-row menu in
7488
+ // one commit, which reads as the option items jumping into place.
7489
+ loading = false,
7467
7490
  // Onboarding confirm bar: { buttons:[{value,label}], onConfirm(button,index) }.
7468
7491
  // When present, ←/→ and Tab drive button focus (mutually exclusive with
7469
7492
  // onLeft/onRight), and Enter fires onConfirm while a button is focused.
@@ -7644,6 +7667,7 @@ function Picker({
7644
7667
  );
7645
7668
  if (items.length === 0) {
7646
7669
  const emptyLine = truncateText(String(description || "").replace(/\s+/g, " ").trim(), Math.max(0, columns - 4));
7670
+ const loadingRows = loading ? Array.from({ length: visibleLimit }, (_, index) => /* @__PURE__ */ jsx6(Text6, { color: theme.inactive, children: " " }, `loading-${index}`)) : null;
7647
7671
  return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexShrink: 0, height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs4(
7648
7672
  Box6,
7649
7673
  {
@@ -7661,7 +7685,7 @@ function Picker({
7661
7685
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
7662
7686
  /* @__PURE__ */ jsx6(Text6, { color: theme.text, children: emptyLine || " " }),
7663
7687
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
7664
- /* @__PURE__ */ jsx6(Text6, { color: theme.inactive, children: "(empty)" }),
7688
+ loading ? loadingRows : /* @__PURE__ */ jsx6(Text6, { color: theme.inactive, children: "(empty)" }),
7665
7689
  hasConfirm ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
7666
7690
  /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
7667
7691
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
@@ -9372,6 +9396,7 @@ var SLASH_COMMANDS = [
9372
9396
  { name: "clear", usage: "/clear", aliases: ["new"], aliasUsage: ["new"], description: "Start a fresh chat" },
9373
9397
  { name: "project", usage: "/project", aliases: ["projects"], aliasUsage: ["projects"], showAliasUsage: false, params: "[path]", description: "Switch working directory (project)" },
9374
9398
  { name: "compact", usage: "/compact", description: "Compact older conversation context" },
9399
+ { name: "goal", usage: "/goal", params: "[objective --time 1h|status|pause|resume|edit|complete|clear]", description: "Run and manage a durable session Goal" },
9375
9400
  { name: "autoclear", usage: "/autoclear", params: "[on|off|duration]", description: "Reduce cache-miss cost after long idle gaps" },
9376
9401
  { name: "resume", usage: "/resume", params: "[id]", description: "Resume a saved chat" },
9377
9402
  { name: "context", usage: "/context", description: "Show current context surface" },
@@ -17166,6 +17191,7 @@ function renderAppView(ctx) {
17166
17191
  indexMode: picker.indexMode,
17167
17192
  visibleCount: pickerVisibleRows,
17168
17193
  fillHeight: expandedOptionPanel,
17194
+ loading: picker.loading === true,
17169
17195
  themeEpoch: state.themeEpoch || 0,
17170
17196
  confirmBar: picker.confirmBar
17171
17197
  },
@@ -17543,6 +17569,7 @@ function createThemeEffortPickers({
17543
17569
  const openThemePicker = (options = {}) => {
17544
17570
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
17545
17571
  const onboarding = options.onboarding || null;
17572
+ const handoffPanel = options.handoffPanel && typeof options.handoffPanel === "object" ? options.handoffPanel : null;
17546
17573
  const own = surface.claim();
17547
17574
  let themes = [];
17548
17575
  try {
@@ -17577,7 +17604,8 @@ function createThemeEffortPickers({
17577
17604
  }
17578
17605
  };
17579
17606
  const saveTheme = (id, { advance = false } = {}) => {
17580
- own.close();
17607
+ if (handoffPanel) own.paint(handoffPanel);
17608
+ else own.close();
17581
17609
  const applied = applyTheme(id, { persist: true });
17582
17610
  store.pushNotice(themeNotice(applied || { id }), "info");
17583
17611
  if (advance && onboarding) onboarding.onAdvance?.();
@@ -17618,7 +17646,8 @@ function createThemeEffortPickers({
17618
17646
  saveTheme(entry.id, { advance: Boolean(onboarding) });
17619
17647
  },
17620
17648
  onCancel: () => {
17621
- own.close();
17649
+ if (handoffPanel) own.paint(handoffPanel);
17650
+ else own.close();
17622
17651
  if (currentId) applyTheme(currentId, { persist: false });
17623
17652
  if (onboarding) onboarding.onCancel?.();
17624
17653
  else if (returnTo) returnTo();
@@ -17730,6 +17759,7 @@ function createCoreMemoryPicker({
17730
17759
  // Loading state lives in the header description row, not as a fake
17731
17760
  // selectable menu item.
17732
17761
  description: "Loading memories\u2026",
17762
+ loading: true,
17733
17763
  items: [],
17734
17764
  onSelect: () => {
17735
17765
  },
@@ -17793,6 +17823,7 @@ function createCoreMemoryPicker({
17793
17823
  paintPanel({
17794
17824
  title: "Memory \xB7 List",
17795
17825
  description: "Loading memories\u2026",
17826
+ loading: true,
17796
17827
  items: [],
17797
17828
  onSelect: () => {
17798
17829
  },
@@ -17956,7 +17987,7 @@ function createExtensionPickers({
17956
17987
  mcpActive = true;
17957
17988
  };
17958
17989
  const openMcpPicker = () => {
17959
- openMcpServersPicker();
17990
+ return openMcpServersPicker();
17960
17991
  };
17961
17992
  const skillsStatus = async () => {
17962
17993
  let status;
@@ -18488,7 +18519,7 @@ function createMaintenancePickers({
18488
18519
  settled();
18489
18520
  });
18490
18521
  };
18491
- void Promise.resolve(render2({ checking: true })).catch((e) => {
18522
+ return Promise.resolve(render2({ checking: true })).catch((e) => {
18492
18523
  store.pushNotice(`update panel failed: ${e?.message || e}`, "error");
18493
18524
  return false;
18494
18525
  }).then((painted) => {
@@ -18621,8 +18652,7 @@ function createMaintenancePickers({
18621
18652
  setSettingsPrompt(null);
18622
18653
  own.context(null);
18623
18654
  closeUsagePanel();
18624
- if (options.advanced === true) renderAdvanced();
18625
- else render2();
18655
+ return Promise.resolve(options.advanced === true ? renderAdvanced() : render2()).catch((e) => store.pushNotice(`auto-clear panel failed: ${e?.message || e}`, "error"));
18626
18656
  };
18627
18657
  const openProfilePicker = async (options = {}) => {
18628
18658
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
@@ -18867,6 +18897,7 @@ var providerDisplayName = (provider) => {
18867
18897
  if (key === "xai" || key === "xai-api") return "xAI API";
18868
18898
  if (key === "deepseek" || key === "deepseek-api") return "DeepSeek API";
18869
18899
  if (key === "opencode-go") return "OpenCode Go API";
18900
+ if (key === "openrouter") return "OpenRouter";
18870
18901
  if (key === "ollama") return "Ollama";
18871
18902
  if (key === "lmstudio") return "LM Studio";
18872
18903
  if (key === "default") return "Default";
@@ -18882,6 +18913,7 @@ var providerDisplayRank = (provider) => {
18882
18913
  "antigravity-oauth": 31,
18883
18914
  "cursor-oauth": 32,
18884
18915
  "opencode-go": 35,
18916
+ openrouter: 36,
18885
18917
  openai: 40,
18886
18918
  "openai-api": 40,
18887
18919
  anthropic: 50,
@@ -19877,6 +19909,7 @@ function createModelPicker({
19877
19909
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
19878
19910
  const returnLabel = String(options.returnLabel || "Agents");
19879
19911
  const returnOnNestedCancel = options.returnOnNestedCancel === true;
19912
+ const handoffPanel = options.handoffPanel && typeof options.handoffPanel === "object" ? options.handoffPanel : null;
19880
19913
  const cancelModelPicker = () => {
19881
19914
  modelPickerClosed = true;
19882
19915
  if (returnTo) returnTo();
@@ -19893,6 +19926,7 @@ function createModelPicker({
19893
19926
  title: options.title || "Model",
19894
19927
  description: options.loadingDescription || "Loading models...",
19895
19928
  help: returnTo ? `\u2191/\u2193 Select \xB7 Enter Open \xB7 Esc ${returnLabel}` : "\u2191/\u2193 Select \xB7 Enter Open \xB7 Esc Back",
19929
+ loading: true,
19896
19930
  items: [],
19897
19931
  onCancel: cancelModelPicker
19898
19932
  });
@@ -20162,7 +20196,7 @@ ${model?.id || ""}`;
20162
20196
  if (typeof options.onImmediateSelect === "function") {
20163
20197
  options.onImmediateSelect(routeInput, selected, effort);
20164
20198
  } else {
20165
- own.paint(null);
20199
+ own.paint(handoffPanel);
20166
20200
  }
20167
20201
  const afterSelect2 = own.defer(() => {
20168
20202
  if (typeof options.onAfterSelect === "function") options.onAfterSelect();
@@ -20171,10 +20205,13 @@ ${model?.id || ""}`;
20171
20205
  if (result) clearModelCaches("all");
20172
20206
  afterSelect2();
20173
20207
  return result;
20174
- }).catch((e) => store.pushNotice(`Couldn\u2019t save model: ${e?.message || e}`, "error"));
20208
+ }).catch((e) => {
20209
+ store.pushNotice(`Couldn\u2019t save model: ${e?.message || e}`, "error");
20210
+ if (handoffPanel) afterSelect2();
20211
+ });
20175
20212
  return;
20176
20213
  }
20177
- own.paint(null);
20214
+ own.paint(handoffPanel);
20178
20215
  const afterSelect = own.defer(() => {
20179
20216
  if (typeof options.onAfterSelect === "function") options.onAfterSelect();
20180
20217
  });
@@ -20185,7 +20222,10 @@ ${model?.id || ""}`;
20185
20222
  ok ? "info" : "warn"
20186
20223
  );
20187
20224
  if (ok) afterSelect();
20188
- }).catch((e) => store.pushNotice(`Couldn\u2019t switch model: ${e?.message || e}`, "error"));
20225
+ }).catch((e) => {
20226
+ store.pushNotice(`Couldn\u2019t switch model: ${e?.message || e}`, "error");
20227
+ if (handoffPanel) afterSelect();
20228
+ });
20189
20229
  };
20190
20230
  const renderProviderModels = () => {
20191
20231
  const providerModelItems = buildProviderModelItems(models, provider, activeRoute);
@@ -20448,6 +20488,7 @@ function createRoutePickers({
20448
20488
  };
20449
20489
  const openWorkflowPicker = async (options = {}) => {
20450
20490
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
20491
+ const handoffPanel = options.handoffPanel && typeof options.handoffPanel === "object" ? options.handoffPanel : null;
20451
20492
  const own = surface.claim();
20452
20493
  let workflows = [];
20453
20494
  try {
@@ -20482,27 +20523,33 @@ function createRoutePickers({
20482
20523
  onSelect: (_value, item) => {
20483
20524
  const workflow = item?._workflow;
20484
20525
  if (!workflow) return;
20485
- own.paint(null);
20526
+ own.paint(handoffPanel);
20486
20527
  const returnAfterSwitch = own.defer(() => {
20487
20528
  if (returnTo) returnTo();
20488
20529
  });
20489
20530
  void store.setWorkflow?.(workflow.id).then((result) => {
20490
20531
  if (!result) {
20491
20532
  store.pushNotice("Workflow switch is already running.", "warn");
20533
+ if (handoffPanel) returnAfterSwitch();
20492
20534
  return;
20493
20535
  }
20494
20536
  store.pushNotice(workflowSwitchNotice2(result), "info");
20495
20537
  returnAfterSwitch();
20496
- }).catch((e) => store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error"));
20538
+ }).catch((e) => {
20539
+ store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error");
20540
+ if (handoffPanel) returnAfterSwitch();
20541
+ });
20497
20542
  },
20498
20543
  onCancel: () => {
20499
- own.close();
20544
+ if (handoffPanel) own.paint(handoffPanel);
20545
+ else own.close();
20500
20546
  if (returnTo) returnTo();
20501
20547
  }
20502
20548
  });
20503
20549
  };
20504
20550
  const openOutputStylePicker = async (options = {}) => {
20505
20551
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
20552
+ const handoffPanel = options.handoffPanel && typeof options.handoffPanel === "object" ? options.handoffPanel : null;
20506
20553
  const onboarding = options.onboarding || null;
20507
20554
  const own = surface.claim();
20508
20555
  let status = null;
@@ -20534,7 +20581,7 @@ function createRoutePickers({
20534
20581
  closeUsagePanel();
20535
20582
  const saveStyle = (styleId, { advance = false } = {}) => {
20536
20583
  if (!styleId) return;
20537
- if (!(advance && onboarding)) own.paint(null);
20584
+ if (!(advance && onboarding)) own.paint(handoffPanel);
20538
20585
  const advanceAfterSave = own.defer(() => {
20539
20586
  if (advance && onboarding) onboarding.onAdvance?.();
20540
20587
  else if (returnTo) returnTo();
@@ -20546,7 +20593,10 @@ function createRoutePickers({
20546
20593
  store.pushNotice(outputStyleNotice(result), "info");
20547
20594
  }
20548
20595
  advanceAfterSave();
20549
- }).catch((e) => store.pushNotice(`Couldn\u2019t switch output style: ${e?.message || e}`, "error"));
20596
+ }).catch((e) => {
20597
+ store.pushNotice(`Couldn\u2019t switch output style: ${e?.message || e}`, "error");
20598
+ if (handoffPanel) advanceAfterSave();
20599
+ });
20550
20600
  };
20551
20601
  own.paint({
20552
20602
  title: "Output Style",
@@ -20579,7 +20629,8 @@ function createRoutePickers({
20579
20629
  saveStyle(style.id, { advance: Boolean(onboarding) });
20580
20630
  },
20581
20631
  onCancel: () => {
20582
- own.close();
20632
+ if (handoffPanel) own.paint(handoffPanel);
20633
+ else own.close();
20583
20634
  if (onboarding) onboarding.onCancel?.();
20584
20635
  else if (returnTo) returnTo();
20585
20636
  }
@@ -20749,6 +20800,15 @@ function createSettingsPicker({
20749
20800
  openMemoryCorePicker,
20750
20801
  openUpdatePicker
20751
20802
  }) {
20803
+ const settingsHandoffPanel = (description = "Loading settings...") => ({
20804
+ title: "Settings",
20805
+ description,
20806
+ help: "Esc Close",
20807
+ indexMode: "never",
20808
+ pickerKey: "settings-handoff",
20809
+ loading: true,
20810
+ items: []
20811
+ });
20752
20812
  const nextSettingsRequest = () => {
20753
20813
  if (!settingsRequestRef) return 0;
20754
20814
  settingsRequestRef.current = (Number(settingsRequestRef.current) || 0) + 1;
@@ -20758,6 +20818,7 @@ function createSettingsPicker({
20758
20818
  const buildSettingsPicker = async (opts = {}, requestId = 0) => {
20759
20819
  const own = surface.claim();
20760
20820
  const light = opts.light === true;
20821
+ if (!light && !own.paint(settingsHandoffPanel())) return;
20761
20822
  const overrides = opts.overrides || null;
20762
20823
  const heavyCache = light ? settingsHeavyCacheRef.current : null;
20763
20824
  const snapshot = await store.getSettingsSnapshot?.({ heavy: !heavyCache }) || {};
@@ -21100,14 +21161,24 @@ function createSettingsPicker({
21100
21161
  else if (item._action === "memory-enabled") toggleMemory();
21101
21162
  else if (item._action === "memory-cycles") toggleMemoryCycles();
21102
21163
  else if (item._action === "voice") applyVoice();
21103
- else if (item._action === "output-style") openOutputStylePicker({ returnTo: openSettingsPicker });
21104
- else if (item._action === "theme") openThemePicker({ returnTo: openSettingsPicker });
21105
- else if (item._action === "workflow") openWorkflowPicker({ returnTo: openSettingsPicker });
21164
+ else if (item._action === "output-style") openOutputStylePicker({
21165
+ returnTo: openSettingsPicker,
21166
+ handoffPanel: settingsHandoffPanel("Applying output style...")
21167
+ });
21168
+ else if (item._action === "theme") openThemePicker({
21169
+ returnTo: openSettingsPicker,
21170
+ handoffPanel: settingsHandoffPanel("Applying theme...")
21171
+ });
21172
+ else if (item._action === "workflow") openWorkflowPicker({
21173
+ returnTo: openSettingsPicker,
21174
+ handoffPanel: settingsHandoffPanel("Switching workflow...")
21175
+ });
21106
21176
  else if (item._action === "model") openModelPicker({
21107
21177
  returnTo: openSettingsPicker,
21108
21178
  returnLabel: "Settings",
21109
21179
  returnOnNestedCancel: true,
21110
- onAfterSelect: openSettingsPicker
21180
+ onAfterSelect: openSettingsPicker,
21181
+ handoffPanel: settingsHandoffPanel("Switching model...")
21111
21182
  });
21112
21183
  else if (item._action === "websearch") openWebSearchPicker({
21113
21184
  returnTo: openSettingsPicker,
@@ -21199,6 +21270,42 @@ function createSlashDispatch({
21199
21270
  runDoctor,
21200
21271
  requestExit
21201
21272
  }) {
21273
+ const openSlashPanel = (command, title, open) => {
21274
+ const own = surface.claim();
21275
+ if (!own.paint({
21276
+ _kind: `slash-loading:${command}`,
21277
+ title,
21278
+ description: `Loading ${title.toLowerCase()}...`,
21279
+ help: "Esc Close",
21280
+ indexMode: "never",
21281
+ pickerKey: `slash-loading:${command}`,
21282
+ loading: true,
21283
+ items: [],
21284
+ onSelect: () => {
21285
+ },
21286
+ onCancel: () => own.close()
21287
+ })) return;
21288
+ const finishUnclaimedLoading = () => {
21289
+ if (own.owns()) own.close();
21290
+ };
21291
+ try {
21292
+ const opening = open();
21293
+ if (opening && typeof opening.then === "function") {
21294
+ void Promise.resolve(opening).then(
21295
+ finishUnclaimedLoading,
21296
+ (error) => {
21297
+ finishUnclaimedLoading();
21298
+ store.pushNotice(`${title} panel failed: ${error?.message || error}`, "error");
21299
+ }
21300
+ );
21301
+ } else {
21302
+ finishUnclaimedLoading();
21303
+ }
21304
+ } catch (error) {
21305
+ finishUnclaimedLoading();
21306
+ store.pushNotice(`${title} panel failed: ${error?.message || error}`, "error");
21307
+ }
21308
+ };
21202
21309
  const runSlashCommand = (cmd, arg = "") => {
21203
21310
  const rawName = String(cmd || "").toLowerCase();
21204
21311
  cmd = normalizeSlashCommandName2(cmd);
@@ -21225,25 +21332,27 @@ function createSlashDispatch({
21225
21332
  return true;
21226
21333
  case "model":
21227
21334
  if (!arg) {
21228
- openModelPicker();
21335
+ openSlashPanel("model", "Model", () => openModelPicker());
21229
21336
  return true;
21230
21337
  }
21231
21338
  if (arg.trim().toLowerCase() === "refresh") {
21232
- openModelPicker({ refreshModels: true });
21339
+ openSlashPanel("model", "Model", () => openModelPicker({ refreshModels: true }));
21233
21340
  return true;
21234
21341
  }
21235
21342
  void store.setModel(arg).then((ok) => store.pushNotice(ok ? modelSwitchNotice2() : "Model switch is already running.", ok ? "info" : "warn")).catch((e) => store.pushNotice(`Couldn\u2019t switch model: ${e?.message || e}`, "error"));
21236
21343
  return true;
21237
21344
  case "websearch":
21238
21345
  if (arg) store.pushNotice("/websearch sets the web-search provider/model; the web_search tool uses that model when called.", "warn");
21239
- openWebSearchPicker();
21346
+ openSlashPanel("websearch", "Web Search Model", () => openWebSearchPicker());
21240
21347
  return true;
21241
21348
  case "agents":
21242
- openAgentsPicker(arg.trim().toLowerCase() === "refresh" ? { refreshModels: true } : {});
21349
+ openSlashPanel("agents", "Agents", () => openAgentsPicker(
21350
+ arg.trim().toLowerCase() === "refresh" ? { refreshModels: true } : {}
21351
+ ));
21243
21352
  return true;
21244
21353
  case "workflow":
21245
21354
  if (!arg) {
21246
- openWorkflowPicker();
21355
+ openSlashPanel("workflow", "Workflow", () => openWorkflowPicker());
21247
21356
  return true;
21248
21357
  }
21249
21358
  void store.setWorkflow?.(arg.trim()).then((result) => {
@@ -21262,7 +21371,7 @@ function createSlashDispatch({
21262
21371
  const value = arg.trim();
21263
21372
  const lower = value.toLowerCase();
21264
21373
  if (!value) {
21265
- openOutputStylePicker();
21374
+ openSlashPanel("outputstyle", "Output Style", () => openOutputStylePicker());
21266
21375
  return true;
21267
21376
  }
21268
21377
  if (lower === "status" || lower === "current" || lower === "show") {
@@ -21285,7 +21394,7 @@ function createSlashDispatch({
21285
21394
  const value = arg.trim();
21286
21395
  const lower = value.toLowerCase();
21287
21396
  if (!value) {
21288
- openThemePicker();
21397
+ openSlashPanel("theme", "Theme", () => openThemePicker());
21289
21398
  return true;
21290
21399
  }
21291
21400
  let themes = [];
@@ -21318,7 +21427,7 @@ function createSlashDispatch({
21318
21427
  case "effort": {
21319
21428
  const pendingTurn = state.busy ? " (applies from the next turn)" : "";
21320
21429
  if (!arg) {
21321
- openEffortPicker();
21430
+ openSlashPanel("effort", "Effort", () => openEffortPicker());
21322
21431
  return true;
21323
21432
  }
21324
21433
  void store.setEffort(arg).then((result) => store.pushNotice(result ? `Effort set to ${result}${pendingTurn}` : "Effort switch is already running.", result ? "info" : "warn")).catch((e) => store.pushNotice(`Couldn\u2019t switch effort: ${e?.message || e}`, "error"));
@@ -21350,23 +21459,23 @@ function createSlashDispatch({
21350
21459
  enterProject(target);
21351
21460
  return true;
21352
21461
  }
21353
- openProjectPicker();
21462
+ openSlashPanel("project", "Projects", () => openProjectPicker());
21354
21463
  return true;
21355
21464
  }
21356
21465
  case "mcp":
21357
- openMcpPicker();
21466
+ openSlashPanel("mcp", "MCP Servers", () => openMcpPicker());
21358
21467
  return true;
21359
21468
  case "skills":
21360
- openSkillsPicker();
21469
+ openSlashPanel("skills", "Skills", () => openSkillsPicker());
21361
21470
  return true;
21362
21471
  case "plugins":
21363
- openPluginsPicker();
21472
+ openSlashPanel("plugins", "Plugins", () => openPluginsPicker());
21364
21473
  return true;
21365
21474
  case "hooks":
21366
- openHooksPicker();
21475
+ openSlashPanel("hooks", "Hooks", () => openHooksPicker());
21367
21476
  return true;
21368
21477
  case "providers":
21369
- void openProviderSetupPicker();
21478
+ openSlashPanel("providers", "Providers", () => openProviderSetupPicker());
21370
21479
  return true;
21371
21480
  case "schedules":
21372
21481
  case "webhooks":
@@ -21374,7 +21483,7 @@ function createSlashDispatch({
21374
21483
  return true;
21375
21484
  case "memory": {
21376
21485
  if (!arg.trim()) {
21377
- openMemoryCorePicker({ returnTo: null });
21486
+ openSlashPanel("memory", "Memory", () => openMemoryCorePicker({ returnTo: null }));
21378
21487
  return true;
21379
21488
  }
21380
21489
  void store.memoryControl?.(parseMemoryCommand2(arg)).catch((e) => store.pushNotice(`memory failed: ${e?.message || e}`, "error"));
@@ -21383,7 +21492,7 @@ function createSlashDispatch({
21383
21492
  case "autoclear": {
21384
21493
  const value = arg.trim().toLowerCase();
21385
21494
  if (!value) {
21386
- openAutoClearPicker();
21495
+ openSlashPanel("autoclear", "Auto-clear", () => openAutoClearPicker());
21387
21496
  return true;
21388
21497
  }
21389
21498
  void Promise.resolve(
@@ -21422,6 +21531,15 @@ function createSlashDispatch({
21422
21531
  store.pushNotice("Compact done.", "info");
21423
21532
  }).catch((error) => store.pushNotice(compactFailureNotice(error), "error"));
21424
21533
  return true;
21534
+ case "goal":
21535
+ void Promise.resolve(store.goalControl?.({ command: arg })).then((result) => {
21536
+ if (!result) {
21537
+ store.pushNotice("Goal is unavailable.", "warn");
21538
+ return;
21539
+ }
21540
+ store.pushNotice(result.message || "Goal updated.", "info");
21541
+ }).catch((error) => store.pushNotice(`Goal failed: ${error?.message || error}`, "error"));
21542
+ return true;
21425
21543
  case "resume":
21426
21544
  if (state.busy) {
21427
21545
  store.pushNotice("wait for the current turn to finish before /resume", "warn");
@@ -21430,14 +21548,14 @@ function createSlashDispatch({
21430
21548
  if (arg) {
21431
21549
  void store.resume(arg).then((ok) => store.pushNotice(ok ? `Resumed ${arg}` : "Couldn\u2019t resume chat.", ok ? "info" : "warn")).catch((e) => store.pushNotice(`Couldn\u2019t resume chat: ${e?.message || e}`, "error"));
21432
21550
  } else {
21433
- openResumePicker();
21551
+ openSlashPanel("resume", "Resume", () => openResumePicker());
21434
21552
  }
21435
21553
  return true;
21436
21554
  case "usage":
21437
- openUsagePanel(arg);
21555
+ openSlashPanel("usage", "Provider Quotas", () => openUsagePanel(arg));
21438
21556
  return true;
21439
21557
  case "context":
21440
- openContextPicker();
21558
+ openSlashPanel("context", "Context Usage", () => openContextPicker());
21441
21559
  return true;
21442
21560
  case "inherit":
21443
21561
  if (state.busy) {
@@ -21457,13 +21575,13 @@ function createSlashDispatch({
21457
21575
  return true;
21458
21576
  case "settings":
21459
21577
  case "config":
21460
- openSettingsPicker();
21578
+ openSlashPanel("settings", "Settings", () => openSettingsPicker());
21461
21579
  return true;
21462
21580
  case "profile":
21463
- openProfilePicker();
21581
+ openSlashPanel("profile", "Profile", () => openProfilePicker());
21464
21582
  return true;
21465
21583
  case "update":
21466
- openUpdatePicker();
21584
+ openSlashPanel("update", "Update", () => openUpdatePicker());
21467
21585
  return true;
21468
21586
  case "doctor":
21469
21587
  if (state.commandBusy) {
@@ -22648,6 +22766,7 @@ function createProjectPicker({
22648
22766
  description: "Opening folder picker\u2026 choose a folder in the dialog window.",
22649
22767
  help: "Waiting for the system folder dialog\u2026",
22650
22768
  indexMode: "never",
22769
+ loading: true,
22651
22770
  items: [],
22652
22771
  onSelect: () => {
22653
22772
  },
@@ -48,6 +48,7 @@ export function createContextState({ runtime, getState, updateState, getPendingS
48
48
  webSearchRoute: runtime.getWebSearchRoute?.() || runtime.webSearchRoute || null,
49
49
  autoClear: autoClearState(),
50
50
  workflow: runtime.workflow || null,
51
+ goal: runtime.goalStatus?.() || null,
51
52
  });
52
53
 
53
54
  const routeState = () => {
@@ -0,0 +1,110 @@
1
+ function clean(value) {
2
+ return String(value ?? '').trim();
3
+ }
4
+
5
+ function isGoalContinuation(entry) {
6
+ return entry?.mode === 'goal-continuation';
7
+ }
8
+
9
+ export function createGoalContinuation({
10
+ runtime,
11
+ flags,
12
+ getState,
13
+ set,
14
+ getPending,
15
+ enqueue,
16
+ } = {}) {
17
+ let scheduled = null;
18
+ let disposed = false;
19
+
20
+ const cancelQueuedGoalContinuations = () => {
21
+ const pending = getPending();
22
+ let removed = 0;
23
+ for (let index = pending.length - 1; index >= 0; index -= 1) {
24
+ if (!isGoalContinuation(pending[index])) continue;
25
+ pending.splice(index, 1);
26
+ removed += 1;
27
+ }
28
+ return removed;
29
+ };
30
+
31
+ const refreshGoalState = () => {
32
+ const goal = runtime.goalStatus?.() || null;
33
+ if (getState().goal !== goal) set({ goal });
34
+ return goal;
35
+ };
36
+
37
+ const continuationDecision = () => {
38
+ const decision = runtime.goalContinuation?.() || { run: false, reason: 'unavailable', goal: null };
39
+ if (getState().goal !== decision.goal) set({ goal: decision.goal || null });
40
+ return decision;
41
+ };
42
+
43
+ const shouldRunGoalContinuation = (entry) => {
44
+ if (disposed || flags.disposed || flags.pendingSessionReset) return false;
45
+ if (getState().sessionRemoteAttached) return false;
46
+ const decision = continuationDecision();
47
+ if (!decision.run) return false;
48
+ const queuedGoalId = clean(entry?.goalId);
49
+ return !queuedGoalId || queuedGoalId === clean(decision.goal?.id);
50
+ };
51
+
52
+ const scheduleGoalContinuation = () => {
53
+ if (disposed || scheduled) return false;
54
+ scheduled = setImmediate(() => {
55
+ scheduled = null;
56
+ if (disposed || flags.disposed || flags.pendingSessionReset) return;
57
+ const state = getState();
58
+ if (state.busy || state.commandBusy || state.sessionRemoteAttached) return;
59
+ const pending = getPending();
60
+ if (pending.some((entry) => !isGoalContinuation(entry))) return;
61
+ if (pending.some(isGoalContinuation)) return;
62
+ const decision = continuationDecision();
63
+ if (!decision.run || !clean(decision.prompt)) return;
64
+ enqueue(decision.prompt, {
65
+ mode: 'goal-continuation',
66
+ priority: 'later',
67
+ isMeta: true,
68
+ suppressDisplay: true,
69
+ skipSlashCommands: true,
70
+ restorable: false,
71
+ goalId: clean(decision.goal?.id),
72
+ displayText: '',
73
+ });
74
+ });
75
+ scheduled.unref?.();
76
+ return true;
77
+ };
78
+
79
+ const onGoalChanged = (event = {}) => {
80
+ const currentSessionId = clean(getState().sessionId || runtime.id);
81
+ if (clean(event.sessionId) && clean(event.sessionId) !== currentSessionId) return;
82
+ cancelQueuedGoalContinuations();
83
+ set({ goal: event.goal || runtime.goalStatus?.() || null });
84
+ if ((event.goal || runtime.goalStatus?.())?.status === 'active') scheduleGoalContinuation();
85
+ };
86
+
87
+ const unsubscribe = runtime.onGoalStatusChange?.(onGoalChanged) || (() => {});
88
+
89
+ return {
90
+ cancelQueuedGoalContinuations,
91
+ refreshGoalState,
92
+ scheduleGoalContinuation,
93
+ shouldRunGoalContinuation,
94
+ onGoalTurnSettled(status) {
95
+ if (status === 'done') scheduleGoalContinuation();
96
+ },
97
+ archiveCompletedGoalOnUserInput() {
98
+ cancelQueuedGoalContinuations();
99
+ void Promise.resolve(runtime.archiveCompletedGoalOnUserInput?.())
100
+ .then(() => refreshGoalState())
101
+ .catch(() => {});
102
+ },
103
+ disposeGoalContinuation() {
104
+ disposed = true;
105
+ if (scheduled) clearImmediate(scheduled);
106
+ scheduled = null;
107
+ try { unsubscribe(); } catch {}
108
+ },
109
+ };
110
+ }