@ashlr/hub 3.8.0 → 3.9.0

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 (147) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/dist/build-identity.json +2 -2
  3. package/dist/cli/resource-profile.js +95 -21
  4. package/dist/cli/resource-profile.js.map +1 -1
  5. package/dist/core/resources/native-profile.d.ts +56 -0
  6. package/dist/core/resources/native-profile.js +368 -6
  7. package/dist/core/resources/native-profile.js.map +1 -1
  8. package/dist/core/run/model-catalog.js +8 -1
  9. package/dist/core/run/model-catalog.js.map +1 -1
  10. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  11. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +9 -2
  12. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +8 -1
  13. package/dist/core/verse/adapters/claude.d.ts +49 -2
  14. package/dist/core/verse/adapters/claude.js +317 -56
  15. package/dist/core/verse/adapters/claude.js.map +1 -1
  16. package/dist/core/verse/adapters/codex.d.ts +81 -5
  17. package/dist/core/verse/adapters/codex.js +409 -47
  18. package/dist/core/verse/adapters/codex.js.map +1 -1
  19. package/dist/core/verse/adapters/grok.d.ts +13 -1
  20. package/dist/core/verse/adapters/grok.js +28 -2
  21. package/dist/core/verse/adapters/grok.js.map +1 -1
  22. package/dist/core/verse/adapters/index.d.ts +32 -0
  23. package/dist/core/verse/adapters/index.js +2 -0
  24. package/dist/core/verse/adapters/index.js.map +1 -1
  25. package/dist/core/verse/codex-rollout.d.ts +284 -0
  26. package/dist/core/verse/codex-rollout.js +786 -0
  27. package/dist/core/verse/codex-rollout.js.map +1 -0
  28. package/dist/core/verse/context-fit.d.ts +63 -0
  29. package/dist/core/verse/context-fit.js +355 -0
  30. package/dist/core/verse/context-fit.js.map +1 -0
  31. package/dist/core/verse/context-math.d.ts +215 -0
  32. package/dist/core/verse/context-math.js +365 -0
  33. package/dist/core/verse/context-math.js.map +1 -0
  34. package/dist/core/verse/local-models.d.ts +137 -1
  35. package/dist/core/verse/local-models.js +209 -5
  36. package/dist/core/verse/local-models.js.map +1 -1
  37. package/dist/core/verse/model-windows.d.ts +217 -0
  38. package/dist/core/verse/model-windows.js +616 -0
  39. package/dist/core/verse/model-windows.js.map +1 -0
  40. package/dist/core/verse/preferences.d.ts +123 -0
  41. package/dist/core/verse/preferences.js +403 -0
  42. package/dist/core/verse/preferences.js.map +1 -0
  43. package/dist/core/verse/project-memory.d.ts +112 -0
  44. package/dist/core/verse/project-memory.js +295 -0
  45. package/dist/core/verse/project-memory.js.map +1 -0
  46. package/dist/core/verse/seats.d.ts +106 -4
  47. package/dist/core/verse/seats.js +291 -89
  48. package/dist/core/verse/seats.js.map +1 -1
  49. package/dist/core/verse/session-engine.d.ts +76 -2
  50. package/dist/core/verse/session-engine.js +684 -63
  51. package/dist/core/verse/session-engine.js.map +1 -1
  52. package/dist/core/verse/session-handoff.d.ts +94 -0
  53. package/dist/core/verse/session-handoff.js +551 -0
  54. package/dist/core/verse/session-handoff.js.map +1 -0
  55. package/dist/core/verse/session-search.d.ts +63 -0
  56. package/dist/core/verse/session-search.js +213 -0
  57. package/dist/core/verse/session-search.js.map +1 -0
  58. package/dist/core/verse/session-store.d.ts +16 -2
  59. package/dist/core/verse/session-store.js +80 -6
  60. package/dist/core/verse/session-store.js.map +1 -1
  61. package/dist/core/verse/types.d.ts +333 -1
  62. package/dist/core/verse/types.js +54 -2
  63. package/dist/core/verse/types.js.map +1 -1
  64. package/dist/core/verse/verse-api.d.ts +40 -5
  65. package/dist/core/verse/verse-api.js +724 -25
  66. package/dist/core/verse/verse-api.js.map +1 -1
  67. package/dist/core/web/api.d.ts +14 -4
  68. package/dist/core/web/api.js +26 -11
  69. package/dist/core/web/api.js.map +1 -1
  70. package/dist/core/web/public/next/assets/{App-BsbJFA2j.js → App-XURmiAAp.js} +5 -5
  71. package/dist/core/web/public/next/assets/{App-BsbJFA2j.js.map → App-XURmiAAp.js.map} +1 -1
  72. package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js → ApprovalsSection-BriUzoZc.js} +2 -2
  73. package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js.map → ApprovalsSection-BriUzoZc.js.map} +1 -1
  74. package/dist/core/web/public/next/assets/AutonomySection-C4GBsQVc.js +2 -0
  75. package/dist/core/web/public/next/assets/{AutonomySection-yeNNUPur.js.map → AutonomySection-C4GBsQVc.js.map} +1 -1
  76. package/dist/core/web/public/next/assets/ChatSection-BpOBOUvg.css +1 -0
  77. package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js +84 -0
  78. package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js.map +1 -0
  79. package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js +2 -0
  80. package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js.map +1 -0
  81. package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js → Dialog-BxdIfmFH.js} +2 -2
  82. package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js.map → Dialog-BxdIfmFH.js.map} +1 -1
  83. package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js → DiffViewer-DmeRWuc4.js} +2 -2
  84. package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js.map → DiffViewer-DmeRWuc4.js.map} +1 -1
  85. package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js → McpSection-q34T7Tho.js} +2 -2
  86. package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js.map → McpSection-q34T7Tho.js.map} +1 -1
  87. package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js → MutationTokenDialog-CFAkwOEt.js} +2 -2
  88. package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js.map → MutationTokenDialog-CFAkwOEt.js.map} +1 -1
  89. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js → ResourcePoolConsoleApp-DHc8GFOY.js} +4 -4
  90. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js.map → ResourcePoolConsoleApp-DHc8GFOY.js.map} +1 -1
  91. package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js → RouteErrorBoundary-ZL9F4qUn.js} +2 -2
  92. package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js.map → RouteErrorBoundary-ZL9F4qUn.js.map} +1 -1
  93. package/dist/core/web/public/next/assets/{Meter-CLcr-4wS.css → Segmented-8cf0Fypn.css} +1 -1
  94. package/dist/core/web/public/next/assets/{Meter-Cmo1pLbk.js → Segmented-DbRjVye3.js} +2 -2
  95. package/dist/core/web/public/next/assets/Segmented-DbRjVye3.js.map +1 -0
  96. package/dist/core/web/public/next/assets/SettingsSection-nJGGGYqv.js +2 -0
  97. package/dist/core/web/public/next/assets/{SettingsSection-B3YH2Npi.js.map → SettingsSection-nJGGGYqv.js.map} +1 -1
  98. package/dist/core/web/public/next/assets/UniverseConsoleApp-6GJydgo4.js +2 -0
  99. package/dist/core/web/public/next/assets/{UniverseConsoleApp-wEKIzl1M.js.map → UniverseConsoleApp-6GJydgo4.js.map} +1 -1
  100. package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js → UniverseView-DLDtNYL8.js} +2 -2
  101. package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js.map → UniverseView-DLDtNYL8.js.map} +1 -1
  102. package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js +2 -0
  103. package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js.map +1 -0
  104. package/dist/core/web/public/next/assets/VerseConsoleApp-DsdKQoTH.css +1 -0
  105. package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js +3 -0
  106. package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js.map +1 -0
  107. package/dist/core/web/public/next/assets/{charts-BopViIha.js → charts-CVooqJWN.js} +2 -2
  108. package/dist/core/web/public/next/assets/{charts-BopViIha.js.map → charts-CVooqJWN.js.map} +1 -1
  109. package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js +2 -0
  110. package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js.map +1 -0
  111. package/dist/core/web/public/next/assets/global-eaY9GyX3.js +2 -0
  112. package/dist/core/web/public/next/assets/global-eaY9GyX3.js.map +1 -0
  113. package/dist/core/web/public/next/assets/{icons-L06pQH6j.js → icons-vex8rclC.js} +2 -2
  114. package/dist/core/web/public/next/assets/{icons-L06pQH6j.js.map → icons-vex8rclC.js.map} +1 -1
  115. package/dist/core/web/public/next/assets/{index-CXgVQHek.js → index-D2Eik-zt.js} +3 -3
  116. package/dist/core/web/public/next/assets/{index-CXgVQHek.js.map → index-D2Eik-zt.js.map} +1 -1
  117. package/dist/core/web/public/next/assets/{queries-D3sSlan1.js → queries-bAYU0VNy.js} +2 -2
  118. package/dist/core/web/public/next/assets/{queries-D3sSlan1.js.map → queries-bAYU0VNy.js.map} +1 -1
  119. package/dist/core/web/public/next/assets/use-guarded-action-C7QrP7Yp.js +2 -0
  120. package/dist/core/web/public/next/assets/{use-guarded-action-BxfJ9E-n.js.map → use-guarded-action-C7QrP7Yp.js.map} +1 -1
  121. package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js +2 -0
  122. package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js.map +1 -0
  123. package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js → workspace-model-CHol5RVp.js} +2 -2
  124. package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js.map → workspace-model-CHol5RVp.js.map} +1 -1
  125. package/dist/core/web/public/next/index.html +1 -1
  126. package/dist/release-dependency-inventory.json +1 -1
  127. package/docs/README.md +1 -0
  128. package/package.json +1 -1
  129. package/dist/core/web/public/next/assets/AutonomySection-yeNNUPur.js +0 -2
  130. package/dist/core/web/public/next/assets/ChatSection-BfnLe5dT.css +0 -1
  131. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js +0 -80
  132. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js.map +0 -1
  133. package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js +0 -2
  134. package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js.map +0 -1
  135. package/dist/core/web/public/next/assets/Meter-Cmo1pLbk.js.map +0 -1
  136. package/dist/core/web/public/next/assets/SettingsSection-B3YH2Npi.js +0 -2
  137. package/dist/core/web/public/next/assets/UniverseConsoleApp-wEKIzl1M.js +0 -2
  138. package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js +0 -2
  139. package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js.map +0 -1
  140. package/dist/core/web/public/next/assets/VerseConsoleApp-CMkCizvL.css +0 -1
  141. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js +0 -3
  142. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js.map +0 -1
  143. package/dist/core/web/public/next/assets/global-DMWakEhP.js +0 -2
  144. package/dist/core/web/public/next/assets/global-DMWakEhP.js.map +0 -1
  145. package/dist/core/web/public/next/assets/use-guarded-action-BxfJ9E-n.js +0 -2
  146. package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js +0 -2
  147. package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js.map +0 -1
