@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
@@ -1,62 +0,0 @@
1
- import * as appStore from "../../../../memory/app-store.js";
2
- import type {
3
- ToolContext,
4
- ToolExecutionResult,
5
- } from "../../../../tools/types.js";
6
-
7
- interface AppListEntry {
8
- app_id: string;
9
- name: string;
10
- description?: string;
11
- updated_at: number;
12
- created_at: number;
13
- }
14
-
15
- function toEntry(app: appStore.AppDefinition): AppListEntry {
16
- return {
17
- app_id: app.id,
18
- name: app.name,
19
- ...(app.description ? { description: app.description } : {}),
20
- updated_at: app.updatedAt,
21
- created_at: app.createdAt,
22
- };
23
- }
24
-
25
- export async function run(
26
- input: Record<string, unknown>,
27
- _context: ToolContext,
28
- ): Promise<ToolExecutionResult> {
29
- const apps = appStore.listApps();
30
- const entries = apps.map(toEntry);
31
-
32
- const query =
33
- typeof input.query === "string" ? input.query.trim().toLowerCase() : "";
34
-
35
- if (!query) {
36
- return {
37
- content: JSON.stringify({ count: entries.length, apps: entries }),
38
- isError: false,
39
- };
40
- }
41
-
42
- // Resolve the app the user mentioned by name. Prefer an exact (case-insensitive)
43
- // name match, then fall back to substring matches in either direction so
44
- // "habit tracker" resolves "Habit Tracker" and "my budget" resolves "Budget".
45
- const exact = entries.filter((e) => e.name.toLowerCase() === query);
46
- const matches =
47
- exact.length > 0
48
- ? exact
49
- : entries.filter((e) => {
50
- const name = e.name.toLowerCase();
51
- return name.includes(query) || query.includes(name);
52
- });
53
-
54
- return {
55
- content: JSON.stringify({
56
- query: input.query,
57
- match_count: matches.length,
58
- matches,
59
- }),
60
- isError: false,
61
- };
62
- }
@@ -1,159 +0,0 @@
1
- /**
2
- * In-memory registry of active {@link Conversation} instances, keyed by
3
- * conversation ID, plus the read/write accessors over it.
4
- *
5
- * This is a leaf module: it imports `Conversation` as a type only, so any
6
- * layer — including the memory-retrieval plugin's injectors, which only know a
7
- * conversation id — can look up the live conversation and read its state
8
- * without pulling in the daemon-core creation graph (providers, system-prompt
9
- * assembly, the `Conversation` class value) that `getOrCreateConversation` in
10
- * `conversation-store` depends on. Keeping the registry free of those value
11
- * imports is what lets the injector chain consume it without forming an import
12
- * cycle (`injectors → store → conversation → agent-loop → runtime-assembly →
13
- * injector-chain → injectors`).
14
- *
15
- * `conversation-store` owns the creation/reuse lifecycle and writes top-level
16
- * conversations into this map via {@link setConversation}.
17
- */
18
-
19
- import type { Conversation } from "./conversation.js";
20
-
21
- const conversations = new Map<string, Conversation>();
22
-
23
- // ── Read helpers ───────────────────────────────────────────────────
24
-
25
- export function findConversation(
26
- conversationId: string | undefined,
27
- ): Conversation | undefined {
28
- if (!conversationId) return undefined;
29
- return conversations.get(conversationId);
30
- }
31
-
32
- export function findConversationBySurfaceId(
33
- surfaceId: string,
34
- ): Conversation | undefined {
35
- // Fast path: exact surfaceId match in surfaceState
36
- for (const c of conversations.values()) {
37
- if (c.surfaceState.has(surfaceId)) return c;
38
- }
39
-
40
- // Fallback: standalone app surfaces use "app-open-{appId}" IDs that
41
- // were never part of any conversation. Extract the appId and find
42
- // a conversation whose surfaceState has a surface for that app.
43
- const appOpenPrefix = "app-open-";
44
- if (surfaceId.startsWith(appOpenPrefix)) {
45
- const appId = surfaceId.slice(appOpenPrefix.length);
46
- for (const c of conversations.values()) {
47
- for (const [, state] of c.surfaceState.entries()) {
48
- const data = state.data as unknown as Record<string, unknown>;
49
- if (data?.appId === appId) {
50
- // Register this surfaceId so subsequent lookups are O(1)
51
- c.surfaceState.set(surfaceId, state);
52
- return c;
53
- }
54
- }
55
- }
56
- }
57
-
58
- return undefined;
59
- }
60
-
61
- export function conversationCount(): number {
62
- return conversations.size;
63
- }
64
-
65
- /** Iterate over all active conversations. */
66
- export function allConversations(): IterableIterator<Conversation> {
67
- return conversations.values();
68
- }
69
-
70
- /** Iterate over all [id, conversation] entries. */
71
- export function conversationEntries(): IterableIterator<
72
- [string, Conversation]
73
- > {
74
- return conversations.entries();
75
- }
76
-
77
- /** Iterate over all active conversation IDs. */
78
- export function conversationIds(): IterableIterator<string> {
79
- return conversations.keys();
80
- }
81
-
82
- // ── Write helpers ──────────────────────────────────────────────────
83
-
84
- export function setConversation(
85
- conversationId: string,
86
- conversation: Conversation,
87
- ): void {
88
- conversations.set(conversationId, conversation);
89
- }
90
-
91
- export function deleteConversation(conversationId: string): boolean {
92
- return conversations.delete(conversationId);
93
- }
94
-
95
- export function clearConversations(): void {
96
- conversations.clear();
97
- }
98
-
99
- // ── Underlying Map (for the evictor, which takes a mutable ref) ───
100
-
101
- /**
102
- * Expose the raw Map for the {@link ConversationEvictor}, which needs
103
- * a mutable reference to delete entries during sweeps. No other code
104
- * should use this — prefer the named helpers above.
105
- */
106
- export function getConversationMap(): Map<string, Conversation> {
107
- return conversations;
108
- }
109
-
110
- // ── Subagent live-conversation index ───────────────────────────────
111
- //
112
- // Subagents run their own agent loop — and therefore the injector chain — but
113
- // are owned by the `SubagentManager` rather than the daemon's eviction-managed
114
- // store, so they are deliberately absent from `conversations` above. This
115
- // point-lookup index lets the per-conversation injectors (workspace context,
116
- // disk-pressure warning) resolve a subagent's live `Conversation` by id and
117
- // read state off it. It is intentionally excluded from the iteration accessors
118
- // and the evictor map so subagent lifecycle stays solely with the manager.
119
-
120
- const subagentConversations = new Map<string, Conversation>();
121
-
122
- /** Register a subagent's live conversation for point lookups. */
123
- export function setSubagentConversation(
124
- conversationId: string,
125
- conversation: Conversation,
126
- ): void {
127
- subagentConversations.set(conversationId, conversation);
128
- }
129
-
130
- /**
131
- * Remove a subagent's conversation from the index. Guards against clobbering a
132
- * newer registration for the same id by only deleting when the stored entry
133
- * still points at this instance.
134
- */
135
- export function removeSubagentConversation(
136
- conversationId: string,
137
- conversation: Conversation,
138
- ): void {
139
- if (subagentConversations.get(conversationId) === conversation) {
140
- subagentConversations.delete(conversationId);
141
- }
142
- }
143
-
144
- /**
145
- * Resolve a live `Conversation` by id, including subagent conversations.
146
- * Top-level conversations are checked first, then subagents. Used by the
147
- * per-conversation injectors, which run for subagent turns too; other callers
148
- * should use {@link findConversation}, which is scoped to the eviction-managed
149
- * top-level conversations.
150
- */
151
- export function findConversationOrSubagent(
152
- conversationId: string | undefined,
153
- ): Conversation | undefined {
154
- if (!conversationId) return undefined;
155
- return (
156
- conversations.get(conversationId) ??
157
- subagentConversations.get(conversationId)
158
- );
159
- }
@@ -1,230 +0,0 @@
1
- import type { ContextWindowConfig } from "../config/schemas/inference.js";
2
- import type {
3
- ContextWindowCompactOptions,
4
- ContextWindowResult,
5
- } from "../context/window-manager.js";
6
- import type { Message } from "../providers/types.js";
7
- import {
8
- createInitialReducerState,
9
- reduceContextOverflow,
10
- type ReducerState,
11
- } from "./context-overflow-reducer.js";
12
- import type { InjectionMode } from "./conversation-runtime-assembly.js";
13
-
14
- /**
15
- * Input to the overflow-reduction loop. Captures everything the reducer
16
- * tier loop needs, including the message history, reducer configuration,
17
- * and side-effect callbacks that bridge the loop back to the orchestrator's
18
- * mutable per-turn state (context-window manager, activity emitter, runtime
19
- * injection reassembly, memory reinjection).
20
- *
21
- * The callbacks are supplied by the orchestrator because the reducer loop
22
- * needs to coordinate with state that lives on the `AgentLoopConversationContext`
23
- * (message mutation, compaction event emission, circuit breaker tracking,
24
- * injection block reassembly). Keeping them as explicit callbacks keeps the
25
- * loop free of any dependency on the agent-loop context object.
26
- */
27
- export interface OverflowReduceArgs {
28
- /** Bare persisted message history (the reducer applies results to a copy
29
- * of this array). */
30
- readonly messages: Message[];
31
- /** Current run-time message array with runtime injections applied. */
32
- readonly runMessages: Message[];
33
- /** System prompt used for post-step token estimation. */
34
- readonly systemPrompt: string;
35
- /** Provider name used for token estimation (calibration provider key). */
36
- readonly providerName: string;
37
- /** Context window config (drives compaction behavior). */
38
- readonly contextWindow: ContextWindowConfig;
39
- /** Token budget the reducer must get below (preflight budget). */
40
- readonly preflightBudget: number;
41
- /** Tool-token overhead included in every estimation call. */
42
- readonly toolTokenBudget?: number;
43
- /** Maximum reducer iterations before the loop exits unconditionally. */
44
- readonly maxAttempts: number;
45
- /** Abort signal threaded through compaction calls. */
46
- readonly abortSignal?: AbortSignal;
47
- /**
48
- * Compaction callback — the loop never owns the ContextWindowManager
49
- * instance. The orchestrator supplies this closure so the loop can
50
- * delegate the forced-compaction tier without crossing the infra
51
- * boundary on its own.
52
- */
53
- readonly compactFn: (
54
- messages: Message[],
55
- signal: AbortSignal | undefined,
56
- options: unknown,
57
- ) => Promise<ContextWindowResult>;
58
- /**
59
- * Invoked before each reducer iteration to emit the `context_compacting`
60
- * activity state. The orchestrator owns activity emission because the
61
- * signal is trust/channel aware.
62
- */
63
- readonly emitActivityState: () => void;
64
- /**
65
- * Invoked after each reducer step that produced a successful compaction.
66
- * Handles circuit-breaker tracking, event emission, and context mutation.
67
- */
68
- readonly onCompactionResult: (
69
- result: ContextWindowResult,
70
- compactedBasis?: Message[],
71
- ) => void | Promise<void>;
72
- /**
73
- * Invoked after each step to rebuild `runMessages` from the step's
74
- * reduced history with the requested injection mode. The orchestrator
75
- * owns this helper so the full per-turn injection options object doesn't
76
- * leak into the loop. The current reduced messages array is passed
77
- * explicitly so the orchestrator doesn't need to read mutable shared
78
- * state. Returns the new `runMessages`.
79
- *
80
- * Two distinct "did compact" signals are passed so the orchestrator can
81
- * apply the correct per-iteration vs sticky gating:
82
- * - `stepCompacted` — whether THIS iteration's reducer step produced a
83
- * fresh compaction. Gates PKB / NOW re-injection: compaction strips the
84
- * existing blocks, so only iterations that just compacted need the
85
- * content re-threaded. Iterations that only truncated tool results or
86
- * downgraded injections must NOT force a re-injection or the token
87
- * count grows each round.
88
- * - `accumulatedCompacted` — whether ANY iteration in this invocation has
89
- * compacted. Gates `slackChronologicalMessages` suppression: once
90
- * compaction has run, the captured Slack transcript snapshot would
91
- * overwrite the compacted history, so it must stay suppressed for every
92
- * subsequent iteration even if that iteration didn't re-compact.
93
- */
94
- readonly reinjectForMode: (
95
- messages: Message[],
96
- mode: InjectionMode,
97
- stepCompacted: boolean,
98
- accumulatedCompacted: boolean,
99
- ) => Promise<Message[]>;
100
- /**
101
- * Invoked after each step to post-estimate the rebuilt `runMessages`.
102
- * Pulled out so the orchestrator controls how estimation is performed
103
- * (and which fields feed it) without the loop reimplementing it.
104
- */
105
- readonly estimatePostInjection: (runMessages: Message[]) => number;
106
- }
107
-
108
- /** Output of the overflow-reduction loop. */
109
- export interface OverflowReduceResult {
110
- /** Final reduced `ctx.messages` value. */
111
- readonly messages: Message[];
112
- /** Final `runMessages` with re-applied runtime injections. */
113
- readonly runMessages: Message[];
114
- /** Final injection mode (may be `"minimal"` if the downgrade tier fired). */
115
- readonly injectionMode: InjectionMode;
116
- /** Accumulated reducer state at exit. */
117
- readonly reducerState: ReducerState;
118
- /** True if any step successfully compacted history. */
119
- readonly reducerCompacted: boolean;
120
- /** How many iterations of the tier loop executed. */
121
- readonly attempts: number;
122
- }
123
-
124
- /**
125
- * Run the context-overflow reducer tier loop — forced compaction, tool-result
126
- * truncation, media stubbing, injection downgrade — plus the post-step
127
- * re-injection / re-estimation convergence check.
128
- *
129
- * The forced-compaction tier is delegated through `args.compactFn`; the other
130
- * tiers mutate the message array directly. After each step the orchestrator
131
- * rebuilds `runMessages` via `args.reinjectForMode` and the loop re-estimates
132
- * the post-injection token count, exiting once it fits the preflight budget,
133
- * the reducer is exhausted, or `maxAttempts` is reached.
134
- */
135
- export async function runOverflowReductionLoop(
136
- args: OverflowReduceArgs,
137
- ): Promise<OverflowReduceResult> {
138
- let messages = args.messages;
139
- let runMessages = args.runMessages;
140
- let injectionMode: "full" | "minimal" = "full";
141
- let reducerState: ReducerState = createInitialReducerState();
142
- let reducerCompacted = false;
143
- let attempts = 0;
144
-
145
- while (attempts < args.maxAttempts && !reducerState.exhausted) {
146
- // Abort check at the top of every iteration. When the caller aborts
147
- // externally, this check lets us bail out BETWEEN iterations rather
148
- // than letting another round of compaction / re-injection mutate
149
- // `ctx.messages` after the turn has already failed. Individual
150
- // `reduceContextOverflow` calls also honor the signal, but without this
151
- // gate a fresh iteration could still start after the signal fires,
152
- // since the previous one returned normally before the abort propagated.
153
- args.abortSignal?.throwIfAborted();
154
-
155
- attempts++;
156
- args.emitActivityState();
157
-
158
- const basisMessages = messages;
159
- const step = await reduceContextOverflow(
160
- basisMessages,
161
- {
162
- providerName: args.providerName,
163
- systemPrompt: args.systemPrompt,
164
- contextWindow: args.contextWindow,
165
- targetTokens: args.preflightBudget,
166
- toolTokenBudget: args.toolTokenBudget,
167
- },
168
- reducerState,
169
- (msgs, signal, opts: ContextWindowCompactOptions) =>
170
- args.compactFn(msgs, signal, opts),
171
- args.abortSignal,
172
- );
173
-
174
- reducerState = step.state;
175
- messages = step.messages;
176
- injectionMode = step.state.injectionMode;
177
-
178
- // Per-iteration compaction flag: whether THIS step just produced a
179
- // fresh compaction. PKB / NOW re-injection is gated on this — see the
180
- // reinjectForMode JSDoc for why the two signals differ.
181
- const stepCompacted = step.compactionResult?.compacted === true;
182
-
183
- // Let the orchestrator apply compaction side effects (circuit-breaker
184
- // tracking, event emission, ctx mutation) before we re-inject.
185
- if (step.compactionResult) {
186
- await args.onCompactionResult(step.compactionResult, basisMessages);
187
- if (stepCompacted) {
188
- reducerCompacted = true;
189
- }
190
- }
191
-
192
- // Second abort gate — if the side effects or the step itself took us
193
- // past the deadline, don't rebuild runMessages or iterate again.
194
- args.abortSignal?.throwIfAborted();
195
-
196
- // Rebuild runMessages via the orchestrator-supplied helper (which
197
- // re-runs `applyRuntimeInjections` with potentially downgraded mode
198
- // and freshly re-hydrated PKB/NOW blocks after compaction). We pass
199
- // the current reduced `messages` explicitly so the orchestrator never
200
- // has to read from mutable shared state to rebuild runMessages — a
201
- // tier that doesn't trigger compaction (tool-result truncation, media
202
- // stubbing) won't update `ctx.messages` on its own.
203
- //
204
- // `stepCompacted` and `reducerCompacted` are both passed so the
205
- // orchestrator can gate PKB / NOW re-injection per-iteration while
206
- // keeping `slackChronologicalMessages` suppressed once any iteration
207
- // has compacted.
208
- runMessages = await args.reinjectForMode(
209
- messages,
210
- injectionMode,
211
- stepCompacted,
212
- reducerCompacted,
213
- );
214
-
215
- // Re-estimate with injections included — `step.estimatedTokens` was
216
- // computed on bare history and doesn't account for tokens added by
217
- // runtime injections.
218
- const postInjectionTokens = args.estimatePostInjection(runMessages);
219
- if (postInjectionTokens <= args.preflightBudget) break;
220
- }
221
-
222
- return {
223
- messages,
224
- runMessages,
225
- injectionMode,
226
- reducerState,
227
- reducerCompacted,
228
- attempts,
229
- };
230
- }
@@ -1,36 +0,0 @@
1
- import type { DrizzleDb } from "../db-connection.js";
2
- import { getSqliteFrom } from "../db-connection.js";
3
- import { withCrashRecovery } from "./validate-migration-state.js";
4
-
5
- const CHECKPOINT_KEY = "migration_acp_session_history_cwd_v1";
6
-
7
- const TABLE = "acp_session_history";
8
- const COLUMN = "cwd";
9
-
10
- /**
11
- * Add a nullable `cwd TEXT` column to `acp_session_history`.
12
- *
13
- * Records the working directory the ACP agent process was spawned with so
14
- * that a persisted session can later be resumed (`session/load` /
15
- * `session/resume` both require the original cwd to reconstruct params).
16
- *
17
- * `NULL` for rows persisted before this migration ran — legacy sessions
18
- * simply are not resumable.
19
- *
20
- * Idempotent — the PRAGMA guard makes re-running a no-op once the column
21
- * exists.
22
- */
23
- export function migrateAcpSessionHistoryCwd(database: DrizzleDb): void {
24
- withCrashRecovery(database, CHECKPOINT_KEY, () => {
25
- const raw = getSqliteFrom(database);
26
-
27
- const columns = raw.query(`PRAGMA table_info(${TABLE})`).all() as Array<{
28
- name: string;
29
- }>;
30
- const columnNames = new Set(columns.map((c) => c.name));
31
-
32
- if (!columnNames.has(COLUMN)) {
33
- raw.exec(`ALTER TABLE ${TABLE} ADD COLUMN ${COLUMN} TEXT`);
34
- }
35
- });
36
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Default compaction implementation.
3
- *
4
- * Summarizes conversation history when the context window fills up by
5
- * delegating to a {@link ContextWindowManager}. The agent loop calls
6
- * {@link defaultCompact} directly with a {@link CompactionContext}; the request
7
- * carries the conversational inputs while the manager performs the work.
8
- *
9
- * This module is side-effect free: importing it does not register any plugin.
10
- */
11
-
12
- import type {
13
- ContextWindowManager,
14
- ContextWindowResult,
15
- } from "../../../context/window-manager.js";
16
- import type { Message } from "../../../providers/types.js";
17
- import type { TrustClass } from "../../../runtime/actor-trust-resolver.js";
18
-
19
- /**
20
- * Name under which the default compaction plugin registers. Exposed so call
21
- * sites can attribute compaction failures to the default plugin.
22
- */
23
- export const DEFAULT_COMPACTION_PLUGIN_NAME = "default-compaction";
24
-
25
- /**
26
- * Self-describing compaction request handed to {@link defaultCompact}. Carries
27
- * the conversational inputs plus the {@link ContextWindowManager} that runs the
28
- * summary, so the request is the part that can grow toward a model-facing
29
- * compaction-method shape without coupling callers to the manager's option bag.
30
- */
31
- export interface CompactionContext {
32
- /** Context window manager that performs the compaction. */
33
- manager: ContextWindowManager;
34
- /** Message history to consider for compaction. */
35
- messages: Message[];
36
- /** Abort signal forwarded to the compaction summary call. */
37
- signal?: AbortSignal;
38
- /** Skip the auto-threshold check (forced/overflow/emergency compaction). */
39
- force?: boolean;
40
- /** Per-conversation inference-profile override for the summary call. */
41
- overrideProfile?: string | null;
42
- /** Pre-computed token estimate from a prior `shouldCompact()` call. */
43
- precomputedEstimate?: number;
44
- /** Legacy keep-boundary hint forwarded to the compactor. */
45
- minKeepRecentUserTurns?: number;
46
- /** Trust class of the actor whose turn triggered compaction. */
47
- actorTrustClass?: TrustClass;
48
- }
49
-
50
- /**
51
- * Run compaction for the turn: delegates to the request's context window
52
- * manager and returns the (possibly summarized) message history.
53
- */
54
- export async function defaultCompact(
55
- context: CompactionContext,
56
- ): Promise<ContextWindowResult> {
57
- const { manager, messages, signal, ...options } = context;
58
- return manager.maybeCompact(messages, signal, options);
59
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * No-op `post-compact` scaffolding for the memory-v3-shadow plugin.
3
- *
4
- * v3 re-injection after mid-turn compaction is not yet owned by this plugin —
5
- * the active injector still runs through the static chain. Once the
6
- * post-compaction hook API stabilizes we will import `memoryV3Injector` from
7
- * `../injector.js` and re-apply it here. Until then this hook does nothing.
8
- */
9
-
10
- import type { PluginHookFn } from "@vellumai/plugin-api";
11
-
12
- const postCompact: PluginHookFn = async () => {};
13
-
14
- export default postCompact;
@@ -1,19 +0,0 @@
1
- /**
2
- * No-op `user-prompt-submit` scaffolding for the memory-v3-shadow plugin.
3
- *
4
- * The v3 injector currently runs through the static injector chain
5
- * (`memory-retrieval/injector-chain.ts`), not through this hook. Once the
6
- * prompt-submit hook API stabilizes we will import `memoryV3Injector` from
7
- * `../injector.js` and run it here, so v3 injection is owned by the plugin's
8
- * own lifecycle rather than the loop-driven chain. Until then this hook
9
- * intentionally does nothing.
10
- */
11
-
12
- import type {
13
- PluginHookFn,
14
- UserPromptSubmitContext,
15
- } from "@vellumai/plugin-api";
16
-
17
- const userPromptSubmit: PluginHookFn<UserPromptSubmitContext> = async () => {};
18
-
19
- export default userPromptSubmit;
@@ -1,75 +0,0 @@
1
- /**
2
- * The memory-v3 {@link Injector}. Reads both v3 flags:
3
- * - `memory-v3-live` on → orchestrate, log, render the working-set selection
4
- * into a `<memory>` block, and return it at v2's dynamic-memory placement.
5
- * - `memory-v3-shadow` on (live off) → orchestrate + log only, return `null`.
6
- * - both off → return `null` (no orchestration).
7
- *
8
- * Empty selection and any failure return `null` (no v3 injection). v2
9
- * suppression keys off BOTH the flag AND this return value, so a `null` here
10
- * (failure or empty selection) falls back to v2 memory rather than dropping all
11
- * memory.
12
- *
13
- * Orchestration and telemetry live in {@link observeTurn}; this module is the
14
- * thin injector wrapper that renders the result for live injection.
15
- */
16
-
17
- import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
18
- import { getConfig } from "../../../config/loader.js";
19
- import { getLogger } from "../../../util/logger.js";
20
- import {
21
- type InjectionBlock,
22
- type Injector,
23
- type TurnContext as PluginTurnContext,
24
- } from "../../types.js";
25
- import { renderV3PageContent } from "./page-content.js";
26
- import { renderMemoryBlock } from "./render-injection.js";
27
- import {
28
- MEMORY_V3_LIVE,
29
- MEMORY_V3_SHADOW,
30
- observeTurn,
31
- } from "./shadow-plugin.js";
32
- import { MEMORY_V3_BLOCK_ID } from "./types.js";
33
-
34
- const log = getLogger("memory-v3-shadow");
35
-
36
- export const memoryV3Injector: Injector = {
37
- name: "memory-v3-shadow",
38
- // High order so it sorts last; the live `<memory>` block uses the
39
- // after-memory-prefix placement so it lands at the memory boundary regardless
40
- // of this sort key, which only orders content-producing injectors.
41
- order: 1000,
42
- async produce(ctx: PluginTurnContext): Promise<InjectionBlock | null> {
43
- const config = getConfig();
44
- const live = isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
45
- const shadow = isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config);
46
- if (!live && !shadow) return null;
47
-
48
- const result = await observeTurn(ctx.conversationId, ctx.turnIndex);
49
- if (!live || !result) return null;
50
-
51
- try {
52
- // `renderMemoryBlock` returns "" for an empty selection; inject nothing.
53
- const text = await renderMemoryBlock(
54
- result.finalInjection,
55
- renderV3PageContent,
56
- );
57
- if (text.length === 0) return null;
58
- return {
59
- id: MEMORY_V3_BLOCK_ID,
60
- text,
61
- // Mirror v2's dynamic `<memory>` block placement.
62
- placement: "after-memory-prefix",
63
- };
64
- } catch (err) {
65
- log.warn(
66
- {
67
- err: err instanceof Error ? err.message : String(err),
68
- conversationId: ctx.conversationId,
69
- },
70
- "memory-v3 live render failed (non-fatal) — falling back to v2",
71
- );
72
- return null;
73
- }
74
- },
75
- };
@@ -1,26 +0,0 @@
1
- /**
2
- * Default `memory-v3-shadow` plugin.
3
- *
4
- * Houses the memory-v3 shadow/live orchestration engine (this directory) and
5
- * its injector (`./injector.ts`). The injector is still consumed by the static
6
- * chain in `memory-retrieval/injector-chain.ts`; the `user-prompt-submit` /
7
- * `post-compact` hooks below are no-op scaffolding for the eventual
8
- * convergence, when v3 injection moves off the loop-driven chain and into these
9
- * lifecycle hooks. See each hook file for the convergence note.
10
- */
11
-
12
- import { type Plugin } from "../../types.js";
13
- import postCompact from "./hooks/post-compact.js";
14
- import userPromptSubmit from "./hooks/user-prompt-submit.js";
15
- import pkg from "./package.json" with { type: "json" };
16
-
17
- export const memoryV3ShadowPlugin: Plugin = {
18
- manifest: {
19
- name: pkg.name,
20
- version: pkg.version,
21
- },
22
- hooks: {
23
- "user-prompt-submit": userPromptSubmit,
24
- "post-compact": postCompact,
25
- },
26
- };