@vellumai/assistant 0.8.7-dev.202606052232.2ddc989 → 0.8.8

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 (262) hide show
  1. package/bun.lock +2 -2
  2. package/docs/plugins.md +832 -0
  3. package/examples/plugins/echo/README.md +60 -61
  4. package/examples/plugins/echo/package.json +2 -1
  5. package/examples/plugins/echo/register.ts +143 -0
  6. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -7
  7. package/openapi.yaml +5 -15
  8. package/package.json +2 -2
  9. package/src/__tests__/agent-loop-exit-reason.test.ts +56 -3
  10. package/src/__tests__/anthropic-provider.test.ts +1 -1
  11. package/src/__tests__/app-control-flow.test.ts +1 -1
  12. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  13. package/src/__tests__/approval-routes-http.test.ts +1 -4
  14. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  15. package/src/__tests__/channel-approvals.test.ts +1 -1
  16. package/src/__tests__/circuit-breaker-pipeline.test.ts +405 -0
  17. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  18. package/src/__tests__/compaction-timeout-recovery.test.ts +251 -0
  19. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -0
  20. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +3 -0
  21. package/src/__tests__/conversation-agent-loop-overflow.test.ts +7 -3
  22. package/src/__tests__/conversation-agent-loop.test.ts +39 -42
  23. package/src/__tests__/conversation-clean-command.test.ts +2 -5
  24. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -4
  25. package/src/__tests__/conversation-runtime-assembly.test.ts +71 -140
  26. package/src/__tests__/conversation-runtime-workspace.test.ts +27 -108
  27. package/src/__tests__/conversation-starter-routes.test.ts +6 -14
  28. package/src/__tests__/conversation-workspace-cache-state.test.ts +16 -17
  29. package/src/__tests__/conversation-workspace-injection.test.ts +1 -61
  30. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -7
  31. package/src/__tests__/db-acp-history.test.ts +0 -101
  32. package/src/__tests__/dynamic-page-surface.test.ts +0 -31
  33. package/src/__tests__/file-write-tool.test.ts +0 -63
  34. package/src/__tests__/gateway-only-guard.test.ts +2 -12
  35. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  36. package/src/__tests__/guardian-routing-invariants.test.ts +4 -2
  37. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  38. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -1
  39. package/src/__tests__/heartbeat-service.test.ts +0 -1
  40. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  41. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  42. package/src/__tests__/injector-background-turn.test.ts +1 -1
  43. package/src/__tests__/injector-chain.test.ts +6 -34
  44. package/src/__tests__/injector-disk-pressure.test.ts +34 -77
  45. package/src/__tests__/injector-document-comments.test.ts +1 -1
  46. package/src/__tests__/list-messages-hidden-metadata.test.ts +0 -38
  47. package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
  48. package/src/__tests__/{overflow-reduction-loop.test.ts → overflow-reduce-pipeline.test.ts} +284 -64
  49. package/src/__tests__/pipeline-runner.test.ts +554 -0
  50. package/src/__tests__/plugin-api-shim.test.ts +6 -3
  51. package/src/__tests__/plugin-bootstrap.test.ts +23 -12
  52. package/src/__tests__/plugin-registry.test.ts +49 -3
  53. package/src/__tests__/plugin-types.test.ts +70 -0
  54. package/src/__tests__/reaction-persistence.test.ts +1 -1
  55. package/src/__tests__/send-endpoint-busy.test.ts +1 -4
  56. package/src/__tests__/skill-feature-flags-integration.test.ts +0 -33
  57. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  58. package/src/__tests__/subagent-fork-notifications.test.ts +3 -1
  59. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  60. package/src/__tests__/subagent-manager-notify.test.ts +3 -1
  61. package/src/__tests__/subagent-notify-parent.test.ts +3 -1
  62. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  63. package/src/__tests__/user-plugin-loader.test.ts +286 -54
  64. package/src/acp/__tests__/client-handler.test.ts +0 -40
  65. package/src/acp/__tests__/prepare-agent-env.test.ts +0 -137
  66. package/src/acp/__tests__/session-manager-persistence.test.ts +28 -95
  67. package/src/acp/agent-process.ts +1 -61
  68. package/src/acp/client-handler.ts +0 -31
  69. package/src/acp/prepare-agent-env.ts +29 -83
  70. package/src/acp/resolve-agent.test.ts +7 -320
  71. package/src/acp/resolve-agent.ts +18 -182
  72. package/src/acp/session-manager.ts +73 -495
  73. package/src/acp/types.ts +0 -8
  74. package/src/agent/compaction-circuit.ts +102 -60
  75. package/src/agent/loop.ts +59 -32
  76. package/src/api/responses/conversation-message.ts +1 -7
  77. package/src/approvals/guardian-request-resolvers.ts +1 -1
  78. package/src/background-wake/next-wake.ts +0 -1
  79. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  80. package/src/config/acp-defaults.test.ts +0 -10
  81. package/src/config/acp-defaults.ts +0 -6
  82. package/src/config/bundled-skills/acp/SKILL.md +31 -83
  83. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  84. package/src/config/bundled-skills/app-builder/SKILL.md +381 -224
  85. package/src/config/bundled-skills/app-builder/TOOLS.json +0 -29
  86. package/src/config/bundled-skills/document-editor/SKILL.md +23 -28
  87. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  88. package/src/config/bundled-tool-registry.ts +0 -2
  89. package/src/config/feature-flag-registry.json +5 -14
  90. package/src/config/schemas/heartbeat.ts +0 -9
  91. package/src/context/strip-injections.ts +2 -8
  92. package/src/context/window-manager.ts +1 -2
  93. package/src/daemon/conversation-agent-loop-handlers.ts +11 -0
  94. package/src/daemon/conversation-agent-loop.ts +279 -62
  95. package/src/daemon/conversation-runtime-assembly.ts +69 -106
  96. package/src/daemon/conversation-store.ts +90 -9
  97. package/src/daemon/conversation-workspace.ts +0 -17
  98. package/src/daemon/conversation.ts +6 -0
  99. package/src/daemon/external-plugins-bootstrap.ts +11 -11
  100. package/src/daemon/handlers/conversations.ts +1 -3
  101. package/src/daemon/handlers/skills.ts +1 -4
  102. package/src/daemon/lifecycle.ts +0 -21
  103. package/src/daemon/server.ts +0 -2
  104. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -3
  105. package/src/heartbeat/heartbeat-run-store.ts +1 -23
  106. package/src/heartbeat/heartbeat-service.ts +0 -26
  107. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  108. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  109. package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -15
  110. package/src/ipc/skill-routes/memory.ts +2 -4
  111. package/src/memory/conversation-starter-checkpoints.ts +0 -1
  112. package/src/memory/db-init.ts +0 -2
  113. package/src/memory/job-handlers/conversation-starters.ts +2 -13
  114. package/src/memory/jobs-worker.ts +1 -1
  115. package/src/memory/migrations/index.ts +0 -1
  116. package/src/memory/schema/acp.ts +0 -4
  117. package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
  118. package/src/memory/v2/consolidation-job.ts +4 -13
  119. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/assign.test.ts +4 -4
  120. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/live-integration.test.ts +4 -4
  121. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/maintain-job.test.ts +5 -5
  122. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/orchestrate.test.ts +3 -3
  123. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/reconcile.test.ts +2 -2
  124. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/render-injection.test.ts +1 -1
  125. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/router.test.ts +3 -3
  126. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/selection-log-store.test.ts +8 -8
  127. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/selector.test.ts +3 -3
  128. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/shadow-plugin.test.ts +12 -12
  129. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/assign.ts +5 -5
  130. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/capabilities.ts +2 -2
  131. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/maintain-job.ts +8 -8
  132. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/page-content.ts +2 -2
  133. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/provider-blocks.ts +1 -1
  134. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/reconcile.ts +3 -3
  135. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/render-injection.ts +1 -1
  136. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/router.ts +3 -3
  137. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/selection-log-store.ts +4 -4
  138. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/selector.ts +4 -4
  139. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/shadow-plugin.ts +90 -28
  140. package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/tree.ts +1 -1
  141. package/src/plugin-api/index.ts +5 -0
  142. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
  143. package/src/plugins/defaults/{memory-v3-shadow → circuit-breaker}/package.json +2 -2
  144. package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
  145. package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
  146. package/src/plugins/defaults/compaction/package.json +1 -1
  147. package/src/plugins/defaults/compaction/register.ts +19 -8
  148. package/src/plugins/defaults/compaction/terminal.ts +73 -0
  149. package/src/plugins/defaults/index.ts +5 -3
  150. package/src/plugins/defaults/{memory-retrieval/injectors.ts → injectors/register.ts} +7 -45
  151. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +7 -11
  152. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  153. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +126 -0
  154. package/src/plugins/defaults/overflow-reduce/package.json +15 -0
  155. package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
  156. package/src/plugins/external-api.ts +2 -2
  157. package/src/plugins/pipeline.ts +293 -6
  158. package/src/plugins/registry.ts +37 -9
  159. package/src/plugins/types.ts +336 -32
  160. package/src/plugins/user-loader.ts +127 -30
  161. package/src/proactive-artifact/aux-message-injector.ts +1 -1
  162. package/src/proactive-artifact/job.test.ts +1 -1
  163. package/src/prompts/__tests__/system-prompt.test.ts +0 -6
  164. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +2 -4
  165. package/src/runtime/__tests__/agent-wake.test.ts +5 -5
  166. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  167. package/src/runtime/agent-wake.ts +3 -0
  168. package/src/runtime/assistant-event-hub.ts +1 -1
  169. package/src/runtime/channel-approvals.ts +1 -1
  170. package/src/runtime/interactive-ui.ts +1 -1
  171. package/src/runtime/routes/__tests__/acp-routes.test.ts +55 -283
  172. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  173. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +4 -5
  174. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -4
  175. package/src/runtime/routes/acp-routes.test.ts +25 -89
  176. package/src/runtime/routes/acp-routes.ts +29 -81
  177. package/src/runtime/routes/approval-routes.ts +1 -1
  178. package/src/runtime/routes/browser-routes.ts +1 -1
  179. package/src/runtime/routes/browser-tabs-routes.ts +10 -6
  180. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  181. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  182. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  183. package/src/runtime/routes/conversation-routes.ts +2 -15
  184. package/src/runtime/routes/conversation-starter-routes.ts +7 -13
  185. package/src/runtime/routes/conversations-import-routes.ts +7 -24
  186. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  187. package/src/runtime/routes/host-cu-routes.ts +1 -1
  188. package/src/runtime/routes/identity-routes.ts +3 -18
  189. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  190. package/src/runtime/routes/memory-v3-routes.ts +6 -16
  191. package/src/runtime/routes/playground/helpers.ts +1 -1
  192. package/src/runtime/routes/surface-conversation-resolver.ts +3 -4
  193. package/src/runtime/routes/work-items-routes.ts +4 -2
  194. package/src/runtime/services/conversation-serializer.ts +1 -1
  195. package/src/signals/cancel.ts +4 -2
  196. package/src/subagent/manager.ts +5 -17
  197. package/src/tools/acp/list-agents.test.ts +1 -7
  198. package/src/tools/acp/spawn.test.ts +55 -158
  199. package/src/tools/acp/spawn.ts +72 -47
  200. package/src/tools/acp/steer.test.ts +8 -105
  201. package/src/tools/acp/steer.ts +17 -48
  202. package/src/tools/apps/executors.ts +8 -13
  203. package/src/tools/filesystem/write.ts +0 -34
  204. package/src/tools/subagent/spawn.ts +4 -2
  205. package/src/tools/ui-surface/definitions.ts +4 -25
  206. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +5 -4
  207. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +45 -69
  208. package/examples/plugins/echo/hooks/post-tool-use.ts +0 -18
  209. package/examples/plugins/echo/hooks/stop.ts +0 -16
  210. package/examples/plugins/echo/hooks/user-prompt-submit.ts +0 -18
  211. package/examples/plugins/echo/src/emit.ts +0 -19
  212. package/src/__tests__/compaction-circuit.test.ts +0 -258
  213. package/src/__tests__/compaction-direct.test.ts +0 -132
  214. package/src/__tests__/conversations-import-system-filter.test.ts +0 -101
  215. package/src/acp/__tests__/agent-process.test.ts +0 -161
  216. package/src/acp/__tests__/helpers/acp-history-db.ts +0 -82
  217. package/src/acp/__tests__/helpers/exec-file-stub.ts +0 -101
  218. package/src/acp/__tests__/session-manager-resume.test.ts +0 -736
  219. package/src/acp/auto-install.test.ts +0 -196
  220. package/src/acp/auto-install.ts +0 -177
  221. package/src/acp/feature-gate.test.ts +0 -48
  222. package/src/acp/feature-gate.ts +0 -34
  223. package/src/acp/resume-hint.ts +0 -25
  224. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +0 -48
  225. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +0 -57
  226. package/src/config/bundled-skills/app-builder/references/SLIDES.md +0 -38
  227. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -62
  228. package/src/daemon/conversation-registry.ts +0 -159
  229. package/src/daemon/overflow-reduction-loop.ts +0 -230
  230. package/src/memory/migrations/272-acp-session-history-cwd.ts +0 -36
  231. package/src/plugins/defaults/compaction/compact.ts +0 -59
  232. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +0 -14
  233. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +0 -19
  234. package/src/plugins/defaults/memory-v3-shadow/injector.ts +0 -75
  235. package/src/plugins/defaults/memory-v3-shadow/register.ts +0 -26
  236. package/src/tools/acp/context.ts +0 -20
  237. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/capabilities.test.ts +0 -0
  238. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/core.test.ts +0 -0
  239. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/fixtures/eval-turns.json +0 -0
  240. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/fixtures/live-turns.json +0 -0
  241. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/health.test.ts +0 -0
  242. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/needle.test.ts +0 -0
  243. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/provider-blocks.test.ts +0 -0
  244. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/snapshot.test.ts +0 -0
  245. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/tree.test.ts +0 -0
  246. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/types.test.ts +0 -0
  247. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/working-set-eviction.test.ts +0 -0
  248. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/__tests__/working-set-skeleton.test.ts +0 -0
  249. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/core.ts +0 -0
  250. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/README.md +0 -0
  251. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/assignments.json +0 -0
  252. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/core.json +0 -0
  253. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/leaves/domain-a/topic-x.md +0 -0
  254. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/leaves/domain-a/topic-y.md +0 -0
  255. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/data/leaves/domain-b/topic-z.md +0 -0
  256. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/health.ts +0 -0
  257. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/llm-retry.ts +0 -0
  258. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/needle.ts +0 -0
  259. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/orchestrate.ts +0 -0
  260. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/snapshot.ts +0 -0
  261. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/types.ts +0 -0
  262. /package/src/{plugins/defaults/memory-v3-shadow → memory/v3}/working-set.ts +0 -0