@@ -0,0 +1,551 @@
1
+ /**
2
+ * Seat-to-seat handoff: a DETERMINISTIC, ZERO-SPEND note that lets a fresh
3
+ * session (same seat, a larger window, or another vendor) pick up where a long
4
+ * one left off.
5
+ *
6
+ * Not to be confused with `handoff.ts`, which is the typed planner-to-worker
7
+ * contract for the local fan-out. This module reads a session's own event log
8
+ * and writes prose for the next agent to read as its first message.
9
+ *
10
+ * WHY DETERMINISTIC: asking the current seat to summarise itself costs a paid
11
+ * turn (and is refused under local-only). Everything below is assembled from
12
+ * `events.jsonl` plus a bounded `git diff --stat` per root, so the preview is
13
+ * free, instant, reproducible, and allowed in every policy mode. When the
14
+ * operator DOES want the agent's own summary, the UI sends a normal turn to the
15
+ * current session first and re-previews with `includeLastAssistant: true` — the
16
+ * spend is then an ordinary, visible, operator-initiated turn.
17
+ *
18
+ * NOTHING HERE STARTS A MODEL CALL. The new session's first turn is spent only
19
+ * when the operator presses send on the pre-filled composer.
20
+ *
21
+ * SECTIONS, highest priority first. When the note would exceed
22
+ * VERSE_HANDOFF_MAX_CHARS the LOWEST-priority sections are dropped, one whole
23
+ * section at a time, and named in `stats.truncated`:
24
+ *
25
+ * focus the operator's focus line for the new session
26
+ * goal the first user message (the original ask)
27
+ * latest-asks the last three user messages after it
28
+ * state the last assistant message, abridged (head + tail) — or
29
+ * verbatim (capped) when `includeLastAssistant`, in which
30
+ * case it is the reply to the canned summary request
31
+ *
32
+ * CONTROL TURNS ARE NOT ASKS. Two kinds of user message are Verse talking to
33
+ * the agent, not the operator stating work: the canned
34
+ * VERSE_HANDOFF_SUMMARY_REQUEST ("Ask this seat to summarize first") and the
35
+ * `/compact [focus]` that "Compact now" sends. Neither may become the goal or
36
+ * a latest request: the footer tells the next agent to "continue with the
37
+ * latest request", and a new chat told to continue with "Write the handoff
38
+ * note…" writes another handoff note instead of doing the work.
39
+ * files files the agent read or edited, from tool inputs (≤ 60)
40
+ * commands shell commands it ran (≤ 15, most recent)
41
+ * errors failures it hit (≤ 5, most recent)
42
+ * repositories the roots, each with a bounded `git diff --stat HEAD`
43
+ * compactions how often the CLI compacted (early detail is summaries)
44
+ *
45
+ * Every section is `scrubSecrets`'d before it is measured, so the cap holds on
46
+ * the text that is actually returned. Control characters (NUL above all) are
47
+ * stripped too: the note becomes the new chat's first turn, and turn text
48
+ * rides on the CLI's argv, where a NUL makes spawn throw. Individual sections carry their own caps
49
+ * too (a 64 KB first message must not evict everything else).
50
+ *
51
+ * The note PREFERS PATHS TO CONTENTS (the rule `handoff.ts` enforces for the
52
+ * local fan-out, for the same reason): the next agent has the filesystem, and
53
+ * a file it reads itself is current where a pasted copy is already stale.
54
+ */
55
+ import { execFileSync } from 'node:child_process';
56
+ import { isAbsolute, relative, resolve as resolvePath, sep } from 'node:path';
57
+ import { scrubSecrets } from '../util/scrub.js';
58
+ import { estimateTokensFromChars } from './context-math.js';
59
+ import { gitArgs } from './context-fit.js';
60
+ import { isDirectoryPath } from './path-guard.js';
61
+ import { VerseServiceError } from './preferences.js';
62
+ import { stripUnsafeControlChars } from './project-memory.js';
63
+ import { VERSE_HANDOFF_MAX_CHARS, VERSE_HANDOFF_SUMMARY_REQUEST, verseSessionRoots, } from './types.js';
64
+ // ---------------------------------------------------------------------------
65
+ // Constants
66
+ // ---------------------------------------------------------------------------
67
+ /** First line of every handoff note. Also how a handoff OF a handoff is recognised. */
68
+ export const VERSE_HANDOFF_HEADER = '# Continuing from an earlier Verse session';
69
+ export const VERSE_HANDOFF_FOCUS_MAX_CHARS = 500;
70
+ export const HANDOFF_MAX_FILES = 60;
71
+ export const HANDOFF_MAX_COMMANDS = 15;
72
+ export const HANDOFF_MAX_ERRORS = 5;
73
+ export const HANDOFF_LATEST_ASKS = 3;
74
+ const GOAL_MAX_CHARS = 2_500;
75
+ const ASK_MAX_CHARS = 900;
76
+ const STATE_SUMMARY_HEAD_CHARS = 900;
77
+ const STATE_SUMMARY_TAIL_CHARS = 700;
78
+ const STATE_VERBATIM_MAX_CHARS = 6_000;
79
+ const COMMAND_MAX_CHARS = 200;
80
+ const ERROR_MAX_CHARS = 240;
81
+ /** `git diff --stat` bounds, per root and in total (the preview is a synchronous request). */
82
+ export const HANDOFF_GIT_TIMEOUT_MS = 3_000;
83
+ export const HANDOFF_GIT_MAX_BYTES = 4 * 1024;
84
+ const HANDOFF_GIT_TOTAL_BUDGET_MS = 6_000;
85
+ /** Section names, in priority order (index 0 = kept longest). Reported in `stats.truncated`. */
86
+ export const HANDOFF_SECTIONS = [
87
+ 'focus',
88
+ 'goal',
89
+ 'latest-asks',
90
+ 'state',
91
+ 'files',
92
+ 'commands',
93
+ 'errors',
94
+ 'repositories',
95
+ 'compactions',
96
+ ];
97
+ // ---------------------------------------------------------------------------
98
+ // Tool classification (claude / local share Claude Code's names; codex and
99
+ // grok have their own). Names read off real event logs and the CLIs' string
100
+ // tables — see the adapters for where each comes from.
101
+ // ---------------------------------------------------------------------------
102
+ const READ_TOOLS = new Set(['Read', 'NotebookRead', 'read_file', 'hashline_read', 'view']);
103
+ const EDIT_TOOLS = new Set([
104
+ 'Edit', 'Write', 'MultiEdit', 'NotebookEdit', // Claude Code (claude + local seats)
105
+ 'write', 'write_file', 'search_replace', 'hashline_edit', 'edit_file', 'create_file', // grok
106
+ ]);
107
+ const COMMAND_TOOLS = new Set(['Bash', 'command_execution', 'run_terminal_command', 'PowerShell', 'shell']);
108
+ const PATH_KEYS = ['file_path', 'notebook_path', 'target_file', 'path', 'filePath', 'filename'];
109
+ function isObject(value) {
110
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
111
+ }
112
+ function pathFromInput(input) {
113
+ if (!isObject(input))
114
+ return null;
115
+ for (const key of PATH_KEYS) {
116
+ const value = input[key];
117
+ if (typeof value === 'string' && value.trim().length > 0 && !value.includes('\0'))
118
+ return value.trim();
119
+ }
120
+ return null;
121
+ }
122
+ function commandFromInput(input) {
123
+ if (!isObject(input))
124
+ return null;
125
+ const command = input['command'];
126
+ if (typeof command === 'string')
127
+ return command;
128
+ if (Array.isArray(command) && command.every((part) => typeof part === 'string'))
129
+ return command.join(' ');
130
+ return null;
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Control turns (see the module comment)
134
+ // ---------------------------------------------------------------------------
135
+ function normaliseTurnText(text) {
136
+ return text.replace(/\r\n?/g, '\n').trim();
137
+ }
138
+ const SUMMARY_REQUEST = normaliseTurnText(VERSE_HANDOFF_SUMMARY_REQUEST);
139
+ /** The canned "summarize first" turn, byte-for-byte (after CRLF/whitespace trim). */
140
+ export function isHandoffSummaryRequest(text) {
141
+ return normaliseTurnText(text) === SUMMARY_REQUEST;
142
+ }
143
+ /**
144
+ * A CLI control command Verse itself sends as a turn. Only `/compact` — the one
145
+ * verb "Compact now" emits — is recognised: a message merely STARTING with a
146
+ * slash ("/Users/me/app is broken") is an ordinary ask and must stay one.
147
+ */
148
+ export function isControlCommandTurn(text) {
149
+ return /^\/compact(?:\s|$)/.test(normaliseTurnText(text));
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Text helpers
153
+ // ---------------------------------------------------------------------------
154
+ function oneLine(text, max) {
155
+ const collapsed = text.replace(/\s+/g, ' ').trim();
156
+ return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1).trimEnd()}…`;
157
+ }
158
+ /** Cap a multi-line block, cutting at a line boundary when that keeps most of it. */
159
+ function capBlock(text, max) {
160
+ const trimmed = text.replace(/\r\n?/g, '\n').trim();
161
+ if (trimmed.length <= max)
162
+ return trimmed;
163
+ const marker = '\n[… truncated]';
164
+ let cut = trimmed.slice(0, max - marker.length);
165
+ const newline = cut.lastIndexOf('\n');
166
+ if (newline >= cut.length * 0.7)
167
+ cut = cut.slice(0, newline);
168
+ return `${cut.trimEnd()}${marker}`;
169
+ }
170
+ /** Head + tail of a long message: agents put the verdict first or last. */
171
+ function abridge(text) {
172
+ const trimmed = text.replace(/\r\n?/g, '\n').trim();
173
+ if (trimmed.length <= STATE_SUMMARY_HEAD_CHARS + STATE_SUMMARY_TAIL_CHARS + 40)
174
+ return trimmed;
175
+ let head = trimmed.slice(0, STATE_SUMMARY_HEAD_CHARS);
176
+ const headBreak = head.lastIndexOf('\n');
177
+ if (headBreak >= head.length * 0.6)
178
+ head = head.slice(0, headBreak);
179
+ let tail = trimmed.slice(trimmed.length - STATE_SUMMARY_TAIL_CHARS);
180
+ const tailBreak = tail.indexOf('\n');
181
+ if (tailBreak !== -1 && tailBreak <= tail.length * 0.4)
182
+ tail = tail.slice(tailBreak + 1);
183
+ return `${head.trimEnd()}\n[… middle of the reply omitted …]\n${tail.trimStart()}`;
184
+ }
185
+ function quoteBlock(text) {
186
+ return text.split('\n').map((line) => (line.length > 0 ? `> ${line}` : '>')).join('\n');
187
+ }
188
+ /** A path as the next agent should read it: relative to the primary root when under it. */
189
+ function displayPath(raw, primary) {
190
+ const absolute = isAbsolute(raw) ? resolvePath(raw) : resolvePath(primary, raw);
191
+ const rel = relative(primary, absolute);
192
+ if (rel.length > 0 && !rel.startsWith('..') && !isAbsolute(rel))
193
+ return rel.split(sep).join('/');
194
+ return absolute;
195
+ }
196
+ function extract(session, events) {
197
+ const primary = session.projectPath;
198
+ const userMessages = [];
199
+ let turnCount = 0;
200
+ let lastAssistant = null;
201
+ // True while the most recent user message is the canned summary request.
202
+ let inSummaryReply = false;
203
+ let summaryReply = null;
204
+ // Insertion-ordered sets; a file edited after being read counts as edited.
205
+ const edited = new Map();
206
+ const read = new Map();
207
+ const commands = [];
208
+ const errors = [];
209
+ const compactions = [];
210
+ const toolNames = new Map();
211
+ const noteEdited = (raw) => {
212
+ const path = displayPath(raw, primary);
213
+ read.delete(path);
214
+ edited.delete(path); // re-insert so "most recent" ordering holds
215
+ edited.set(path, true);
216
+ };
217
+ const noteRead = (raw) => {
218
+ const path = displayPath(raw, primary);
219
+ if (edited.has(path))
220
+ return;
221
+ read.delete(path);
222
+ read.set(path, true);
223
+ };
224
+ for (const event of events) {
225
+ switch (event.type) {
226
+ case 'user-message':
227
+ if (typeof event.text === 'string' && event.text.trim().length > 0) {
228
+ turnCount += 1;
229
+ const summary = isHandoffSummaryRequest(event.text);
230
+ // A new user turn supersedes any earlier summary: only the reply to
231
+ // the LATEST message can describe where the session stands now.
232
+ inSummaryReply = summary;
233
+ summaryReply = null;
234
+ if (!summary && !isControlCommandTurn(event.text)) {
235
+ userMessages.push({ seq: event.seq, text: event.text });
236
+ }
237
+ }
238
+ break;
239
+ case 'assistant-message':
240
+ if (typeof event.text === 'string' && event.text.trim().length > 0) {
241
+ lastAssistant = event.text;
242
+ if (inSummaryReply)
243
+ summaryReply = event.text;
244
+ }
245
+ break;
246
+ case 'tool-use': {
247
+ toolNames.set(event.toolUseId, event.name);
248
+ if (READ_TOOLS.has(event.name)) {
249
+ const path = pathFromInput(event.input);
250
+ if (path)
251
+ noteRead(path);
252
+ }
253
+ else if (EDIT_TOOLS.has(event.name)) {
254
+ const path = pathFromInput(event.input);
255
+ if (path)
256
+ noteEdited(path);
257
+ }
258
+ else if (event.name === 'file_change' && isObject(event.input) && Array.isArray(event.input['changes'])) {
259
+ // codex: one item can touch several files.
260
+ for (const change of event.input['changes']) {
261
+ if (isObject(change) && typeof change['path'] === 'string' && change['path'].length > 0)
262
+ noteEdited(change['path']);
263
+ }
264
+ }
265
+ else if (COMMAND_TOOLS.has(event.name)) {
266
+ const command = commandFromInput(event.input);
267
+ if (command && command.trim().length > 0) {
268
+ const line = oneLine(command, COMMAND_MAX_CHARS);
269
+ // Collapse immediate repeats (the same test run five times is one fact).
270
+ if (commands[commands.length - 1] !== line)
271
+ commands.push(line);
272
+ }
273
+ }
274
+ break;
275
+ }
276
+ case 'tool-result':
277
+ if (event.isError) {
278
+ const name = toolNames.get(event.toolUseId) ?? 'tool';
279
+ const output = typeof event.output === 'string' ? event.output : '';
280
+ const firstLines = output.split('\n').map((l) => l.trim()).filter((l) => l.length > 0).slice(0, 2).join(' — ');
281
+ errors.push(oneLine(`${name}: ${firstLines || 'failed'}`, ERROR_MAX_CHARS));
282
+ }
283
+ break;
284
+ case 'error':
285
+ if (typeof event.message === 'string' && event.message.trim().length > 0) {
286
+ errors.push(oneLine(event.message, ERROR_MAX_CHARS));
287
+ }
288
+ break;
289
+ case 'compaction':
290
+ compactions.push({ preTokens: event.preTokens, postTokens: event.postTokens, trigger: event.trigger });
291
+ break;
292
+ default:
293
+ break;
294
+ }
295
+ }
296
+ // Dedupe errors keeping the most recent occurrence of each.
297
+ const recentErrors = [];
298
+ for (let i = errors.length - 1; i >= 0 && recentErrors.length < HANDOFF_MAX_ERRORS; i -= 1) {
299
+ if (!recentErrors.includes(errors[i]))
300
+ recentErrors.unshift(errors[i]);
301
+ }
302
+ const recentCommands = [];
303
+ for (let i = commands.length - 1; i >= 0 && recentCommands.length < HANDOFF_MAX_COMMANDS; i -= 1) {
304
+ if (!recentCommands.includes(commands[i]))
305
+ recentCommands.unshift(commands[i]);
306
+ }
307
+ return {
308
+ userMessages,
309
+ turnCount,
310
+ lastAssistant,
311
+ summaryReply,
312
+ edited: [...edited.keys()],
313
+ read: [...read.keys()],
314
+ commands: recentCommands,
315
+ errors: recentErrors,
316
+ compactions,
317
+ };
318
+ }
319
+ /**
320
+ * A session started from a handoff has the previous note as its first message.
321
+ * Its real goal is the "Original goal" section inside that note, not the note.
322
+ */
323
+ function goalFrom(firstUserText) {
324
+ if (!firstUserText.startsWith(VERSE_HANDOFF_HEADER))
325
+ return firstUserText;
326
+ const match = /\n## Original goal\n+([\s\S]*?)(?:\n## |\n---\n|$)/.exec(firstUserText);
327
+ if (!match)
328
+ return firstUserText;
329
+ return match[1].split('\n').map((line) => line.replace(/^> ?/, '')).join('\n').trim() || firstUserText;
330
+ }
331
+ // ---------------------------------------------------------------------------
332
+ // git diff --stat
333
+ // ---------------------------------------------------------------------------
334
+ /**
335
+ * `git -C <root> diff --stat HEAD`, hardened and bounded: 3 s, 4 KB, no
336
+ * external diff drivers or textconv filters (a repo's .gitattributes can name
337
+ * arbitrary commands), no fsmonitor hook, no index lock. Null when the root is
338
+ * not a repository, has no commits, or git fails.
339
+ */
340
+ export function defaultGitDiffStat(root) {
341
+ if (!isDirectoryPath(root))
342
+ return null;
343
+ try {
344
+ const out = execFileSync('git', gitArgs(root, ['diff', '--stat', '--no-color', '--no-ext-diff', '--no-textconv', 'HEAD']), {
345
+ encoding: 'utf8',
346
+ timeout: HANDOFF_GIT_TIMEOUT_MS,
347
+ maxBuffer: 1024 * 1024,
348
+ stdio: ['ignore', 'pipe', 'ignore'],
349
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: '0', GIT_TERMINAL_PROMPT: '0', GIT_PAGER: 'cat', PAGER: 'cat' },
350
+ });
351
+ return capGitStat(out);
352
+ }
353
+ catch {
354
+ return null;
355
+ }
356
+ }
357
+ function capGitStat(raw) {
358
+ const text = raw.replace(/\r\n?/g, '\n').trimEnd();
359
+ if (text.trim().length === 0)
360
+ return '';
361
+ if (Buffer.byteLength(text, 'utf8') <= HANDOFF_GIT_MAX_BYTES)
362
+ return text;
363
+ // Keep the summary line ("N files changed, …"): it is the most informative.
364
+ const lines = text.split('\n');
365
+ const summary = lines[lines.length - 1];
366
+ const kept = [];
367
+ let bytes = Buffer.byteLength(summary, 'utf8') + 64;
368
+ for (const line of lines.slice(0, -1)) {
369
+ const size = Buffer.byteLength(line, 'utf8') + 1;
370
+ if (bytes + size > HANDOFF_GIT_MAX_BYTES)
371
+ break;
372
+ kept.push(line);
373
+ bytes += size;
374
+ }
375
+ const omitted = lines.length - 1 - kept.length;
376
+ return [...kept, ` … ${omitted} more file${omitted === 1 ? '' : 's'}`, summary].join('\n');
377
+ }
378
+ function header(session, turns, events) {
379
+ const first = events.length > 0 ? events[0].at.slice(0, 10) : session.createdAt.slice(0, 10);
380
+ const last = events.length > 0 ? events[events.length - 1].at.slice(0, 10) : session.updatedAt.slice(0, 10);
381
+ const span = first === last ? first : `${first} → ${last}`;
382
+ return [
383
+ VERSE_HANDOFF_HEADER,
384
+ '',
385
+ `This chat continues "${oneLine(session.title, 120)}" (${session.engine} · ${session.model}, ${turns} turn${turns === 1 ? '' : 's'}, ${span}).`,
386
+ 'The notes below were assembled mechanically from that session\'s log. Treat them as leads, not ground truth: re-read any file before you change it, and check the working tree rather than trusting a summary of it.',
387
+ ].join('\n');
388
+ }
389
+ const FOOTER = [
390
+ '---',
391
+ 'Start by checking the current state of the files and repositories above, then continue with the latest request. If anything here is unclear or contradicts what you find, say so before acting.',
392
+ ].join('\n');
393
+ /**
394
+ * Build the handoff note. Deterministic for a given (session, events, opts,
395
+ * git output): no clock, no randomness, no model.
396
+ */
397
+ export function buildHandoffPreview(session, events, opts = {}) {
398
+ const focusRaw = typeof opts.focus === 'string' ? opts.focus.trim() : '';
399
+ if (focusRaw.length > VERSE_HANDOFF_FOCUS_MAX_CHARS) {
400
+ throw new VerseServiceError('VERSE_INVALID', `focus must be at most ${VERSE_HANDOFF_FOCUS_MAX_CHARS} characters`);
401
+ }
402
+ const gitDiffStat = opts.gitDiffStat ?? defaultGitDiffStat;
403
+ const data = extract(session, events);
404
+ const turns = data.turnCount;
405
+ const sections = [];
406
+ if (focusRaw.length > 0) {
407
+ sections.push({ name: 'focus', text: `## Focus for this session\n\n${focusRaw}` });
408
+ }
409
+ if (data.userMessages.length > 0) {
410
+ const goal = capBlock(goalFrom(data.userMessages[0].text), GOAL_MAX_CHARS);
411
+ sections.push({ name: 'goal', text: `## Original goal\n\n${quoteBlock(goal)}` });
412
+ }
413
+ const later = data.userMessages.slice(1).slice(-HANDOFF_LATEST_ASKS);
414
+ if (later.length > 0) {
415
+ const skipped = data.userMessages.length - 1 - later.length;
416
+ const lines = [`## Latest requests (oldest first${skipped > 0 ? `; ${skipped} earlier omitted` : ''})`, ''];
417
+ later.forEach((message, index) => {
418
+ // Continuation lines indented under the number so a multi-line ask stays one item.
419
+ lines.push(`${index + 1}. ${capBlock(message.text, ASK_MAX_CHARS).replace(/\n/g, '\n ')}`);
420
+ });
421
+ sections.push({ name: 'latest-asks', text: lines.join('\n') });
422
+ }
423
+ // With `includeLastAssistant` the operator asked the seat to summarise
424
+ // itself: the state section is THAT reply (the last text of the turn that
425
+ // answered the canned request). With no such reply on record — the request
426
+ // failed, or a later turn superseded it — it is the last reply, as before.
427
+ const stateText = opts.includeLastAssistant === true && data.summaryReply !== null ? data.summaryReply : data.lastAssistant;
428
+ if (stateText !== null) {
429
+ const verbatim = opts.includeLastAssistant === true;
430
+ const body = verbatim ? capBlock(stateText, STATE_VERBATIM_MAX_CHARS) : abridge(stateText);
431
+ // Say "abridged" only when something was actually left out.
432
+ const shortened = body !== stateText.replace(/\r\n?/g, '\n').trim();
433
+ const title = verbatim
434
+ ? `## Where it stood (the previous agent's own summary, verbatim${shortened ? ', capped' : ''})`
435
+ : `## Where it stood (the previous agent's last reply${shortened ? ', abridged' : ''})`;
436
+ sections.push({ name: 'state', text: `${title}\n\n${quoteBlock(body)}` });
437
+ }
438
+ const touched = [...data.edited, ...data.read];
439
+ if (touched.length > 0) {
440
+ const lines = ['## Files touched', ''];
441
+ const editedShown = data.edited.slice(-HANDOFF_MAX_FILES);
442
+ const readShown = data.read.slice(-(HANDOFF_MAX_FILES - editedShown.length));
443
+ if (editedShown.length > 0) {
444
+ lines.push('Edited:');
445
+ for (const path of editedShown)
446
+ lines.push(`- ${path}`);
447
+ }
448
+ if (readShown.length > 0) {
449
+ if (editedShown.length > 0)
450
+ lines.push('');
451
+ lines.push('Read:');
452
+ for (const path of readShown)
453
+ lines.push(`- ${path}`);
454
+ }
455
+ const hidden = touched.length - editedShown.length - readShown.length;
456
+ if (hidden > 0)
457
+ lines.push('', `(${hidden} more not listed)`);
458
+ lines.push('', `Relative paths are relative to ${session.projectPath}.`);
459
+ sections.push({ name: 'files', text: lines.join('\n') });
460
+ }
461
+ if (data.commands.length > 0) {
462
+ sections.push({
463
+ name: 'commands',
464
+ text: ['## Commands run (most recent last)', '', ...data.commands.map((c) => `- \`${c.replace(/`/g, "'")}\``)].join('\n'),
465
+ });
466
+ }
467
+ if (data.errors.length > 0) {
468
+ sections.push({
469
+ name: 'errors',
470
+ text: ['## Errors seen (most recent last)', '', ...data.errors.map((e) => `- ${e}`)].join('\n'),
471
+ });
472
+ }
473
+ const roots = verseSessionRoots(session);
474
+ {
475
+ const lines = ['## Repositories', ''];
476
+ const started = Date.now();
477
+ roots.forEach((root, index) => {
478
+ lines.push(`- ${root}${index === 0 ? ' (primary)' : ''}`);
479
+ // The default runs git synchronously on the request thread: bound the
480
+ // whole loop, not just each call, so eight slow roots cannot stall it.
481
+ if (opts.gitDiffStat === undefined && Date.now() - started > HANDOFF_GIT_TOTAL_BUDGET_MS) {
482
+ lines.push(' (diff not collected: time budget spent)');
483
+ return;
484
+ }
485
+ let stat;
486
+ try {
487
+ stat = gitDiffStat(root);
488
+ }
489
+ catch {
490
+ stat = null;
491
+ }
492
+ if (stat === null)
493
+ return;
494
+ const capped = capGitStat(stat);
495
+ if (capped === null || capped.length === 0) {
496
+ lines.push(' No uncommitted changes against HEAD.');
497
+ }
498
+ else {
499
+ lines.push(' Uncommitted changes against HEAD (`git diff --stat HEAD`):', '', ' ```', ...capped.split('\n').map((l) => ` ${l}`), ' ```');
500
+ }
501
+ });
502
+ sections.push({ name: 'repositories', text: lines.join('\n') });
503
+ }
504
+ const compactionCount = Math.max(session.compactionCount ?? 0, data.compactions.length);
505
+ if (compactionCount > 0) {
506
+ const last = data.compactions[data.compactions.length - 1];
507
+ const detail = last && last.preTokens !== null && last.postTokens !== null
508
+ ? ` The last one shrank the context from ~${Math.round(last.preTokens / 1000)}k to ~${Math.round(last.postTokens / 1000)}k tokens.`
509
+ : '';
510
+ sections.push({
511
+ name: 'compactions',
512
+ text: `## Compactions\n\nThe previous session auto-compacted ${compactionCount} time${compactionCount === 1 ? '' : 's'}, so its early turns survived only as summaries.${detail} Details from early in that session may be lost; verify rather than assume.`,
513
+ });
514
+ }
515
+ // Scrub every part BEFORE measuring, so the cap holds on what is returned.
516
+ const clean = (text) => scrubSecrets(stripUnsafeControlChars(text));
517
+ const head = clean(header(session, turns, events));
518
+ const foot = FOOTER;
519
+ const scrubbed = sections.map((s) => ({ name: s.name, text: clean(s.text) }));
520
+ const separator = '\n\n';
521
+ const total = (parts) => [head, ...parts.map((p) => p.text), foot].join(separator).length;
522
+ const kept = [...scrubbed];
523
+ const dropped = [];
524
+ while (kept.length > 0 && total(kept) > VERSE_HANDOFF_MAX_CHARS) {
525
+ // Drop the lowest-priority section still present.
526
+ let worst = 0;
527
+ for (let i = 1; i < kept.length; i += 1) {
528
+ if (HANDOFF_SECTIONS.indexOf(kept[i].name) > HANDOFF_SECTIONS.indexOf(kept[worst].name))
529
+ worst = i;
530
+ }
531
+ dropped.push(kept[worst].name);
532
+ kept.splice(worst, 1);
533
+ }
534
+ let text = [head, ...kept.map((p) => p.text), foot].join(separator);
535
+ // Unreachable with the per-section caps above, but the contract is a hard cap.
536
+ if (text.length > VERSE_HANDOFF_MAX_CHARS)
537
+ text = text.slice(0, VERSE_HANDOFF_MAX_CHARS);
538
+ return {
539
+ sourceSessionId: session.id,
540
+ sourceTitle: clean(session.title),
541
+ text,
542
+ stats: {
543
+ chars: text.length,
544
+ estTokens: estimateTokensFromChars(text.length),
545
+ turnsCovered: turns,
546
+ filesTouched: touched.length,
547
+ truncated: dropped,
548
+ },
549
+ };
550
+ }
551
+ //# sourceMappingURL=session-handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-handoff.js","sourceRoot":"","sources":["../../../src/core/verse/session-handoff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Keyword search over past Verse sessions — zero spend, no index, no model.
3
+ *
4
+ * The corpus is the durable event log the session store already keeps
5
+ * (`<id>.events.jsonl`). Only what the OPERATOR and the AGENT said is searched
6
+ * (`user-message`, `assistant-message`): tool inputs and outputs are huge,
7
+ * noisy and full of file contents that would drown every real hit, and
8
+ * `text-delta` / `thinking` duplicate or pre-empt the final message.
9
+ *
10
+ * MATCHING: every term must occur in ONE message (terms are AND-ed,
11
+ * case-insensitive; a "double-quoted phrase" is one term). A hit is a message,
12
+ * so the snippet shown is the place the operator actually remembers.
13
+ *
14
+ * SCORING (deterministic given `now`):
15
+ * term score = Σ over terms of (1 + ln(occurrences)) — repetition helps, with
16
+ * diminishing returns, so one message that says "migration" 40
17
+ * times does not bury every other
18
+ * title bonus = +1 when every term also appears in the session title
19
+ * kind weight = user messages ×1.1 (the operator's own words are what they
20
+ * search for), assistant ×1.0
21
+ * recency = × (0.35 + 0.65 · 0.5^(ageDays / 14)) — a two-week half-life
22
+ * that never drives an old exact match to zero
23
+ *
24
+ * BOUNDS — this runs synchronously on the API thread, so it is bounded twice:
25
+ * the newest ≤ VERSE_SEARCH_MAX_SESSIONS sessions (by updatedAt) and at most
26
+ * VERSE_SEARCH_MAX_SCAN_CHARS of message text in total. Either bound tripping
27
+ * sets `truncated`, so the UI can say "searched the most recent N" instead of
28
+ * implying the whole history was covered. At most VERSE_SEARCH_MAX_HITS_PER_SESSION
29
+ * hits come from one session so a single long chat cannot fill the list.
30
+ *
31
+ * Snippets are whitespace-collapsed, ≤ 240 chars, and `scrubSecrets`'d.
32
+ */
33
+ import type { VerseEvent, VerseSearchResponse, VerseSession } from './types.js';
34
+ export declare const VERSE_SEARCH_MAX_SESSIONS = 200;
35
+ /** ~20 MB of message text (UTF-16 code units, which is what is actually scanned). */
36
+ export declare const VERSE_SEARCH_MAX_SCAN_CHARS: number;
37
+ export declare const VERSE_SEARCH_DEFAULT_LIMIT = 20;
38
+ export declare const VERSE_SEARCH_MAX_LIMIT = 50;
39
+ export declare const VERSE_SEARCH_MAX_QUERY_CHARS = 200;
40
+ export declare const VERSE_SEARCH_MAX_TERMS = 8;
41
+ export declare const VERSE_SEARCH_MAX_HITS_PER_SESSION = 3;
42
+ export declare const VERSE_SEARCH_SNIPPET_CHARS = 240;
43
+ export interface VerseSearchInput {
44
+ sessions: VerseSession[];
45
+ readEvents: (id: string) => VerseEvent[];
46
+ query: string;
47
+ limit?: number;
48
+ /** Injectable clock for the recency weight (tests). Default: the real clock. */
49
+ now?: () => Date;
50
+ }
51
+ /**
52
+ * Split a query into lower-cased terms. `"exact phrase"` stays one term;
53
+ * everything else splits on whitespace. Duplicates collapse. Throws
54
+ * VERSE_INVALID for an empty or oversized query.
55
+ */
56
+ export declare function parseSearchQuery(query: string): string[];
57
+ /**
58
+ * ≤ VERSE_SEARCH_SNIPPET_CHARS of whitespace-collapsed, secret-scrubbed text
59
+ * around the first match. Scrubbed BEFORE windowing so a redaction can never
60
+ * push the snippet over its cap or leave half a token at a window edge.
61
+ */
62
+ export declare function buildSnippet(text: string, firstTerm: string): string;
63
+ export declare function searchSessions(input: VerseSearchInput): VerseSearchResponse;