hammoc 1.1.5 → 1.2.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 (160) hide show
  1. package/README.md +8 -2
  2. package/bin/hammoc.js +12 -0
  3. package/package.json +3 -3
  4. package/packages/client/dist/assets/{index-BG1rX4_v.js → index-Bz_ljWO0.js} +1 -1
  5. package/packages/client/dist/assets/index-Cc_AX5QV.css +32 -0
  6. package/packages/client/dist/assets/index-DjVOQMst.js +1451 -0
  7. package/packages/client/dist/index.html +2 -2
  8. package/packages/client/dist/sw.js +1 -1
  9. package/packages/server/dist/app.d.ts.map +1 -1
  10. package/packages/server/dist/app.js +3 -0
  11. package/packages/server/dist/app.js.map +1 -1
  12. package/packages/server/dist/controllers/authController.d.ts +5 -0
  13. package/packages/server/dist/controllers/authController.d.ts.map +1 -1
  14. package/packages/server/dist/controllers/authController.js +61 -0
  15. package/packages/server/dist/controllers/authController.js.map +1 -1
  16. package/packages/server/dist/controllers/boardController.d.ts +2 -1
  17. package/packages/server/dist/controllers/boardController.d.ts.map +1 -1
  18. package/packages/server/dist/controllers/boardController.js +29 -4
  19. package/packages/server/dist/controllers/boardController.js.map +1 -1
  20. package/packages/server/dist/controllers/projectController.d.ts +6 -0
  21. package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
  22. package/packages/server/dist/controllers/projectController.js +62 -0
  23. package/packages/server/dist/controllers/projectController.js.map +1 -1
  24. package/packages/server/dist/controllers/queueController.d.ts +1 -0
  25. package/packages/server/dist/controllers/queueController.d.ts.map +1 -1
  26. package/packages/server/dist/controllers/queueController.js +12 -0
  27. package/packages/server/dist/controllers/queueController.js.map +1 -1
  28. package/packages/server/dist/controllers/queueTemplateController.d.ts.map +1 -1
  29. package/packages/server/dist/controllers/queueTemplateController.js +1 -15
  30. package/packages/server/dist/controllers/queueTemplateController.js.map +1 -1
  31. package/packages/server/dist/controllers/serverController.d.ts.map +1 -1
  32. package/packages/server/dist/controllers/serverController.js +83 -69
  33. package/packages/server/dist/controllers/serverController.js.map +1 -1
  34. package/packages/server/dist/controllers/sessionController.d.ts +5 -10
  35. package/packages/server/dist/controllers/sessionController.d.ts.map +1 -1
  36. package/packages/server/dist/controllers/sessionController.js +65 -75
  37. package/packages/server/dist/controllers/sessionController.js.map +1 -1
  38. package/packages/server/dist/handlers/streamCallbacks.d.ts +4 -0
  39. package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
  40. package/packages/server/dist/handlers/streamCallbacks.js +9 -3
  41. package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
  42. package/packages/server/dist/handlers/websocket.d.ts +9 -15
  43. package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
  44. package/packages/server/dist/handlers/websocket.js +672 -116
  45. package/packages/server/dist/handlers/websocket.js.map +1 -1
  46. package/packages/server/dist/locales/en/server.json +8 -2
  47. package/packages/server/dist/locales/es/server.json +311 -305
  48. package/packages/server/dist/locales/ja/server.json +311 -305
  49. package/packages/server/dist/locales/ko/server.json +8 -2
  50. package/packages/server/dist/locales/pt/server.json +311 -305
  51. package/packages/server/dist/locales/zh-CN/server.json +311 -305
  52. package/packages/server/dist/routes/auth.d.ts.map +1 -1
  53. package/packages/server/dist/routes/auth.js +2 -0
  54. package/packages/server/dist/routes/auth.js.map +1 -1
  55. package/packages/server/dist/routes/board.d.ts.map +1 -1
  56. package/packages/server/dist/routes/board.js +3 -1
  57. package/packages/server/dist/routes/board.js.map +1 -1
  58. package/packages/server/dist/routes/images.d.ts +7 -0
  59. package/packages/server/dist/routes/images.d.ts.map +1 -0
  60. package/packages/server/dist/routes/images.js +51 -0
  61. package/packages/server/dist/routes/images.js.map +1 -0
  62. package/packages/server/dist/routes/projects.d.ts.map +1 -1
  63. package/packages/server/dist/routes/projects.js +2 -0
  64. package/packages/server/dist/routes/projects.js.map +1 -1
  65. package/packages/server/dist/routes/queue.d.ts.map +1 -1
  66. package/packages/server/dist/routes/queue.js +2 -1
  67. package/packages/server/dist/routes/queue.js.map +1 -1
  68. package/packages/server/dist/routes/sessions.d.ts.map +1 -1
  69. package/packages/server/dist/routes/sessions.js +2 -3
  70. package/packages/server/dist/routes/sessions.js.map +1 -1
  71. package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
  72. package/packages/server/dist/services/bmadStatusService.js +15 -25
  73. package/packages/server/dist/services/bmadStatusService.js.map +1 -1
  74. package/packages/server/dist/services/chatService.d.ts +2 -0
  75. package/packages/server/dist/services/chatService.d.ts.map +1 -1
  76. package/packages/server/dist/services/chatService.js +34 -1
  77. package/packages/server/dist/services/chatService.js.map +1 -1
  78. package/packages/server/dist/services/historyParser.d.ts +1 -11
  79. package/packages/server/dist/services/historyParser.d.ts.map +1 -1
  80. package/packages/server/dist/services/historyParser.js +144 -182
  81. package/packages/server/dist/services/historyParser.js.map +1 -1
  82. package/packages/server/dist/services/imageStorageService.d.ts +28 -0
  83. package/packages/server/dist/services/imageStorageService.d.ts.map +1 -0
  84. package/packages/server/dist/services/imageStorageService.js +108 -0
  85. package/packages/server/dist/services/imageStorageService.js.map +1 -0
  86. package/packages/server/dist/services/issueService.d.ts +10 -2
  87. package/packages/server/dist/services/issueService.d.ts.map +1 -1
  88. package/packages/server/dist/services/issueService.js +90 -23
  89. package/packages/server/dist/services/issueService.js.map +1 -1
  90. package/packages/server/dist/services/ptyService.d.ts +6 -0
  91. package/packages/server/dist/services/ptyService.d.ts.map +1 -1
  92. package/packages/server/dist/services/ptyService.js +59 -0
  93. package/packages/server/dist/services/ptyService.js.map +1 -1
  94. package/packages/server/dist/services/queueService.d.ts.map +1 -1
  95. package/packages/server/dist/services/queueService.js +23 -2
  96. package/packages/server/dist/services/queueService.js.map +1 -1
  97. package/packages/server/dist/services/rateLimitProbeService.d.ts +5 -0
  98. package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
  99. package/packages/server/dist/services/rateLimitProbeService.js +7 -0
  100. package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
  101. package/packages/server/dist/services/sessionBufferManager.d.ts +26 -0
  102. package/packages/server/dist/services/sessionBufferManager.d.ts.map +1 -0
  103. package/packages/server/dist/services/sessionBufferManager.js +113 -0
  104. package/packages/server/dist/services/sessionBufferManager.js.map +1 -0
  105. package/packages/server/dist/services/sessionService.d.ts +26 -1
  106. package/packages/server/dist/services/sessionService.d.ts.map +1 -1
  107. package/packages/server/dist/services/sessionService.js +236 -67
  108. package/packages/server/dist/services/sessionService.js.map +1 -1
  109. package/packages/server/dist/services/summarizeService.d.ts +25 -0
  110. package/packages/server/dist/services/summarizeService.d.ts.map +1 -0
  111. package/packages/server/dist/services/summarizeService.js +122 -0
  112. package/packages/server/dist/services/summarizeService.js.map +1 -0
  113. package/packages/server/dist/utils/imageUtils.d.ts +11 -0
  114. package/packages/server/dist/utils/imageUtils.d.ts.map +1 -0
  115. package/packages/server/dist/utils/imageUtils.js +37 -0
  116. package/packages/server/dist/utils/imageUtils.js.map +1 -0
  117. package/packages/server/dist/utils/messageTree.d.ts +56 -0
  118. package/packages/server/dist/utils/messageTree.d.ts.map +1 -0
  119. package/packages/server/dist/utils/messageTree.js +356 -0
  120. package/packages/server/dist/utils/messageTree.js.map +1 -0
  121. package/packages/server/package.json +3 -1
  122. package/packages/shared/dist/constants/index.d.ts +3 -0
  123. package/packages/shared/dist/constants/index.d.ts.map +1 -0
  124. package/packages/shared/dist/constants/index.js +3 -0
  125. package/packages/shared/dist/constants/index.js.map +1 -0
  126. package/packages/shared/dist/constants/messageTree.d.ts +6 -0
  127. package/packages/shared/dist/constants/messageTree.d.ts.map +1 -0
  128. package/packages/shared/dist/constants/messageTree.js +7 -0
  129. package/packages/shared/dist/constants/messageTree.js.map +1 -0
  130. package/packages/shared/dist/index.d.ts +2 -1
  131. package/packages/shared/dist/index.d.ts.map +1 -1
  132. package/packages/shared/dist/index.js +1 -1
  133. package/packages/shared/dist/index.js.map +1 -1
  134. package/packages/shared/dist/types/auth.d.ts +15 -0
  135. package/packages/shared/dist/types/auth.d.ts.map +1 -1
  136. package/packages/shared/dist/types/auth.js.map +1 -1
  137. package/packages/shared/dist/types/bmadStatus.d.ts +1 -2
  138. package/packages/shared/dist/types/bmadStatus.d.ts.map +1 -1
  139. package/packages/shared/dist/types/bmadStatus.js.map +1 -1
  140. package/packages/shared/dist/types/history.d.ts +21 -8
  141. package/packages/shared/dist/types/history.d.ts.map +1 -1
  142. package/packages/shared/dist/types/message.d.ts +10 -0
  143. package/packages/shared/dist/types/message.d.ts.map +1 -1
  144. package/packages/shared/dist/types/message.js.map +1 -1
  145. package/packages/shared/dist/types/preferences.d.ts +5 -1
  146. package/packages/shared/dist/types/preferences.d.ts.map +1 -1
  147. package/packages/shared/dist/types/preferences.js.map +1 -1
  148. package/packages/shared/dist/types/queue.d.ts +3 -3
  149. package/packages/shared/dist/types/queue.d.ts.map +1 -1
  150. package/packages/shared/dist/types/sdk.d.ts +14 -0
  151. package/packages/shared/dist/types/sdk.d.ts.map +1 -1
  152. package/packages/shared/dist/types/sdk.js.map +1 -1
  153. package/packages/shared/dist/types/session.d.ts +1 -0
  154. package/packages/shared/dist/types/session.d.ts.map +1 -1
  155. package/packages/shared/dist/types/session.js.map +1 -1
  156. package/packages/shared/dist/types/websocket.d.ts +61 -3
  157. package/packages/shared/dist/types/websocket.d.ts.map +1 -1
  158. package/scripts/spike-25.9-output.log +47 -0
  159. package/packages/client/dist/assets/index-B1mDkqlY.css +0 -32
  160. package/packages/client/dist/assets/index-B_NOAvK3.js +0 -1380
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Summarize Service — Story 25.9
3
+ *
4
+ * Uses Agent SDK query() with a temporary session to generate conversation
5
+ * summaries. Works with both OAuth and API key authentication.
6
+ * The temporary session JSONL is deleted after use.
7
+ */
8
+ import { randomUUID } from 'crypto';
9
+ import { unlink } from 'fs/promises';
10
+ import { query } from '@anthropic-ai/claude-agent-sdk';
11
+ import { createLogger } from '../utils/logger.js';
12
+ import { SessionService } from './sessionService.js';
13
+ const log = createLogger('summarizeService');
14
+ const SUMMARY_PROMPT_PREFIX = `You are a conversation summarizer. Given the following conversation between a user and an AI assistant, produce a concise structured summary that preserves:
15
+
16
+ 1. **Key decisions made** — what was agreed upon
17
+ 2. **Important code changes** — files created/modified, patterns adopted
18
+ 3. **Current state** — what has been completed, what remains
19
+ 4. **Critical context** — constraints, gotchas, or requirements mentioned
20
+
21
+ Format the summary as a bulleted list grouped by these categories. Be concise but complete — the summary will be used to continue the conversation from an earlier point. Write in the same language as the original conversation.
22
+
23
+ <conversation>
24
+ `;
25
+ const SUMMARY_PROMPT_SUFFIX = `
26
+ </conversation>
27
+
28
+ Summarize the above conversation.`;
29
+ /** Max estimated tokens for input messages (safety margin) */
30
+ const MAX_INPUT_TOKENS = 200_000;
31
+ /** Simple heuristic: ~4 chars per token */
32
+ function estimateTokens(text) {
33
+ return Math.ceil(text.length / 4);
34
+ }
35
+ /**
36
+ * Summarize a list of conversation messages using Agent SDK query().
37
+ * Creates a temporary session, extracts the summary, then deletes the session file.
38
+ */
39
+ export async function summarize(messages, options) {
40
+ if (!messages || messages.length === 0) {
41
+ throw new Error('No messages to summarize');
42
+ }
43
+ // Truncate from the beginning if over token limit (keep recent messages)
44
+ let truncated = messages;
45
+ let totalTokens = truncated.reduce((sum, m) => sum + estimateTokens(m.content), 0);
46
+ if (totalTokens > MAX_INPUT_TOKENS) {
47
+ truncated = [...messages];
48
+ while (totalTokens > MAX_INPUT_TOKENS && truncated.length > 0) {
49
+ const removed = truncated.shift();
50
+ totalTokens -= estimateTokens(removed.content);
51
+ }
52
+ log.info(`Truncated messages from ${messages.length} to ${truncated.length} (token estimate: ${totalTokens})`);
53
+ if (truncated.length === 0) {
54
+ throw new Error('Conversation too large to summarize');
55
+ }
56
+ }
57
+ // Format conversation
58
+ const conversationText = truncated
59
+ .map((m) => `${m.role}: ${m.content}`)
60
+ .join('\n');
61
+ let localeHint = '';
62
+ if (options?.locale) {
63
+ localeHint = `\nRespond in ${options.locale} language.\n`;
64
+ }
65
+ const prompt = SUMMARY_PROMPT_PREFIX + conversationText + SUMMARY_PROMPT_SUFFIX + localeHint;
66
+ const tempSessionId = randomUUID();
67
+ // Resolve temp session JSONL path using SessionService (same encoding as SDK)
68
+ let tempSessionPath = null;
69
+ if (options?.projectSlug) {
70
+ const sessionService = new SessionService();
71
+ tempSessionPath = sessionService.getSessionFilePath(options.projectSlug, tempSessionId);
72
+ }
73
+ log.info(`Generating summary: tempSession=${tempSessionId}, messageCount=${truncated.length}`);
74
+ try {
75
+ const q = query({
76
+ prompt,
77
+ options: {
78
+ maxTurns: 1,
79
+ sessionId: tempSessionId,
80
+ cwd: options?.cwd,
81
+ permissionMode: 'dontAsk',
82
+ enableFileCheckpointing: false,
83
+ abortController: options?.signal
84
+ ? (() => { const ac = new AbortController(); options.signal.addEventListener('abort', () => ac.abort(), { once: true }); return ac; })()
85
+ : undefined,
86
+ },
87
+ });
88
+ let resultText = '';
89
+ for await (const message of q) {
90
+ if (options?.signal?.aborted) {
91
+ await q.return();
92
+ break;
93
+ }
94
+ if (message.type === 'result') {
95
+ const msg = message;
96
+ if (msg.subtype === 'success' && msg.result) {
97
+ resultText = msg.result;
98
+ }
99
+ else if (msg.is_error || (msg.subtype && msg.subtype !== 'success')) {
100
+ throw new Error(msg.result || `Summary failed: ${msg.subtype}`);
101
+ }
102
+ }
103
+ }
104
+ if (!resultText) {
105
+ throw new Error('No text content in summary response');
106
+ }
107
+ return resultText;
108
+ }
109
+ finally {
110
+ // Clean up temporary session JSONL
111
+ if (tempSessionPath) {
112
+ try {
113
+ await unlink(tempSessionPath);
114
+ log.debug(`Deleted temp session: ${tempSessionPath}`);
115
+ }
116
+ catch {
117
+ log.warn(`Failed to delete temp session: ${tempSessionPath}`);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ //# sourceMappingURL=summarizeService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarizeService.js","sourceRoot":"","sources":["../../src/services/summarizeService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared image utilities for hash computation, MIME mapping, and path/URL construction.
3
+ * Used by imageStorageService and historyParser to ensure consistent behavior.
4
+ */
5
+ export declare function getExtensionFromMimeType(mimeType: string): string | undefined;
6
+ export declare function computeImageHash(data: string): string;
7
+ export declare function buildImageFilename(data: string, mimeType: string): string | null;
8
+ export declare function buildThumbnailFilename(data: string, mimeType: string): string | null;
9
+ export declare function buildImageUrl(projectSlug: string, sessionId: string, filename: string): string;
10
+ export declare function getImageDir(projectDir: string, sessionId: string): string;
11
+ //# sourceMappingURL=imageUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageUtils.d.ts","sourceRoot":"","sources":["../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIhF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIpF;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9F;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzE"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared image utilities for hash computation, MIME mapping, and path/URL construction.
3
+ * Used by imageStorageService and historyParser to ensure consistent behavior.
4
+ */
5
+ import crypto from 'crypto';
6
+ import path from 'path';
7
+ const MIME_TO_EXT = {
8
+ 'image/png': '.png',
9
+ 'image/jpeg': '.jpg',
10
+ 'image/gif': '.gif',
11
+ 'image/webp': '.webp',
12
+ };
13
+ export function getExtensionFromMimeType(mimeType) {
14
+ return MIME_TO_EXT[mimeType];
15
+ }
16
+ export function computeImageHash(data) {
17
+ return crypto.createHash('sha256').update(data).digest('hex').substring(0, 16);
18
+ }
19
+ export function buildImageFilename(data, mimeType) {
20
+ const ext = MIME_TO_EXT[mimeType];
21
+ if (!ext)
22
+ return null;
23
+ return `${computeImageHash(data)}${ext}`;
24
+ }
25
+ export function buildThumbnailFilename(data, mimeType) {
26
+ const ext = MIME_TO_EXT[mimeType];
27
+ if (!ext)
28
+ return null;
29
+ return `${computeImageHash(data)}_thumb${ext}`;
30
+ }
31
+ export function buildImageUrl(projectSlug, sessionId, filename) {
32
+ return `/api/projects/${projectSlug}/sessions/${sessionId}/images/${filename}`;
33
+ }
34
+ export function getImageDir(projectDir, sessionId) {
35
+ return path.join(projectDir, 'images', sessionId);
36
+ }
37
+ //# sourceMappingURL=imageUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageUtils.js","sourceRoot":"","sources":["../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,WAAW,GAA2B;IAC1C,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,OAAO;CACtB,CAAC;AAEF,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IAC/D,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,QAAgB;IACnE,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAmB,EAAE,SAAiB,EAAE,QAAgB;IACpF,OAAO,iBAAiB,WAAW,aAAa,SAAS,WAAW,QAAQ,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,SAAiB;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Server-side message tree building utilities
3
+ * Story 25.4: Branch-aware Message Pagination
4
+ *
5
+ * Pure functions that work with RawJSONLMessage to build a tree,
6
+ * extract active branch paths, and compute branch points.
7
+ */
8
+ import type { RawJSONLMessage } from '@hammoc/shared';
9
+ export interface RawTreeNode {
10
+ message: RawJSONLMessage;
11
+ children: RawTreeNode[];
12
+ }
13
+ export interface RawMessageTree {
14
+ roots: RawTreeNode[];
15
+ nodeMap: Map<string, RawTreeNode>;
16
+ }
17
+ export interface BranchPointInfo {
18
+ total: number;
19
+ current: number;
20
+ /** The key to use in branchSelections when navigating this branch point.
21
+ * For inner branches: the parent node's UUID. For root-level: ROOT_BRANCH_KEY. */
22
+ selectionKey: string;
23
+ }
24
+ export interface ActiveBranchResult {
25
+ messages: RawJSONLMessage[];
26
+ branchPoints: Record<string, BranchPointInfo>;
27
+ }
28
+ /**
29
+ * Group children into logical branches by UUID prefix.
30
+ * A true branch exists only when there are multiple user-type groups.
31
+ * Mirrors client-side groupChildrenIntoBranches logic.
32
+ */
33
+ export declare function groupRawChildrenIntoBranches(children: RawTreeNode[]): RawTreeNode[][];
34
+ /**
35
+ * Build a raw message tree from JSONL messages using parentUuid links.
36
+ * Handles orphans (missing parent) and circular references defensively.
37
+ */
38
+ export declare function buildRawMessageTree(messages: RawJSONLMessage[]): RawMessageTree;
39
+ /**
40
+ * Find default branch selections by tracing from the newest leaf to root.
41
+ * Returns a Record<string, number> mapping branch point UUIDs to selected indices.
42
+ */
43
+ export declare function getDefaultRawBranchSelections(roots: RawTreeNode[]): Record<string, number>;
44
+ /**
45
+ * Extract the active branch as a flat RawJSONLMessage array,
46
+ * following branch selections. Unselected branch points default to last (newest) branch.
47
+ * Also computes branchPoints info for the response.
48
+ */
49
+ export declare function getActiveRawBranch(roots: RawTreeNode[], branchSelections: Record<string, number>): ActiveBranchResult;
50
+ /**
51
+ * Find branch selections that lead to a specific message UUID.
52
+ * Returns a branchSelections record suitable for getActiveRawBranch,
53
+ * or null if the UUID is not found in the tree.
54
+ */
55
+ export declare function findBranchSelectionsForUuid(roots: RawTreeNode[], targetUuid: string): Record<string, number> | null;
56
+ //# sourceMappingURL=messageTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messageTree.d.ts","sourceRoot":"","sources":["../../src/utils/messageTree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAQtD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB;uFACmF;IACnF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC/C;AAkCD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,EAAE,CAiDrF;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,cAAc,CA8D/E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgF1F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAAE,EACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC,kBAAkB,CA2EpB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAgD/B"}
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Server-side message tree building utilities
3
+ * Story 25.4: Branch-aware Message Pagination
4
+ *
5
+ * Pure functions that work with RawJSONLMessage to build a tree,
6
+ * extract active branch paths, and compute branch points.
7
+ */
8
+ import { ROOT_BRANCH_KEY } from '@hammoc/shared';
9
+ import { createLogger } from './logger.js';
10
+ const log = createLogger('messageTree');
11
+ // --- Helpers ---
12
+ /** Message types that participate in the conversation tree for branch selection */
13
+ const CONVERSATION_TYPES = new Set(['user', 'assistant', 'system']);
14
+ /**
15
+ * Detect task-notification user messages injected by background tasks.
16
+ * These are user-type messages whose content starts with <task-notification> —
17
+ * they should not count as branch-creating user messages since they are
18
+ * system-injected and can arrive concurrently with real user input.
19
+ */
20
+ function isTaskNotification(node) {
21
+ const content = node.message.message?.content;
22
+ if (typeof content === 'string')
23
+ return content.trimStart().startsWith('<task-notification>');
24
+ if (Array.isArray(content)) {
25
+ const first = content.find((c) => c.type === 'text');
26
+ return first?.text?.trimStart().startsWith('<task-notification>') ?? false;
27
+ }
28
+ return false;
29
+ }
30
+ /**
31
+ * Extract base UUID from a potentially split message UUID.
32
+ * Split IDs follow patterns: {uuid}-text-{n}, {uuid}-tool-{id}, {uuid}-thinking
33
+ */
34
+ function getBaseUuid(uuid) {
35
+ const match = uuid.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:-(text-\d+|tool-.+|thinking))?$/);
36
+ return match ? match[1] : uuid;
37
+ }
38
+ /**
39
+ * Group children into logical branches by UUID prefix.
40
+ * A true branch exists only when there are multiple user-type groups.
41
+ * Mirrors client-side groupChildrenIntoBranches logic.
42
+ */
43
+ export function groupRawChildrenIntoBranches(children) {
44
+ // Step 1: group split fragments by base UUID
45
+ const groups = new Map();
46
+ for (const child of children) {
47
+ const base = getBaseUuid(child.message.uuid);
48
+ if (!groups.has(base))
49
+ groups.set(base, []);
50
+ groups.get(base).push(child);
51
+ }
52
+ const uuidGroups = Array.from(groups.values());
53
+ if (uuidGroups.length <= 1)
54
+ return uuidGroups;
55
+ // Step 2: detect true branches — only user-type groups count.
56
+ // A group "leads to a user" if it directly contains a user-type node OR
57
+ // if it contains a non-conversation intermediary (e.g. attachment, progress)
58
+ // whose subtree contains any conversation node. This handles chains like
59
+ // user → attachment → attachment → assistant (normal flow through attachments)
60
+ // and assistant → progress → user (resumeSessionAt asymmetry).
61
+ function hasConversationDescendant(n, depth) {
62
+ if (depth > 10)
63
+ return false;
64
+ if (CONVERSATION_TYPES.has(n.message.type))
65
+ return true;
66
+ return n.children.some((c) => hasConversationDescendant(c, depth + 1));
67
+ }
68
+ function groupLeadsToUser(g) {
69
+ return g.some((n) => (n.message.type === 'user' && !isTaskNotification(n)) ||
70
+ (!CONVERSATION_TYPES.has(n.message.type) && hasConversationDescendant(n, 0)));
71
+ }
72
+ const userGroupCount = uuidGroups.filter(groupLeadsToUser).length;
73
+ if (userGroupCount <= 1) {
74
+ // No real branches — all children are sequential flow
75
+ return [children];
76
+ }
77
+ // Multiple user groups = true branch point.
78
+ // Fold non-user groups into the nearest preceding user group.
79
+ const branches = [];
80
+ for (const group of uuidGroups) {
81
+ if (groupLeadsToUser(group)) {
82
+ branches.push([...group]);
83
+ }
84
+ else if (branches.length > 0) {
85
+ branches[branches.length - 1].push(...group);
86
+ }
87
+ else {
88
+ branches.push([...group]);
89
+ }
90
+ }
91
+ return branches;
92
+ }
93
+ // --- Core Functions ---
94
+ /**
95
+ * Build a raw message tree from JSONL messages using parentUuid links.
96
+ * Handles orphans (missing parent) and circular references defensively.
97
+ */
98
+ export function buildRawMessageTree(messages) {
99
+ const nodeMap = new Map();
100
+ const roots = [];
101
+ // Phase 1: Create all nodes
102
+ for (const msg of messages) {
103
+ nodeMap.set(msg.uuid, { message: msg, children: [] });
104
+ }
105
+ // Phase 2: Link parent→children, detect orphans
106
+ for (const msg of messages) {
107
+ const node = nodeMap.get(msg.uuid);
108
+ if (!msg.parentUuid) {
109
+ roots.push(node);
110
+ }
111
+ else {
112
+ const parent = nodeMap.get(msg.parentUuid);
113
+ if (!parent) {
114
+ log.warn(`Orphan message "${msg.uuid}" references non-existent parent "${msg.parentUuid}", treating as root`);
115
+ roots.push(node);
116
+ }
117
+ else {
118
+ parent.children.push(node);
119
+ }
120
+ }
121
+ }
122
+ // Phase 3: Detect circular references
123
+ const visited = new Set();
124
+ const inStack = new Set();
125
+ function detectCycle(node) {
126
+ if (inStack.has(node.message.uuid))
127
+ return true;
128
+ if (visited.has(node.message.uuid))
129
+ return false;
130
+ visited.add(node.message.uuid);
131
+ inStack.add(node.message.uuid);
132
+ for (const child of node.children) {
133
+ if (detectCycle(child)) {
134
+ log.warn(`Circular reference detected at "${child.message.uuid}", breaking link and treating as root`);
135
+ node.children = node.children.filter((c) => c !== child);
136
+ roots.push(child);
137
+ }
138
+ }
139
+ inStack.delete(node.message.uuid);
140
+ return false;
141
+ }
142
+ for (const root of [...roots]) {
143
+ detectCycle(root);
144
+ }
145
+ // Phase 3b: Promote unvisited nodes (rootless cycles)
146
+ for (const node of nodeMap.values()) {
147
+ if (!visited.has(node.message.uuid)) {
148
+ log.warn(`Unreachable node "${node.message.uuid}" promoted to root`);
149
+ roots.push(node);
150
+ detectCycle(node);
151
+ }
152
+ }
153
+ return { roots, nodeMap };
154
+ }
155
+ /**
156
+ * Find default branch selections by tracing from the newest leaf to root.
157
+ * Returns a Record<string, number> mapping branch point UUIDs to selected indices.
158
+ */
159
+ export function getDefaultRawBranchSelections(roots) {
160
+ const selections = {};
161
+ if (roots.length === 0)
162
+ return selections;
163
+ // Find newest leaf
164
+ let newestLeaf = null;
165
+ let newestTime = '';
166
+ function findLeaves(node) {
167
+ const branches = groupRawChildrenIntoBranches(node.children);
168
+ if (branches.length === 0) {
169
+ // Only consider conversation-relevant types (user, assistant, system) as
170
+ // leaf candidates. Metadata types (queue-operation, file-history-snapshot,
171
+ // progress, last-prompt) are often isolated roots with no children — if
172
+ // their timestamp is newer than the last conversation message they would
173
+ // be incorrectly selected, causing getActiveRawBranch to pick an empty root.
174
+ if (!CONVERSATION_TYPES.has(node.message.type))
175
+ return;
176
+ const ts = node.message.timestamp || '';
177
+ if (ts > newestTime || !newestLeaf) {
178
+ newestTime = ts;
179
+ newestLeaf = node;
180
+ }
181
+ return;
182
+ }
183
+ for (const branch of branches) {
184
+ for (const child of branch) {
185
+ findLeaves(child);
186
+ }
187
+ }
188
+ }
189
+ for (const root of roots) {
190
+ findLeaves(root);
191
+ }
192
+ if (!newestLeaf)
193
+ return selections;
194
+ // Build parent map for reverse traversal
195
+ const parentMap = new Map();
196
+ function buildParentMap(node) {
197
+ for (const child of node.children) {
198
+ parentMap.set(child.message.uuid, node);
199
+ buildParentMap(child);
200
+ }
201
+ }
202
+ for (const root of roots) {
203
+ buildParentMap(root);
204
+ }
205
+ // Walk up from leaf
206
+ let current = newestLeaf;
207
+ while (current) {
208
+ const parent = parentMap.get(current.message.uuid);
209
+ if (parent) {
210
+ const branches = groupRawChildrenIntoBranches(parent.children);
211
+ if (branches.length > 1) {
212
+ const branchIdx = branches.findIndex((branch) => branch.some((n) => n.message.uuid === current.message.uuid));
213
+ if (branchIdx >= 0) {
214
+ selections[parent.message.uuid] = branchIdx;
215
+ }
216
+ }
217
+ }
218
+ else {
219
+ // current is a root — check multi-root selection
220
+ // Use conversation-relevant roots only (matching getActiveRawBranch)
221
+ const convRoots = roots.filter((r) => r.children.length > 0 || CONVERSATION_TYPES.has(r.message.type));
222
+ if (convRoots.length > 1) {
223
+ const rootIdx = convRoots.indexOf(current);
224
+ if (rootIdx >= 0) {
225
+ selections[ROOT_BRANCH_KEY] = rootIdx;
226
+ }
227
+ }
228
+ }
229
+ current = parent || null;
230
+ }
231
+ return selections;
232
+ }
233
+ /**
234
+ * Extract the active branch as a flat RawJSONLMessage array,
235
+ * following branch selections. Unselected branch points default to last (newest) branch.
236
+ * Also computes branchPoints info for the response.
237
+ */
238
+ export function getActiveRawBranch(roots, branchSelections) {
239
+ const messages = [];
240
+ const branchPoints = {};
241
+ if (roots.length === 0)
242
+ return { messages, branchPoints };
243
+ // Handle multi-root
244
+ // Filter to conversation-relevant roots: roots that either have children
245
+ // (part of a conversation chain) or are displayable types (user/assistant/system).
246
+ // Isolated metadata roots (queue-operation, file-history-snapshot, progress
247
+ // without children, last-prompt) are noise and must not participate in
248
+ // branch selection — otherwise they inflate the root count and cause the
249
+ // wrong root to be selected as the active conversation epoch.
250
+ let activeRoots;
251
+ const conversationRoots = roots.filter((r) => r.children.length > 0 || CONVERSATION_TYPES.has(r.message.type));
252
+ if (conversationRoots.length > 1) {
253
+ const userRootCount = conversationRoots.filter((r) => r.message.type === 'user').length;
254
+ const hasCompactBoundary = conversationRoots.some((r) => r.message.type === 'system' && r.message.subtype === 'compact_boundary');
255
+ if (userRootCount > 1 || hasCompactBoundary) {
256
+ const selectedIdx = branchSelections[ROOT_BRANCH_KEY] ?? conversationRoots.length - 1;
257
+ const clampedIdx = Math.max(0, Math.min(selectedIdx, conversationRoots.length - 1));
258
+ const selectedRoot = conversationRoots[clampedIdx];
259
+ const rootKey = selectedRoot.message.type === 'user' ? selectedRoot.message.uuid : ROOT_BRANCH_KEY;
260
+ branchPoints[rootKey] = { total: conversationRoots.length, current: clampedIdx, selectionKey: ROOT_BRANCH_KEY };
261
+ activeRoots = [selectedRoot];
262
+ }
263
+ else {
264
+ // Sequential orphan roots — display all conversation roots
265
+ activeRoots = conversationRoots;
266
+ }
267
+ }
268
+ else {
269
+ // 0 or 1 conversation root — traverse all conversation roots (+ metadata
270
+ // roots with children, already included via the filter)
271
+ activeRoots = conversationRoots.length > 0 ? conversationRoots : roots;
272
+ }
273
+ function traverse(node) {
274
+ messages.push(node.message);
275
+ const branches = groupRawChildrenIntoBranches(node.children);
276
+ if (branches.length === 0)
277
+ return;
278
+ if (branches.length > 1) {
279
+ const selectedIdx = branchSelections[node.message.uuid] ?? branches.length - 1;
280
+ const clampedIdx = Math.max(0, Math.min(selectedIdx, branches.length - 1));
281
+ // Tag the first user message in the selected branch with branch info.
282
+ // If no direct user child exists, check one level deeper (handles
283
+ // assistant → progress → user pattern where progress is intermediary).
284
+ const selectedBranch = branches[clampedIdx];
285
+ const firstUserInBranch = selectedBranch.find((n) => n.message.type === 'user')
286
+ || selectedBranch.flatMap((n) => n.children).find((c) => c.message.type === 'user');
287
+ const branchKey = firstUserInBranch ? firstUserInBranch.message.uuid : node.message.uuid;
288
+ branchPoints[branchKey] = { total: branches.length, current: clampedIdx, selectionKey: node.message.uuid };
289
+ for (const child of selectedBranch) {
290
+ traverse(child);
291
+ }
292
+ }
293
+ else {
294
+ for (const child of branches[0]) {
295
+ traverse(child);
296
+ }
297
+ }
298
+ }
299
+ for (const root of activeRoots) {
300
+ traverse(root);
301
+ }
302
+ return { messages, branchPoints };
303
+ }
304
+ /**
305
+ * Find branch selections that lead to a specific message UUID.
306
+ * Returns a branchSelections record suitable for getActiveRawBranch,
307
+ * or null if the UUID is not found in the tree.
308
+ */
309
+ export function findBranchSelectionsForUuid(roots, targetUuid) {
310
+ const selections = {};
311
+ function containsUuid(node) {
312
+ if (node.message.uuid === targetUuid)
313
+ return true;
314
+ return node.children.some(containsUuid);
315
+ }
316
+ function traverse(node) {
317
+ if (node.message.uuid === targetUuid)
318
+ return true;
319
+ const branches = groupRawChildrenIntoBranches(node.children);
320
+ if (branches.length === 0)
321
+ return false;
322
+ for (let i = 0; i < branches.length; i++) {
323
+ if (branches[i].some(containsUuid)) {
324
+ if (branches.length > 1) {
325
+ selections[node.message.uuid] = i;
326
+ }
327
+ for (const child of branches[i]) {
328
+ if (traverse(child))
329
+ return true;
330
+ }
331
+ return true;
332
+ }
333
+ }
334
+ return false;
335
+ }
336
+ // Handle multi-root: check which root tree contains the target
337
+ const conversationRoots = roots.filter((r) => r.children.length > 0 || CONVERSATION_TYPES.has(r.message.type));
338
+ if (conversationRoots.length > 1) {
339
+ for (let i = 0; i < conversationRoots.length; i++) {
340
+ if (containsUuid(conversationRoots[i])) {
341
+ selections[ROOT_BRANCH_KEY] = i;
342
+ if (traverse(conversationRoots[i]))
343
+ return selections;
344
+ return selections;
345
+ }
346
+ }
347
+ return null;
348
+ }
349
+ const searchRoots = conversationRoots.length > 0 ? conversationRoots : roots;
350
+ for (const root of searchRoots) {
351
+ if (traverse(root))
352
+ return selections;
353
+ }
354
+ return null;
355
+ }
356
+ //# sourceMappingURL=messageTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messageTree.js","sourceRoot":"","sources":["../../src/utils/messageTree.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@
27
27
  "js-yaml": "^4.1.1",
28
28
  "multer": "^2.1.1",
29
29
  "node-pty": "^1.0.0",
30
+ "sharp": "^0.34.5",
30
31
  "simple-git": "^3.27.0",
31
32
  "socket.io": "^4.8.0",
32
33
  "web-push": "^3.6.7",
@@ -41,12 +42,13 @@
41
42
  "@types/js-yaml": "^4.0.9",
42
43
  "@types/multer": "^2.1.0",
43
44
  "@types/node": "^22.0.0",
45
+ "@types/sharp": "^0.31.1",
44
46
  "@types/supertest": "^6.0.3",
45
47
  "@types/web-push": "^3.6.4",
46
48
  "@vitest/coverage-v8": "^2.0.0",
47
49
  "supertest": "^7.2.2",
48
50
  "tsx": "^4.19.0",
49
51
  "typescript": "^5.6.0",
50
- "vitest": "^2.0.0"
52
+ "vitest": "^2.1.9"
51
53
  }
52
54
  }
@@ -0,0 +1,3 @@
1
+ export * from './errorCodes.js';
2
+ export * from './messageTree.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}