@@ -29,6 +29,7 @@ import {
29
29
  extractMemoryPrefixBlocks,
30
30
  stripAllMemoryInjections,
31
31
  } from "../memory/graph/conversation-graph-memory.js";
32
+ import { MEMORY_V3_BLOCK_ID } from "../memory/v3/types.js";
32
33
  import {
33
34
  readSlackMetadata,
34
35
  readSlackMetadataFromMessageMetadata,
@@ -42,8 +43,8 @@ import {
42
43
  renderSlackTranscriptWithProvenance,
43
44
  } from "../messaging/providers/slack/render-transcript.js";
44
45
  import { getInjectorChain } from "../plugins/defaults/memory-retrieval/injector-chain.js";
45
- import { MEMORY_V3_BLOCK_ID } from "../plugins/defaults/memory-v3-shadow/types.js";
46
46
  import type {
47
+ DiskPressureInjectionContext,
47
48
  InjectionBlock,
48
49
  InjectionPlacement,
49
50
  TurnContext,
@@ -59,7 +60,6 @@ import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/ac
59
60
  import type { SubagentState } from "../subagent/types.js";
60
61
  import { TERMINAL_STATUSES } from "../subagent/types.js";
61
62
  import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
62
- import { findConversationOrSubagent } from "./conversation-registry.js";
63
63
  import type {
64
64
  DynamicPageSurfaceData,
65
65
  SurfaceData,
@@ -270,8 +270,8 @@ export function isGroupChatType(chatType?: string): boolean {
270
270
  }
271
271
  }
272
272
 
273
- /** Context about the active workspace surface, rendered into the `<active_workspace>` block. */
274
- interface ActiveSurfaceContext {
273
+ /** Context about the active workspace surface, passed to applyRuntimeInjections. */
274
+ export interface ActiveSurfaceContext {
275
275
  surfaceId: string;
276
276
  html: string;
277
277
  /** When set, the surface is backed by a persisted app. */
@@ -352,6 +352,24 @@ export function buildActiveDocuments(conversationId: string): Array<{
352
352
  : null;
353
353
  }
354
354
 
355
+ /**
356
+ * Resolves the `<workspace>` top-level block for the runtime injector, or
357
+ * `null` when the turn isn't injecting it. The refresh runs every turn so a
358
+ * workspace-mutating tool's `markWorkspaceTopLevelDirty` is picked up on the
359
+ * following turn; it is dirty-guarded, so it only rescans when the cache is
360
+ * stale.
361
+ */
362
+ export function buildWorkspaceTopLevelContext(
363
+ ctx: {
364
+ refreshWorkspaceTopLevelContextIfNeeded(): void;
365
+ workspaceTopLevelContext: string | null;
366
+ },
367
+ shouldInject: boolean,
368
+ ): string | null {
369
+ ctx.refreshWorkspaceTopLevelContextIfNeeded();
370
+ return shouldInject ? ctx.workspaceTopLevelContext : null;
371
+ }
372
+
355
373
  const MAX_CONTEXT_LENGTH = 100_000;
356
374
 
357
375
  function truncateHtml(html: string, budget: number): string {
@@ -567,7 +585,7 @@ export function buildSubagentStatusBlock(
567
585
  }
568
586
 
569
587
  // The `<active_subagents>` block is emitted by the `subagent-status` default
570
- // injector (`plugins/defaults/memory-retrieval/injectors.ts`) as an `append-user-tail`
588
+ // injector (`plugins/defaults/injectors/register.ts`) as an `append-user-tail`
571
589
  // placement. Use {@link applyRuntimeInjections} with
572
590
  // `options.subagentStatusBlock` set, or drive the injector chain directly
573
591
  // via `collectInjectorBlocks`.
@@ -1822,19 +1840,25 @@ function applyInjectionBlock(
1822
1840
  * bag attached to the {@link TurnContext} the caller provides (or to an
1823
1841
  * ephemeral {@link TurnContext} synthesized for test call sites). A small
1824
1842
  * number of fields drive hardcoded branches that live outside the injector
1825
- * chain — `isNonInteractive` — because it is orchestrator-owned content that
1826
- * never made sense as a plugin-overridable default injector.
1827
- *
1828
- * The active workspace surface, the channel capabilities, the active document
1829
- * list, the channel command context, the voice call-control prompt, and the
1830
- * transport hints are not on this bag: `applyRuntimeInjections` resolves them
1831
- * from the live conversation itself (its surface state, `channelCapabilities`,
1832
- * the document store keyed by `conversationId`, its `commandIntent`, its
1833
- * `voiceCallControlPrompt`, and its `transportHints` respectively), so the
1834
- * orchestrator does not compute or thread them per turn.
1843
+ * chain — `activeSurface`, `channelCapabilities`, `channelCommandContext`,
1844
+ * `voiceCallControlPrompt`, `transportHints`, and `isNonInteractive`
1845
+ * because they are orchestrator-owned content that never made sense as
1846
+ * plugin-overridable default injectors.
1835
1847
  */
1836
1848
  export interface RuntimeInjectionOptions {
1849
+ diskPressureContext?: DiskPressureInjectionContext | null;
1850
+ /**
1851
+ * Active dashboard-surface context (read from `<active_workspace>`). Kept
1852
+ * on the options bag rather than an injector because it is a
1853
+ * channel-capability concern that has never been gated as a default
1854
+ * injector.
1855
+ */
1856
+ activeSurface?: ActiveSurfaceContext | null;
1857
+ workspaceTopLevelContext?: string | null;
1858
+ channelCapabilities?: ChannelCapabilities | null;
1859
+ channelCommandContext?: ChannelCommandContext | null;
1837
1860
  unifiedTurnContext?: string | null;
1861
+ voiceCallControlPrompt?: string | null;
1838
1862
  subagentStatusBlock?: string | null;
1839
1863
  isNonInteractive?: boolean;
1840
1864
  /**
@@ -1844,6 +1868,7 @@ export interface RuntimeInjectionOptions {
1844
1868
  * configured reminder.
1845
1869
  */
1846
1870
  isBackgroundConversation?: boolean;
1871
+ transportHints?: string[] | null;
1847
1872
  /**
1848
1873
  * Pre-rendered Slack chronological transcript that replaces the
1849
1874
  * default `runMessages` history for any Slack conversation (channels
@@ -1878,6 +1903,7 @@ export interface RuntimeInjectionOptions {
1878
1903
  * `undefined` when the inbound is a top-level (non-thread) post.
1879
1904
  */
1880
1905
  slackActiveThreadFocusBlock?: string | null;
1906
+ activeDocuments?: TurnInjectionInputs["activeDocuments"];
1881
1907
  mode?: InjectionMode;
1882
1908
  /**
1883
1909
  * Per-turn {@link TurnContext} forwarded to plugin-registered
@@ -1905,35 +1931,33 @@ export interface RuntimeInjectionOptions {
1905
1931
  */
1906
1932
  function buildTurnInjectionInputs(
1907
1933
  options: RuntimeInjectionOptions,
1908
- channelCapabilities: ChannelCapabilities | null,
1909
- activeDocuments: TurnInjectionInputs["activeDocuments"],
1910
1934
  ): TurnInjectionInputs {
1911
1935
  return {
1912
1936
  mode: options.mode,
1937
+ diskPressureContext: options.diskPressureContext,
1938
+ workspaceTopLevelContext: options.workspaceTopLevelContext,
1913
1939
  unifiedTurnContext: options.unifiedTurnContext,
1914
1940
  subagentStatusBlock: options.subagentStatusBlock,
1915
- channelCapabilities,
1941
+ channelCapabilities: options.channelCapabilities,
1916
1942
  slackChronologicalMessages: options.slackChronologicalMessages,
1917
1943
  slackActiveThreadFocusBlock: options.slackActiveThreadFocusBlock,
1944
+ activeSurface: options.activeSurface,
1945
+ channelCommandContext: options.channelCommandContext,
1946
+ voiceCallControlPrompt: options.voiceCallControlPrompt,
1947
+ transportHints: options.transportHints,
1918
1948
  isNonInteractive: options.isNonInteractive,
1919
1949
  isBackgroundConversation: options.isBackgroundConversation,
1920
- activeDocuments,
1950
+ activeDocuments: options.activeDocuments,
1921
1951
  };
1922
1952
  }
1923
1953
 
1924
- /**
1925
- * Conversation id used for the synthetic fallback {@link TurnContext} and the
1926
- * live-conversation lookup when the caller omits a context (test call sites).
1927
- */
1928
- const RUNTIME_ASSEMBLY_FALLBACK_CONVERSATION_ID = "runtime-assembly-fallback";
1929
-
1930
1954
  /** Minimal synthetic TurnContext used when the caller omits one. */
1931
1955
  function synthesizeFallbackTurnContext(
1932
1956
  inputs: TurnInjectionInputs,
1933
1957
  ): TurnContext {
1934
1958
  return {
1935
- requestId: RUNTIME_ASSEMBLY_FALLBACK_CONVERSATION_ID,
1936
- conversationId: RUNTIME_ASSEMBLY_FALLBACK_CONVERSATION_ID,
1959
+ requestId: "runtime-assembly-fallback",
1960
+ conversationId: "runtime-assembly-fallback",
1937
1961
  turnIndex: 0,
1938
1962
  trust: {
1939
1963
  sourceChannel: inputs.channelCapabilities?.channel
@@ -1974,9 +1998,6 @@ function synthesizeFallbackTurnContext(
1974
1998
  * 6. Run the remaining hardcoded branches (`isNonInteractive`,
1975
1999
  * `voiceCallControlPrompt`, `activeSurface`, `channelCapabilities`,
1976
2000
  * `channelCommandContext`, `transportHints`) in their historical order.
1977
- * `voiceCallControlPrompt`, `activeSurface`, `channelCapabilities`,
1978
- * `channelCommandContext`, and `transportHints` are sourced from the live
1979
- * conversation rather than `options`.
1980
2001
  * 7. Finally, apply the chain's remaining blocks by placement:
1981
2002
  * `"append-user-tail"` in ascending `order`, then `"prepend-user-tail"`
1982
2003
  * in descending `order` so the lowest-`order` prepend lands topmost in
@@ -1991,58 +2012,14 @@ export async function applyRuntimeInjections(
1991
2012
  options: RuntimeInjectionOptions,
1992
2013
  ): Promise<RuntimeInjectionResult> {
1993
2014
  const mode = options.mode ?? "full";
1994
-
1995
- // Source the channel capabilities from the live conversation rather than a
1996
- // per-turn option computed by the orchestrator. The same value drives the
1997
- // Slack-conversation gate, the `<channel_capabilities>` branch, and the
1998
- // per-injector inputs the Slack injectors read.
1999
- const conversationId =
2000
- options.turnContext?.conversationId ??
2001
- RUNTIME_ASSEMBLY_FALLBACK_CONVERSATION_ID;
2002
- const channelCapabilities =
2003
- findConversationOrSubagent(conversationId)?.channelCapabilities ?? null;
2004
- const slackConversation = channelCapabilities?.channel === "slack";
2005
-
2006
- // Source the active documents from the document store keyed by the same
2007
- // conversation id rather than a per-turn option computed by the
2008
- // orchestrator; the `active-documents` and `document-comments` injectors
2009
- // read the result off their per-injector inputs.
2010
- const activeDocuments = buildActiveDocuments(conversationId);
2011
-
2012
- // Source the channel command intent (e.g. Telegram /start) from the live
2013
- // conversation rather than a per-turn option. It is set on the conversation
2014
- // at message-processing start and cleared at turn end, so the same value
2015
- // drives the `<channel_command_context>` branch on every assembly call
2016
- // within the turn.
2017
- const channelCommandContext =
2018
- findConversationOrSubagent(conversationId)?.commandIntent ?? null;
2019
-
2020
- // Source the voice call-control prompt from the live conversation rather
2021
- // than a per-turn option. The voice-session bridge sets it on the
2022
- // conversation when a call attaches and clears it when the call ends, so
2023
- // the same value drives the `<voice_call_control>` branch on every assembly
2024
- // call within the turn.
2025
- const voiceCallControlPrompt =
2026
- findConversationOrSubagent(conversationId)?.voiceCallControlPrompt ?? null;
2027
-
2028
- // Source the transport hints from the live conversation rather than a
2029
- // per-turn option. They are set on the conversation from the inbound
2030
- // message's transport when the message is processed, so the same value
2031
- // drives the `<transport_hints>` branch on every assembly call within the
2032
- // turn.
2033
- const transportHints =
2034
- findConversationOrSubagent(conversationId)?.transportHints ?? null;
2015
+ const slackConversation = options.channelCapabilities?.channel === "slack";
2035
2016
 
2036
2017
  // Build the per-injector inputs and attach them to the caller's
2037
2018
  // TurnContext (without mutating it). When the caller didn't supply one,
2038
2019
  // synthesize a minimal fallback so the chain still runs — test call sites
2039
2020
  // that drive injection via `options` without constructing a full context
2040
2021
  // continue to work.
2041
- const injectionInputs = buildTurnInjectionInputs(
2042
- options,
2043
- channelCapabilities,
2044
- activeDocuments,
2045
- );
2022
+ const injectionInputs = buildTurnInjectionInputs(options);
2046
2023
  const turnCtx: TurnContext = options.turnContext
2047
2024
  ? { ...options.turnContext, injectionInputs }
2048
2025
  : synthesizeFallbackTurnContext(injectionInputs);
@@ -2216,56 +2193,42 @@ export async function applyRuntimeInjections(
2216
2193
  }
2217
2194
  }
2218
2195
 
2219
- if (voiceCallControlPrompt) {
2196
+ if (options.voiceCallControlPrompt) {
2220
2197
  const userTail = result[result.length - 1];
2221
2198
  if (userTail && userTail.role === "user") {
2222
2199
  result = [
2223
2200
  ...result.slice(0, -1),
2224
- injectVoiceCallControlContext(userTail, voiceCallControlPrompt),
2201
+ injectVoiceCallControlContext(userTail, options.voiceCallControlPrompt),
2225
2202
  ];
2226
2203
  }
2227
2204
  }
2228
2205
 
2229
- if (mode === "full") {
2230
- // Source the active workspace surface from the live conversation's surface
2231
- // state rather than from a per-turn option computed by the orchestrator.
2232
- const surfaceConversation = findConversationOrSubagent(
2233
- turnCtx.conversationId,
2234
- );
2235
- const activeSurface = surfaceConversation
2236
- ? buildActiveSurfaceContext({
2237
- currentActiveSurfaceId: surfaceConversation.currentActiveSurfaceId,
2238
- currentPage: surfaceConversation.currentPage,
2239
- surfaceState: surfaceConversation.surfaceState,
2240
- })
2241
- : null;
2242
- if (activeSurface) {
2243
- const userTail = result[result.length - 1];
2244
- if (userTail && userTail.role === "user") {
2245
- result = [
2246
- ...result.slice(0, -1),
2247
- injectActiveSurfaceContext(userTail, activeSurface),
2248
- ];
2249
- }
2206
+ if (mode === "full" && options.activeSurface) {
2207
+ const userTail = result[result.length - 1];
2208
+ if (userTail && userTail.role === "user") {
2209
+ result = [
2210
+ ...result.slice(0, -1),
2211
+ injectActiveSurfaceContext(userTail, options.activeSurface),
2212
+ ];
2250
2213
  }
2251
2214
  }
2252
2215
 
2253
- if (channelCapabilities) {
2216
+ if (options.channelCapabilities) {
2254
2217
  const userTail = result[result.length - 1];
2255
2218
  if (userTail && userTail.role === "user") {
2256
2219
  result = [
2257
2220
  ...result.slice(0, -1),
2258
- injectChannelCapabilityContext(userTail, channelCapabilities),
2221
+ injectChannelCapabilityContext(userTail, options.channelCapabilities),
2259
2222
  ];
2260
2223
  }
2261
2224
  }
2262
2225
 
2263
- if (mode === "full" && channelCommandContext) {
2226
+ if (mode === "full" && options.channelCommandContext) {
2264
2227
  const userTail = result[result.length - 1];
2265
2228
  if (userTail && userTail.role === "user") {
2266
2229
  result = [
2267
2230
  ...result.slice(0, -1),
2268
- injectChannelCommandContext(userTail, channelCommandContext),
2231
+ injectChannelCommandContext(userTail, options.channelCommandContext),
2269
2232
  ];
2270
2233
  }
2271
2234
  }
@@ -2279,14 +2242,14 @@ export async function applyRuntimeInjections(
2279
2242
  if (
2280
2243
  mode === "full" &&
2281
2244
  !slackConversation &&
2282
- transportHints &&
2283
- transportHints.length > 0
2245
+ options.transportHints &&
2246
+ options.transportHints.length > 0
2284
2247
  ) {
2285
2248
  const userTail = result[result.length - 1];
2286
2249
  if (userTail && userTail.role === "user") {
2287
2250
  result = [
2288
2251
  ...result.slice(0, -1),
2289
- injectTransportHints(userTail, transportHints),
2252
+ injectTransportHints(userTail, options.transportHints),
2290
2253
  ];
2291
2254
  }
2292
2255
  }
@@ -27,18 +27,99 @@ import { ProviderNotConfiguredError } from "../util/errors.js";
27
27
  import { getSandboxWorkingDir } from "../util/platform.js";
28
28
  import { Conversation } from "./conversation.js";
29
29
  import type { ConversationEvictor } from "./conversation-evictor.js";
30
- import {
31
- allConversations,
32
- clearConversations,
33
- conversationCount,
34
- conversationIds,
35
- deleteConversation,
36
- findConversation,
37
- setConversation,
38
- } from "./conversation-registry.js";
39
30
  import type { ConversationCreateOptions } from "./handlers/shared.js";
40
31
  import { buildTransportHints } from "./transport-hints.js";
41
32
 
33
+ // ── Private store ──────────────────────────────────────────────────
34
+
35
+ const conversations = new Map<string, Conversation>();
36
+
37
+ // ── Read helpers ───────────────────────────────────────────────────
38
+
39
+ export function findConversation(
40
+ conversationId: string,
41
+ ): Conversation | undefined {
42
+ return conversations.get(conversationId);
43
+ }
44
+
45
+ export function findConversationBySurfaceId(
46
+ surfaceId: string,
47
+ ): Conversation | undefined {
48
+ // Fast path: exact surfaceId match in surfaceState
49
+ for (const c of conversations.values()) {
50
+ if (c.surfaceState.has(surfaceId)) return c;
51
+ }
52
+
53
+ // Fallback: standalone app surfaces use "app-open-{appId}" IDs that
54
+ // were never part of any conversation. Extract the appId and find
55
+ // a conversation whose surfaceState has a surface for that app.
56
+ const appOpenPrefix = "app-open-";
57
+ if (surfaceId.startsWith(appOpenPrefix)) {
58
+ const appId = surfaceId.slice(appOpenPrefix.length);
59
+ for (const c of conversations.values()) {
60
+ for (const [, state] of c.surfaceState.entries()) {
61
+ const data = state.data as unknown as Record<string, unknown>;
62
+ if (data?.appId === appId) {
63
+ // Register this surfaceId so subsequent lookups are O(1)
64
+ c.surfaceState.set(surfaceId, state);
65
+ return c;
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ return undefined;
72
+ }
73
+
74
+ function conversationCount(): number {
75
+ return conversations.size;
76
+ }
77
+
78
+ /** Iterate over all active conversations. */
79
+ export function allConversations(): IterableIterator<Conversation> {
80
+ return conversations.values();
81
+ }
82
+
83
+ /** Iterate over all [id, conversation] entries. */
84
+ export function conversationEntries(): IterableIterator<
85
+ [string, Conversation]
86
+ > {
87
+ return conversations.entries();
88
+ }
89
+
90
+ /** Iterate over all active conversation IDs. */
91
+ function conversationIds(): IterableIterator<string> {
92
+ return conversations.keys();
93
+ }
94
+
95
+ // ── Write helpers ──────────────────────────────────────────────────
96
+
97
+ export function setConversation(
98
+ conversationId: string,
99
+ conversation: Conversation,
100
+ ): void {
101
+ conversations.set(conversationId, conversation);
102
+ }
103
+
104
+ export function deleteConversation(conversationId: string): boolean {
105
+ return conversations.delete(conversationId);
106
+ }
107
+
108
+ export function clearConversations(): void {
109
+ conversations.clear();
110
+ }
111
+
112
+ // ── Underlying Map (for the evictor, which takes a mutable ref) ───
113
+
114
+ /**
115
+ * Expose the raw Map for the {@link ConversationEvictor}, which needs
116
+ * a mutable reference to delete entries during sweeps. No other code
117
+ * should use this — prefer the named helpers above.
118
+ */
119
+ export function getConversationMap(): Map<string, Conversation> {
120
+ return conversations;
121
+ }
122
+
42
123
  // ── Per-conversation persistent options ────────────────────────────
43
124
 
44
125
  const conversationOptions = new Map<string, ConversationCreateOptions>();
@@ -4,7 +4,6 @@ import { getConversation } from "../memory/conversation-crud.js";
4
4
  import { resolveConversationDirectoryPaths } from "../memory/conversation-directories.js";
5
5
  import { renderWorkspaceTopLevelContext } from "../workspace/top-level-renderer.js";
6
6
  import { scanTopLevelDirectories } from "../workspace/top-level-scanner.js";
7
- import { findConversationOrSubagent } from "./conversation-registry.js";
8
7
 
9
8
  /**
10
9
  * Subset of Conversation state that workspace context helpers need.
@@ -26,22 +25,6 @@ export interface WorkspaceConversationContext {
26
25
  hostUsername?: string;
27
26
  }
28
27
 
29
- /**
30
- * Resolve the live workspace top-level block for a conversation, refreshing
31
- * the dirty-guarded cache first so a workspace-mutating tool's
32
- * `markWorkspaceTopLevelDirty` from the prior turn is picked up. Returns `null`
33
- * when no live conversation is found (no active conversation, or a context with
34
- * no conversation id) or when the rendered context is empty.
35
- */
36
- export function resolveWorkspaceTopLevelContext(
37
- conversationId: string | undefined,
38
- ): string | null {
39
- const conversation = findConversationOrSubagent(conversationId);
40
- if (!conversation) return null;
41
- refreshWorkspaceTopLevelContextIfNeeded(conversation);
42
- return conversation.workspaceTopLevelContext;
43
- }
44
-
45
28
  /** Refresh workspace top-level directory context if needed. */
46
29
  export function refreshWorkspaceTopLevelContextIfNeeded(
47
30
  ctx: WorkspaceConversationContext,
@@ -128,6 +128,7 @@ import {
128
128
  createResolveToolsCallback,
129
129
  createToolExecutor,
130
130
  } from "./conversation-tool-setup.js";
131
+ import { refreshWorkspaceTopLevelContextIfNeeded as refreshWorkspaceImpl } from "./conversation-workspace.js";
131
132
  import { canonicalizeTimeZone } from "./date-context.js";
132
133
  import { HostAppControlProxy } from "./host-app-control-proxy.js";
133
134
  import { HostCuProxy } from "./host-cu-proxy.js";
@@ -1098,6 +1099,7 @@ export class Conversation {
1098
1099
  // etc.) — skip those so they don't silently reset the counter.
1099
1100
  if (result.summaryFailed !== undefined) {
1100
1101
  await this.agentLoop.compactionCircuit.recordOutcome(
1102
+ this,
1101
1103
  result.summaryFailed,
1102
1104
  this.sendToClient,
1103
1105
  );
@@ -1344,6 +1346,10 @@ export class Conversation {
1344
1346
 
1345
1347
  // ── Workspace ────────────────────────────────────────────────────
1346
1348
 
1349
+ refreshWorkspaceTopLevelContextIfNeeded(): void {
1350
+ refreshWorkspaceImpl(this);
1351
+ }
1352
+
1347
1353
  markWorkspaceTopLevelDirty(): void {
1348
1354
  this.workspaceTopLevelDirty = true;
1349
1355
  }
@@ -2,22 +2,21 @@
2
2
  * Plugin bootstrap — runs every registered plugin's `init()` hook once during
3
3
  * daemon startup.
4
4
  *
5
- * The registry is populated before this runs: first-party defaults are
6
- * registered explicitly (step 1 below), user plugins are registered by the
7
- * user-plugin loader from the workspace `plugins/` directory, and hot-reload
8
- * registers at runtime. By the time {@link bootstrapPlugins} runs, the
9
- * registry has been fully populated for this boot cycle. This function:
5
+ * Plugins register themselves via side-effect imports elsewhere in the boot
6
+ * sequence (first-party) or at runtime (hot-reload). By the time
7
+ * {@link bootstrapPlugins} runs, the registry has been fully populated for
8
+ * this boot cycle. This function:
10
9
  *
11
10
  * 1. Registers the canonical first-party default plugins via
12
- * {@link registerDefaultPlugins}. Registration is idempotent so repeat
13
- * calls (e.g. during integration tests) do not throw.
11
+ * {@link registerDefaultPlugins} (one per pipeline). Registration is
12
+ * idempotent so repeat calls (e.g. during integration tests) do not throw.
14
13
  * 2. Walks {@link getRegisteredPlugins} in registration order.
15
14
  * 3. For each plugin, consults `manifest.requiresFlag` against
16
15
  * {@link isAssistantFeatureFlagEnabled}. If any listed flag is disabled,
17
16
  * the plugin is skipped wholesale — no `init()`, no tool/route/skill
18
17
  * contributions, no entry in the shutdown hook, and the plugin is also
19
- * dropped from the registry via {@link unregisterPlugin} so none of its
20
- * hooks participate in the turn lifecycle. This is the primary mechanism for
18
+ * dropped from the registry via {@link unregisterPlugin} so its middleware
19
+ * stops participating in pipeline runs. This is the primary mechanism for
21
20
  * shipping experimental plugins behind a feature flag.
22
21
  * 4. Resolves the plugin's `manifest.requiresCredential` entries via the
23
22
  * credential store helper ({@link getSecureKeyAsync}). In Docker mode
@@ -262,8 +261,9 @@ export async function bootstrapPlugins(): Promise<void> {
262
261
  // Tear down a plugin's contributions AND remove it from the registry. The
263
262
  // two steps always move together on the bootstrap failure path: the former
264
263
  // clears tools/routes/skills (so they stop appearing to the model/HTTP
265
- // server), the latter drops the plugin's entry from the Map (so the registry
266
- // doesn't expose an uninitialized plugin's hooks).
264
+ // server), the latter drops the plugin's entry from the Map (so
265
+ // `getMiddlewaresFor` doesn't re-enter an uninitialized plugin on the next
266
+ // pipeline invocation).
267
267
  // Shutdown context is identical for every plugin in this boot — construct
268
268
  // once and reuse across the bootstrap-failure rollback and the normal
269
269
  // shutdown hook below. Only `assistantVersion` is exposed today; future
@@ -8,11 +8,9 @@ import { createAbortReason } from "../../util/abort-reasons.js";
8
8
  import { truncate } from "../../util/truncate.js";
9
9
  import { regenerate } from "../conversation-history.js";
10
10
  import {
11
+ clearAllActiveConversations,
11
12
  conversationEntries,
12
13
  findConversation,
13
- } from "../conversation-registry.js";
14
- import {
15
- clearAllActiveConversations,
16
14
  getOrCreateConversation,
17
15
  touchConversation,
18
16
  } from "../conversation-store.js";
@@ -31,10 +31,7 @@ import {
31
31
  isTextMimeType as isTextMime,
32
32
  MAX_INLINE_TEXT_SIZE,
33
33
  } from "../../runtime/routes/workspace-utils.js";
34
- import {
35
- getCachedCatalogSync,
36
- getCatalog,
37
- } from "../../skills/catalog-cache.js";
34
+ import { getCachedCatalogSync, getCatalog } from "../../skills/catalog-cache.js";
38
35
  import type { SkillFileEntry } from "../../skills/catalog-files.js";
39
36
  import {
40
37
  catalogSkillToSlim,
@@ -98,7 +98,6 @@ import {
98
98
  updateWorkItem,
99
99
  } from "../work-items/work-item-store.js";
100
100
  import { WorkspaceHeartbeatService } from "../workspace/heartbeat-service.js";
101
- import { repairAdaptiveThinkingOnManagedProfiles } from "../workspace/migrations/097-enable-adaptive-thinking-managed-profiles.js";
102
101
  import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
103
102
  import { runWorkspaceMigrations } from "../workspace/migrations/runner.js";
104
103
  import {
@@ -590,26 +589,6 @@ export async function runDaemon(): Promise<void> {
590
589
  );
591
590
  }
592
591
 
593
- // Re-run the adaptive thinking repair after overlay merge + profile seeding.
594
- // Workspace migration 097 enables adaptive thinking on managed profiles, but
595
- // it runs before mergeDefaultWorkspaceConfig() which can overwrite the fix
596
- // with overlay profiles that have thinking disabled or absent. On-platform
597
- // instances where the overlay supplies "balanced" / "quality-optimized"
598
- // profiles without thinking enabled would be stuck permanently because the
599
- // migration is already checkpointed as completed. This idempotent repair
600
- // ensures thinking is enabled regardless of overlay ordering.
601
- if (defaultConfigMerge.hadOverlay) {
602
- try {
603
- repairAdaptiveThinkingOnManagedProfiles(getWorkspaceDir());
604
- log.info("Post-overlay adaptive thinking repair complete");
605
- } catch (err) {
606
- log.warn(
607
- { err },
608
- "Post-overlay adaptive thinking repair failed — continuing startup",
609
- );
610
- }
611
- }
612
-
613
592
  log.info("Daemon startup: loading config");
614
593
  const config = loadConfig();
615
594
 
@@ -39,8 +39,6 @@ import {
39
39
  conversationEntries,
40
40
  deleteConversation,
41
41
  getConversationMap,
42
- } from "./conversation-registry.js";
43
- import {
44
42
  getOrCreateConversation as getOrCreateActiveConversation,
45
43
  initConversationLifecycle,
46
44
  setCesClientPromise,
@@ -85,7 +85,6 @@ const stubConfig: {
85
85
  activeHoursStart: number | null;
86
86
  activeHoursEnd: number | null;
87
87
  maxConsecutiveRuns: number | null;
88
- maxDailyRuns: number | null;
89
88
  disposition: string;
90
89
  };
91
90
  } = {
@@ -95,7 +94,6 @@ const stubConfig: {
95
94
  activeHoursStart: null,
96
95
  activeHoursEnd: null,
97
96
  maxConsecutiveRuns: null,
98
- maxDailyRuns: null,
99
97
  disposition: "Default disposition text.",
100
98
  },
101
99
  };
@@ -197,7 +195,6 @@ mock.module("../heartbeat-run-store.js", () => ({
197
195
  markStaleRunsAsMissed: () => 0,
198
196
  markStaleRunningAsError: () => 0,
199
197
  countCompletedHeartbeatRuns: () => 10,
200
- countCompletedRunsToday: () => 0,
201
198
  countRecentConsecutiveRuns: () => 0,
202
199
  }));
203
200
 
@@ -24,8 +24,7 @@ export type HeartbeatSkipReason =
24
24
  | "outside_active_hours"
25
25
  | "overlap"
26
26
  | "pre_first_user_message"
27
- | "max_consecutive_runs"
28
- | "max_daily_runs";
27
+ | "max_consecutive_runs";
29
28
 
30
29
  export interface HeartbeatRunRecord {
31
30
  id: string;
@@ -218,27 +217,6 @@ export function countCompletedHeartbeatRuns(): number {
218
217
  return row?.count ?? 0;
219
218
  }
220
219
 
221
- /**
222
- * Count heartbeat runs that completed with status `ok` today (local midnight).
223
- */
224
- export function countCompletedRunsToday(): number {
225
- const db = getDb();
226
- const now = new Date();
227
- const startOfDay = new Date(
228
- now.getFullYear(),
229
- now.getMonth(),
230
- now.getDate(),
231
- ).getTime();
232
- const row = db
233
- .select({ count: sql<number>`count(*)` })
234
- .from(heartbeatRuns)
235
- .where(
236
- sql`${heartbeatRuns.status} = 'ok' AND ${heartbeatRuns.scheduledFor} >= ${startOfDay}`,
237
- )
238
- .get();
239
- return row?.count ?? 0;
240
- }
241
-
242
220
  /**
243
221
  * Count the most recent consecutive heartbeat runs with status `ok`,
244
222
  * walking backwards from newest. Stops at the first non-`ok` status