coolhand-cli 0.11.1 → 0.13.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 (195) hide show
  1. package/README.md +13 -1
  2. package/dist/api/client-file-client.d.ts +19 -0
  3. package/dist/api/client-file-client.d.ts.map +1 -0
  4. package/dist/api/client-file-client.js +47 -0
  5. package/dist/api/client-file-client.js.map +1 -0
  6. package/dist/api/help-center-client.d.ts +26 -0
  7. package/dist/api/help-center-client.d.ts.map +1 -0
  8. package/dist/api/help-center-client.js +90 -0
  9. package/dist/api/help-center-client.js.map +1 -0
  10. package/dist/api/last-sync.d.ts.map +1 -1
  11. package/dist/api/last-sync.js +2 -1
  12. package/dist/api/last-sync.js.map +1 -1
  13. package/dist/api/workload-client.d.ts +16 -0
  14. package/dist/api/workload-client.d.ts.map +1 -0
  15. package/dist/api/workload-client.js +28 -0
  16. package/dist/api/workload-client.js.map +1 -0
  17. package/dist/auth/callback-server.d.ts.map +1 -1
  18. package/dist/auth/callback-server.js +13 -1
  19. package/dist/auth/callback-server.js.map +1 -1
  20. package/dist/auto-update/index.d.ts +61 -0
  21. package/dist/auto-update/index.d.ts.map +1 -0
  22. package/dist/auto-update/index.js +355 -0
  23. package/dist/auto-update/index.js.map +1 -0
  24. package/dist/auto-update/select.d.ts +28 -0
  25. package/dist/auto-update/select.d.ts.map +1 -0
  26. package/dist/auto-update/select.js +45 -0
  27. package/dist/auto-update/select.js.map +1 -0
  28. package/dist/auto-update/state.d.ts +25 -0
  29. package/dist/auto-update/state.d.ts.map +1 -0
  30. package/dist/auto-update/state.js +81 -0
  31. package/dist/auto-update/state.js.map +1 -0
  32. package/dist/cli.d.ts.map +1 -1
  33. package/dist/cli.js +525 -5
  34. package/dist/cli.js.map +1 -1
  35. package/dist/commands/capture-turn.d.ts +18 -0
  36. package/dist/commands/capture-turn.d.ts.map +1 -0
  37. package/dist/commands/capture-turn.js +152 -0
  38. package/dist/commands/capture-turn.js.map +1 -0
  39. package/dist/commands/download-client-file.d.ts +9 -0
  40. package/dist/commands/download-client-file.d.ts.map +1 -0
  41. package/dist/commands/download-client-file.js +71 -0
  42. package/dist/commands/download-client-file.js.map +1 -0
  43. package/dist/commands/fetch-log.d.ts.map +1 -1
  44. package/dist/commands/fetch-log.js +9 -0
  45. package/dist/commands/fetch-log.js.map +1 -1
  46. package/dist/commands/flush-pending.d.ts.map +1 -1
  47. package/dist/commands/flush-pending.js +1 -30
  48. package/dist/commands/flush-pending.js.map +1 -1
  49. package/dist/commands/get-help-article.d.ts +3 -0
  50. package/dist/commands/get-help-article.d.ts.map +1 -0
  51. package/dist/commands/get-help-article.js +49 -0
  52. package/dist/commands/get-help-article.js.map +1 -0
  53. package/dist/commands/get-template.d.ts.map +1 -1
  54. package/dist/commands/get-template.js +18 -1
  55. package/dist/commands/get-template.js.map +1 -1
  56. package/dist/commands/link-feedback.d.ts +5 -0
  57. package/dist/commands/link-feedback.d.ts.map +1 -0
  58. package/dist/commands/link-feedback.js +89 -0
  59. package/dist/commands/link-feedback.js.map +1 -0
  60. package/dist/commands/list-client-files.d.ts +3 -0
  61. package/dist/commands/list-client-files.d.ts.map +1 -0
  62. package/dist/commands/list-client-files.js +55 -0
  63. package/dist/commands/list-client-files.js.map +1 -0
  64. package/dist/commands/list-help-categories.d.ts +3 -0
  65. package/dist/commands/list-help-categories.d.ts.map +1 -0
  66. package/dist/commands/list-help-categories.js +47 -0
  67. package/dist/commands/list-help-categories.js.map +1 -0
  68. package/dist/commands/search-help-articles.d.ts +3 -0
  69. package/dist/commands/search-help-articles.d.ts.map +1 -0
  70. package/dist/commands/search-help-articles.js +73 -0
  71. package/dist/commands/search-help-articles.js.map +1 -0
  72. package/dist/commands/search-logs.d.ts.map +1 -1
  73. package/dist/commands/search-logs.js +26 -0
  74. package/dist/commands/search-logs.js.map +1 -1
  75. package/dist/commands/search-templates.d.ts.map +1 -1
  76. package/dist/commands/search-templates.js +12 -0
  77. package/dist/commands/search-templates.js.map +1 -1
  78. package/dist/commands/search-workloads.d.ts +3 -0
  79. package/dist/commands/search-workloads.d.ts.map +1 -0
  80. package/dist/commands/search-workloads.js +79 -0
  81. package/dist/commands/search-workloads.js.map +1 -0
  82. package/dist/commands/update-client-file.d.ts +3 -0
  83. package/dist/commands/update-client-file.d.ts.map +1 -0
  84. package/dist/commands/update-client-file.js +39 -0
  85. package/dist/commands/update-client-file.js.map +1 -0
  86. package/dist/commands/update.d.ts +9 -0
  87. package/dist/commands/update.d.ts.map +1 -0
  88. package/dist/commands/update.js +63 -0
  89. package/dist/commands/update.js.map +1 -0
  90. package/dist/config.d.ts +2 -0
  91. package/dist/config.d.ts.map +1 -1
  92. package/dist/config.js +25 -7
  93. package/dist/config.js.map +1 -1
  94. package/dist/errors.d.ts +1 -1
  95. package/dist/errors.d.ts.map +1 -1
  96. package/dist/errors.js.map +1 -1
  97. package/dist/format-cost.d.ts +9 -0
  98. package/dist/format-cost.d.ts.map +1 -0
  99. package/dist/format-cost.js +22 -0
  100. package/dist/format-cost.js.map +1 -0
  101. package/dist/hooks/adapters/claude.d.ts +12 -0
  102. package/dist/hooks/adapters/claude.d.ts.map +1 -0
  103. package/dist/hooks/adapters/claude.js +98 -0
  104. package/dist/hooks/adapters/claude.js.map +1 -0
  105. package/dist/hooks/adapters/copilot.d.ts +15 -0
  106. package/dist/hooks/adapters/copilot.d.ts.map +1 -0
  107. package/dist/hooks/adapters/copilot.js +96 -0
  108. package/dist/hooks/adapters/copilot.js.map +1 -0
  109. package/dist/hooks/adapters/index.d.ts +5 -0
  110. package/dist/hooks/adapters/index.d.ts.map +1 -0
  111. package/dist/hooks/adapters/index.js +8 -0
  112. package/dist/hooks/adapters/index.js.map +1 -0
  113. package/dist/hooks/adapters/shared.d.ts +25 -0
  114. package/dist/hooks/adapters/shared.d.ts.map +1 -0
  115. package/dist/hooks/adapters/shared.js +37 -0
  116. package/dist/hooks/adapters/shared.js.map +1 -0
  117. package/dist/hooks/disabled.d.ts +3 -0
  118. package/dist/hooks/disabled.d.ts.map +1 -0
  119. package/dist/hooks/disabled.js +5 -0
  120. package/dist/hooks/disabled.js.map +1 -0
  121. package/dist/hooks/hook-log.d.ts +7 -0
  122. package/dist/hooks/hook-log.d.ts.map +1 -0
  123. package/dist/hooks/hook-log.js +34 -0
  124. package/dist/hooks/hook-log.js.map +1 -0
  125. package/dist/hooks/identity.d.ts +21 -0
  126. package/dist/hooks/identity.d.ts.map +1 -0
  127. package/dist/hooks/identity.js +121 -0
  128. package/dist/hooks/identity.js.map +1 -0
  129. package/dist/hooks/log-turn.d.ts +23 -0
  130. package/dist/hooks/log-turn.d.ts.map +1 -0
  131. package/dist/hooks/log-turn.js +48 -0
  132. package/dist/hooks/log-turn.js.map +1 -0
  133. package/dist/hooks/send-turn.d.ts +32 -0
  134. package/dist/hooks/send-turn.d.ts.map +1 -0
  135. package/dist/hooks/send-turn.js +305 -0
  136. package/dist/hooks/send-turn.js.map +1 -0
  137. package/dist/hooks/silence.d.ts +7 -0
  138. package/dist/hooks/silence.d.ts.map +1 -0
  139. package/dist/hooks/silence.js +22 -0
  140. package/dist/hooks/silence.js.map +1 -0
  141. package/dist/hooks/stdin.d.ts +11 -0
  142. package/dist/hooks/stdin.d.ts.map +1 -0
  143. package/dist/hooks/stdin.js +51 -0
  144. package/dist/hooks/stdin.js.map +1 -0
  145. package/dist/hooks/turn-state.d.ts +40 -0
  146. package/dist/hooks/turn-state.d.ts.map +1 -0
  147. package/dist/hooks/turn-state.js +97 -0
  148. package/dist/hooks/turn-state.js.map +1 -0
  149. package/dist/hooks/types.d.ts +74 -0
  150. package/dist/hooks/types.d.ts.map +1 -0
  151. package/dist/hooks/types.js +3 -0
  152. package/dist/hooks/types.js.map +1 -0
  153. package/dist/logger.d.ts.map +1 -1
  154. package/dist/logger.js +12 -1
  155. package/dist/logger.js.map +1 -1
  156. package/dist/pending-store.d.ts +22 -2
  157. package/dist/pending-store.d.ts.map +1 -1
  158. package/dist/pending-store.js +25 -3
  159. package/dist/pending-store.js.map +1 -1
  160. package/dist/pending-upload.d.ts +12 -0
  161. package/dist/pending-upload.d.ts.map +1 -0
  162. package/dist/pending-upload.js +51 -0
  163. package/dist/pending-upload.js.map +1 -0
  164. package/dist/sessions/blocks.d.ts +51 -0
  165. package/dist/sessions/blocks.d.ts.map +1 -0
  166. package/dist/sessions/blocks.js +193 -0
  167. package/dist/sessions/blocks.js.map +1 -0
  168. package/dist/sessions/claude-scanner.d.ts +60 -1
  169. package/dist/sessions/claude-scanner.d.ts.map +1 -1
  170. package/dist/sessions/claude-scanner.js +277 -1
  171. package/dist/sessions/claude-scanner.js.map +1 -1
  172. package/dist/sessions/copilot-scanner.d.ts +27 -0
  173. package/dist/sessions/copilot-scanner.d.ts.map +1 -0
  174. package/dist/sessions/copilot-scanner.js +171 -0
  175. package/dist/sessions/copilot-scanner.js.map +1 -0
  176. package/dist/sessions/redact-secrets.d.ts.map +1 -1
  177. package/dist/sessions/redact-secrets.js +49 -10
  178. package/dist/sessions/redact-secrets.js.map +1 -1
  179. package/dist/sessions/turn-envelope.d.ts +62 -0
  180. package/dist/sessions/turn-envelope.d.ts.map +1 -0
  181. package/dist/sessions/turn-envelope.js +182 -0
  182. package/dist/sessions/turn-envelope.js.map +1 -0
  183. package/dist/sessions/usage.d.ts +23 -0
  184. package/dist/sessions/usage.d.ts.map +1 -0
  185. package/dist/sessions/usage.js +56 -0
  186. package/dist/sessions/usage.js.map +1 -0
  187. package/dist/skills/skill-scanner.d.ts.map +1 -1
  188. package/dist/skills/skill-scanner.js +3 -1
  189. package/dist/skills/skill-scanner.js.map +1 -1
  190. package/dist/types.d.ts +107 -0
  191. package/dist/types.d.ts.map +1 -1
  192. package/dist/types.js.map +1 -1
  193. package/dist/version.d.ts +2 -2
  194. package/dist/version.js +1 -1
  195. package/package.json +7 -4
@@ -0,0 +1,171 @@
1
+ /** Event types `parseCopilotExchanges` reads; the rest (model snapshots, permissions, hooks) are skipped before parsing. */
2
+ const WANTED = new Set(['skill.invoked', 'user.message', 'assistant.message', 'tool.execution_complete', 'session.usage_record']);
3
+ const TYPE_PREFIX = /^\{"type":"([^"]*)"/;
4
+ /** A line whose type can't be read is parsed anyway, unless it is this big. */
5
+ const MAX_UNTYPED_LINE_CHARS = 1000000;
6
+ /** Whether a transcript line is one `parseCopilotExchanges` needs. Cheap: reads only the line's type, without parsing it. */
7
+ export function isWantedLine(line) {
8
+ const type = TYPE_PREFIX.exec(line)?.[1];
9
+ return type === undefined ? line.length <= MAX_UNTYPED_LINE_CHARS : WANTED.has(type);
10
+ }
11
+ function obj(value) {
12
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
13
+ }
14
+ function text(value) {
15
+ return typeof value === 'string' && value.trim() ? value : undefined;
16
+ }
17
+ function num(value) {
18
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
19
+ }
20
+ /**
21
+ * Copilot counts cached tokens inside `inputTokens`; the ingest API wants them apart, as Claude Code writes them.
22
+ */
23
+ function usageFrom(raw) {
24
+ const u = obj(raw);
25
+ if (!u) {
26
+ return undefined;
27
+ }
28
+ const cacheRead = num(u.cacheReadTokens);
29
+ const cacheWrite = num(u.cacheWriteTokens);
30
+ return {
31
+ input_tokens: Math.max(num(u.inputTokens) - cacheRead - cacheWrite, 0),
32
+ output_tokens: num(u.outputTokens),
33
+ cache_read_input_tokens: cacheRead,
34
+ cache_creation_input_tokens: cacheWrite,
35
+ };
36
+ }
37
+ /** What a tool returned. A failed call has not been seen in a recording, so its error text is looked for where it plausibly is. */
38
+ function resultText(result) {
39
+ const r = obj(result);
40
+ const candidates = [r?.content, r?.detailedContent, obj(r?.error)?.message];
41
+ const content = candidates.find((c) => typeof c === 'string' && c.trim()) ?? r?.content ?? r?.error;
42
+ if (typeof content === 'string') {
43
+ return content;
44
+ }
45
+ return content === undefined ? '' : JSON.stringify(content);
46
+ }
47
+ /**
48
+ * Walk a Copilot `events.jsonl` (VS Code agent mode) and return its user→assistant exchanges in order.
49
+ * An exchange starts at a `user.message` and holds what followed as raw messages: each model call's
50
+ * thinking, text and tool calls as one assistant message, and each batch of tool results as one user
51
+ * message. A `skill.invoked` event the user typed (`trigger: user-invoked`) is attached to the exchange of the
52
+ * prompt that comes next; one the model loaded during a turn is attached to the exchange already open. Unreadable
53
+ * lines are skipped. Pure function, never throws.
54
+ */
55
+ export function parseCopilotExchanges(content) {
56
+ const exchanges = [];
57
+ let model;
58
+ let current;
59
+ let pendingSkills = [];
60
+ const add = (exchange, message) => {
61
+ (exchange.messages ?? (exchange.messages = [])).push(message);
62
+ };
63
+ for (const line of content.split('\n')) {
64
+ if (!line.trim() || !isWantedLine(line)) {
65
+ continue;
66
+ }
67
+ let event;
68
+ try {
69
+ event = obj(JSON.parse(line));
70
+ }
71
+ catch {
72
+ continue;
73
+ }
74
+ const data = obj(event?.data);
75
+ if (!event || !data) {
76
+ continue;
77
+ }
78
+ const timestamp = text(event.timestamp);
79
+ switch (event.type) {
80
+ case 'skill.invoked': {
81
+ const file = text(data.path);
82
+ const name = text(data.name);
83
+ if (name) {
84
+ const skill = { name, dir: file ? file.replace(/[\\/][^\\/]*$/, '') : name, text: text(data.content) ?? '' };
85
+ // A skill the user typed (`/skill`) is logged just BEFORE its prompt. One the model loads itself is
86
+ // logged during the turn it belongs to, so it goes to the exchange already open.
87
+ if (current && text(data.trigger) !== undefined && data.trigger !== 'user-invoked') {
88
+ (current.skills ?? (current.skills = [])).push(skill);
89
+ }
90
+ else {
91
+ pendingSkills.push(skill);
92
+ }
93
+ }
94
+ break;
95
+ }
96
+ case 'user.message': {
97
+ current = { userText: text(data.content), timestamp };
98
+ if (pendingSkills.length > 0) {
99
+ current.skills = pendingSkills;
100
+ pendingSkills = [];
101
+ }
102
+ exchanges.push(current);
103
+ break;
104
+ }
105
+ case 'assistant.message': {
106
+ if (!current) {
107
+ break;
108
+ }
109
+ const blocks = [];
110
+ const reasoning = text(data.reasoningText);
111
+ if (reasoning) {
112
+ blocks.push({ type: 'thinking', thinking: reasoning });
113
+ }
114
+ const reply = text(data.content);
115
+ if (reply) {
116
+ blocks.push({ type: 'text', text: reply });
117
+ }
118
+ const requests = Array.isArray(data.toolRequests) ? data.toolRequests : [];
119
+ for (const request of requests) {
120
+ const r = obj(request);
121
+ if (r && text(r.name)) {
122
+ blocks.push({ type: 'tool_use', id: text(r.toolCallId), name: r.name, input: r.arguments ?? {} });
123
+ }
124
+ }
125
+ const key = text(data.messageId);
126
+ model = text(data.model) ?? model;
127
+ if (blocks.length > 0) {
128
+ add(current, { role: 'assistant', key, blocks });
129
+ }
130
+ // The final reply is the last message that answers in text without asking for a tool.
131
+ if (reply && requests.length === 0) {
132
+ current.assistantText = reply;
133
+ current.assistantKey = key;
134
+ }
135
+ break;
136
+ }
137
+ case 'tool.execution_complete': {
138
+ if (!current) {
139
+ break;
140
+ }
141
+ const block = { type: 'tool_result', tool_use_id: text(data.toolCallId), content: resultText(data.result), is_error: data.success === false };
142
+ const last = current.messages?.[current.messages.length - 1];
143
+ if (last && last.role === 'user') {
144
+ last.blocks.push(block);
145
+ }
146
+ else {
147
+ add(current, { role: 'user', blocks: [block] });
148
+ }
149
+ break;
150
+ }
151
+ case 'session.usage_record': {
152
+ // Only the conversation's own model calls (every recorded call of the main thread says so); other
153
+ // kinds of call, such as background helpers, are not what this turn spent.
154
+ const kind = text(obj(data.usage)?.interactionType);
155
+ const usage = kind === undefined || kind === 'conversation-agent' ? usageFrom(data.usage) : undefined;
156
+ if (current && usage) {
157
+ const total = (current.usage ?? (current.usage = { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 }));
158
+ total.input_tokens += usage.input_tokens;
159
+ total.output_tokens += usage.output_tokens;
160
+ total.cache_read_input_tokens += usage.cache_read_input_tokens;
161
+ total.cache_creation_input_tokens += usage.cache_creation_input_tokens;
162
+ }
163
+ break;
164
+ }
165
+ default:
166
+ break;
167
+ }
168
+ }
169
+ return { exchanges, model };
170
+ }
171
+ //# sourceMappingURL=copilot-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-scanner.js","sourceRoot":"","sources":["../../src/sessions/copilot-scanner.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"redact-secrets.d.ts","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA0CH,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWlD"}
1
+ {"version":3,"file":"redact-secrets.d.ts","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8EH,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBlD"}
@@ -14,24 +14,35 @@ const TOKEN_PATTERNS = [
14
14
  /ch_pub_[A-Za-z0-9_-]{8,}/g, // Coolhand public token
15
15
  /ch_priv_[A-Za-z0-9_-]{8,}/g, // Coolhand private token
16
16
  /sk-[A-Za-z0-9_-]{16,}/g, // OpenAI / Anthropic style secret key
17
- /ghp_[A-Za-z0-9]{20,}/g, // GitHub personal access token
18
- /gho_[A-Za-z0-9]{20,}/g, // GitHub OAuth token
17
+ /gh[pousr]_[A-Za-z0-9]{20,}/g, // GitHub personal access / OAuth / user-to-server / server-to-server / refresh token
19
18
  /github_pat_[A-Za-z0-9_]{20,}/g, // GitHub fine-grained PAT
20
- /AKIA[0-9A-Z]{16}/g, // AWS access key id
19
+ /\b(?:AKIA|ASIA|AROA|AIDA)[0-9A-Z]{16}\b/g, // AWS access key id (long-lived and temporary)
21
20
  /xox[baprs]-[A-Za-z0-9-]{10,}/g, // Slack token (bot/user/app/refresh/config)
21
+ /xapp-\d-[A-Za-z0-9-]{10,}/g, // Slack app-level token
22
+ /SG\.[\w-]{16,}\.[\w-]{16,}/g, // SendGrid API key
23
+ /\b(?:rk_live|rk_test)_[0-9A-Za-z]{16,}/g, // Stripe restricted key
24
+ /whsec_[A-Za-z0-9]{24,}/g, // Stripe webhook signing secret
22
25
  /AIza[0-9A-Za-z_-]{35}/g, // Google API key
23
26
  /sk_live_[0-9A-Za-z]{16,}/g, // Stripe live secret key
24
27
  /sk_test_[0-9A-Za-z]{16,}/g, // Stripe test secret key
25
- /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, // JWT (three base64url segments)
28
+ /\beyJ[A-Za-z0-9_-]{1,4096}\.[A-Za-z0-9_-]{1,4096}\.[A-Za-z0-9_-]{1,4096}/g, // JWT (three base64url segments)
26
29
  /\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi, // Authorization: Bearer <token>
27
30
  /\bBasic\s+[A-Za-z0-9+/=]{8,}/gi, // Authorization: Basic <base64 user:pass>
28
31
  /https:\/\/hooks\.slack\.com\/services\/\S+/g, // Slack incoming webhook URL
29
32
  /https:\/\/discord(?:app)?\.com\/api\/webhooks\/\S+/g, // Discord webhook URL
33
+ /npm_[A-Za-z0-9]{36}/g, // npm access token
34
+ /glpat-[A-Za-z0-9_-]{20,}/g, // GitLab personal access token
35
+ /hf_[A-Za-z0-9]{30,}/g, // Hugging Face token
36
+ /ya29\.[A-Za-z0-9_-]+/g, // Google OAuth access token
37
+ /pypi-[A-Za-z0-9_-]{20,}/g, // PyPI API token
38
+ /\b([a-z][a-z0-9+.-]{0,31}:\/\/)[^\s/:@]*:[^\s/]+@/gi, // credentials embedded in a URL (scheme://user:pass@host, empty user allowed, `@` inside the password)
30
39
  /\b[A-Fa-f0-9]{40,}\b/g, // long hex blobs (generic tokens / hashes)
31
40
  // PEM-formatted private key blocks (SSH/RSA/EC/OpenSSH/PGP/encrypted) — no assignment keyword
32
41
  // sits next to the base64 body, so ASSIGNMENT_QUOTED/BARE never catch these; a `cat ~/.ssh/id_rsa`
33
42
  // in a tool output or a deploy key embedded in a config file would otherwise sail through whole.
34
- /-----BEGIN [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----/g,
43
+ // The body is bounded and may end at the next BEGIN header or the end of the text, so a truncated key
44
+ // (`head -5 id_rsa`) is still redacted and a long run of unterminated headers cannot go quadratic.
45
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----[\s\S]{0,16384}?(?:-----END [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----|(?=-----BEGIN )|$)/g,
35
46
  ];
36
47
  // Two variants: a QUOTED value may contain spaces ("my secret value"); a BARE value stops at
37
48
  // whitespace. Splitting them closes the gap where a spaced quoted secret was only partly redacted.
@@ -40,9 +51,32 @@ const TOKEN_PATTERNS = [
40
51
  // this was originally written for), so plain JSON — the dominant config format under
41
52
  // `~/.claude` — is covered too, not just unquoted-key assignment syntax.
42
53
  // The opening quote may be backslash-escaped (`\\"value\\"`): session payloads are JSON.stringify'd
43
- // before redaction, so a quoted shell assignment inside a tool input arrives with escaped quotes.
44
- const ASSIGNMENT_QUOTED = /((?:api[_-]?key|secret|token|password|passwd|access[_-]?key|private[_-]?key)\b["']?\s*[:=]\s*)(\\?['"])[^'"]*\2/gi;
45
- const ASSIGNMENT_BARE = /((?:api[_-]?key|secret|token|password|passwd|access[_-]?key|private[_-]?key)\b["']?\s*[:=]\s*)[^\s'"]+/gi;
54
+ // before redaction, so a quoted shell assignment inside a tool input arrives with escaped quotes. A BARE value
55
+ // never starts with such a backslash-quote, so it cannot re-match what the QUOTED pass just redacted.
56
+ // A keyword may carry a `_value`/`_header` suffix (`CLIENT_SECRET_VALUE=`). `pass` and `pwd` count only inside a compound name (`DB_PASS`, `MYSQL_PWD`), so `bypass:` and `Tests pass:` are not secrets.
57
+ const KEYWORDS = String.raw `(?:api[_-]?key|secret(?:[_-]?key)?|token|password|passwd|(?<=[_-])(?:pass|pwd)|passphrase|credentials?|auth[_-]?header|access[_-]?key|private[_-]?key|auth[_-]?key|signing[_-]?key|encryption[_-]?key)(?:[_-]?(?:value|header))?`;
58
+ // A value can be `Bearer <token>`: take the scheme word with it, or the token pattern below never sees `Bearer`.
59
+ // Values that are settings, not secrets (`credentials: "include"` in a fetch call, `"token": null`).
60
+ const NOT_A_SECRET = String.raw `(?!(?:include|omit|same-origin|true|false|null)\b)`;
61
+ // A bare value is a scalar: `{` or `[` opens a JSON object or array (`"token":{...}`), which it must not swallow.
62
+ const NOT_A_STRUCTURE = String.raw `(?![{\[])`;
63
+ const SCHEME = String.raw `(?:(?:bearer|basic|token|digest|negotiate)[ \t]+)?`;
64
+ const ASSIGNMENT_QUOTED = new RegExp(String.raw `((?:${KEYWORDS})\b["']?\s*[:=]\s*)(\\?['"])${NOT_A_SECRET}[^'"]*\2`, 'gi');
65
+ const ASSIGNMENT_BARE = new RegExp(String.raw `((?:${KEYWORDS})\b["']?\s*[:=]\s*)${NOT_A_SECRET}${NOT_A_STRUCTURE}${SCHEME}(?:\\\\|\\(?!['"])|[^\s'"\\])+`, 'gi');
66
+ // Cookie and authorization headers. Redacting JSON-stringified text (one line, the header inside a string) must
67
+ // leave it parseable, so a value stops at a quote, backslash or line end. A JSON-keyed header (`"Cookie":"sid=1"`)
68
+ // is a value whatever it looks like; in plain text only a `name=value` cookie or a scheme-word authorization
69
+ // (or one long opaque token) counts, so `const cookie: string` and "the Authorization: header" are left alone.
70
+ const HEADER_JSON = /((?<![\w-])(?:(?:set-)?cookie|(?:proxy-)?authorization)\\?["']?\s*[:=]\s*\\?["'])[^"'\\]+/gi;
71
+ const HEADER_COOKIE = /((?<![\w-])(?:set-)?cookie[ \t]*:[ \t]*)[\w.%+-]{1,200}=[^\r\n"'\\`}]*/gi;
72
+ const HEADER_AUTH = /((?<![\w-])(?:proxy-)?authorization[ \t]*[:=][ \t]*)(?:(?:bearer|basic|token|digest|negotiate|apikey|ntlm|hmac|oauth)[ \t]+[^\r\n"'\\`}]+|[^\s"'\\`}]{16,})/gi;
73
+ // A secret given as a command-line flag with its value after a space (`--password hunter2`, `-u admin:s3cr3t`):
74
+ // no `:` or `=` joins them, so the assignment patterns above never see it. The value may be quoted, or
75
+ // backslash-quoted when the text was JSON-stringified; the quoted forms are bounded so an unclosed quote is cheap.
76
+ const CLI_SECRET_FLAG = /((?<![\w-])--?(?:(?:[a-z]{1,20}-){0,3}(?:password|passwd|token|secret|api-?key|auth(?:orization)?|access-?key|private-?key)(?:-(?:key|value))?|pass|pwd)(?:[ \t]{1,10}|=))(?:(?:bearer|basic|token|digest|negotiate)[ \t]+)?(?:\\["'][^\\]{0,500}\\["']|'[^']{0,500}'|"[^"]{0,500}"|[^\s'"\\]+)/gi;
77
+ // `curl -u user:pass`, also `--user`, a flag cluster (`-su`), a quoted credential and a `\`-newline continuation.
78
+ // The separator and the user name share no characters, so a long run of `=` cannot backtrack quadratically.
79
+ const CLI_BASIC_AUTH = /((?<![\w-])curl\b(?:[^\n]|\\\r?\n){0,500}?\s(?:--user|-[a-zA-Z]{0,5}u)(?:[ \t]{1,10}|=)\\?["']?)[^\s:='"\\]+:[^\s'"\\]+/g;
46
80
  const REDACTED = '[REDACTED]';
47
81
  /** Return `text` with recognised secrets replaced by `[REDACTED]`. Safe on empty input. */
48
82
  export function redactSecrets(text) {
@@ -51,9 +85,14 @@ export function redactSecrets(text) {
51
85
  }
52
86
  let out = text
53
87
  .replace(ASSIGNMENT_QUOTED, `$1$2${REDACTED}$2`)
54
- .replace(ASSIGNMENT_BARE, `$1${REDACTED}`);
88
+ .replace(ASSIGNMENT_BARE, `$1${REDACTED}`)
89
+ .replace(HEADER_JSON, `$1${REDACTED}`)
90
+ .replace(HEADER_COOKIE, `$1${REDACTED}`)
91
+ .replace(HEADER_AUTH, `$1${REDACTED}`)
92
+ .replace(CLI_SECRET_FLAG, `$1${REDACTED}`)
93
+ .replace(CLI_BASIC_AUTH, `$1${REDACTED}`);
55
94
  for (const pattern of TOKEN_PATTERNS) {
56
- out = out.replace(pattern, REDACTED);
95
+ out = out.replace(pattern, (_match, scheme) => (typeof scheme === 'string' ? `${scheme}${REDACTED}@` : REDACTED));
57
96
  }
58
97
  return out;
59
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"redact-secrets.js","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,MAAM,cAAc,GAAsB;IACxC,2BAA2B,EAAE,wBAAwB;IACrD,4BAA4B,EAAE,yBAAyB;IACvD,wBAAwB,EAAE,sCAAsC;IAChE,uBAAuB,EAAE,+BAA+B;IACxD,uBAAuB,EAAE,qBAAqB;IAC9C,+BAA+B,EAAE,0BAA0B;IAC3D,mBAAmB,EAAE,oBAAoB;IACzC,+BAA+B,EAAE,4CAA4C;IAC7E,wBAAwB,EAAE,iBAAiB;IAC3C,2BAA2B,EAAE,yBAAyB;IACtD,2BAA2B,EAAE,yBAAyB;IACtD,sDAAsD,EAAE,iCAAiC;IACzF,uCAAuC,EAAE,gCAAgC;IACzE,gCAAgC,EAAE,0CAA0C;IAC5E,6CAA6C,EAAE,6BAA6B;IAC5E,qDAAqD,EAAE,sBAAsB;IAC7E,uBAAuB,EAAE,2CAA2C;IACpE,8FAA8F;IAC9F,mGAAmG;IACnG,iGAAiG;IACjG,uGAAuG;CACxG,CAAC;AAEF,6FAA6F;AAC7F,mGAAmG;AACnG,+FAA+F;AAC/F,oGAAoG;AACpG,qFAAqF;AACrF,yEAAyE;AACzE,oGAAoG;AACpG,kGAAkG;AAClG,MAAM,iBAAiB,GACrB,mHAAmH,CAAC;AACtH,MAAM,eAAe,GACnB,0GAA0G,CAAC;AAE7G,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,GAAG,IAAI;SACX,OAAO,CAAC,iBAAiB,EAAE,OAAO,QAAQ,IAAI,CAAC;SAC/C,OAAO,CAAC,eAAe,EAAE,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC7C,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"redact-secrets.js","sourceRoot":"","sources":["../../src/sessions/redact-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,MAAM,cAAc,GAAsB;IACxC,2BAA2B,EAAE,wBAAwB;IACrD,4BAA4B,EAAE,yBAAyB;IACvD,wBAAwB,EAAE,sCAAsC;IAChE,6BAA6B,EAAE,qFAAqF;IACpH,+BAA+B,EAAE,0BAA0B;IAC3D,0CAA0C,EAAE,+CAA+C;IAC3F,+BAA+B,EAAE,4CAA4C;IAC7E,4BAA4B,EAAE,wBAAwB;IACtD,6BAA6B,EAAE,mBAAmB;IAClD,yCAAyC,EAAE,wBAAwB;IACnE,yBAAyB,EAAE,gCAAgC;IAC3D,wBAAwB,EAAE,iBAAiB;IAC3C,2BAA2B,EAAE,yBAAyB;IACtD,2BAA2B,EAAE,yBAAyB;IACtD,2EAA2E,EAAE,iCAAiC;IAC9G,uCAAuC,EAAE,gCAAgC;IACzE,gCAAgC,EAAE,0CAA0C;IAC5E,6CAA6C,EAAE,6BAA6B;IAC5E,qDAAqD,EAAE,sBAAsB;IAC7E,sBAAsB,EAAE,mBAAmB;IAC3C,2BAA2B,EAAE,+BAA+B;IAC5D,sBAAsB,EAAE,qBAAqB;IAC7C,uBAAuB,EAAE,4BAA4B;IACrD,0BAA0B,EAAE,iBAAiB;IAC7C,qDAAqD,EAAE,uGAAuG;IAC9J,uBAAuB,EAAE,2CAA2C;IACpE,8FAA8F;IAC9F,mGAAmG;IACnG,iGAAiG;IACjG,sGAAsG;IACtG,mGAAmG;IACnG,qIAAqI;CACtI,CAAC;AAEF,6FAA6F;AAC7F,mGAAmG;AACnG,+FAA+F;AAC/F,oGAAoG;AACpG,qFAAqF;AACrF,yEAAyE;AACzE,oGAAoG;AACpG,+GAA+G;AAC/G,sGAAsG;AACtG,yMAAyM;AACzM,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA,kOAAkO,CAAC;AAC9P,iHAAiH;AACjH,qGAAqG;AACrG,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAA,oDAAoD,CAAC;AACpF,kHAAkH;AAClH,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA,WAAW,CAAC;AAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA,oDAAoD,CAAC;AAC9E,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,OAAO,QAAQ,+BAA+B,YAAY,UAAU,EAAE,IAAI,CAAC,CAAC;AAC3H,MAAM,eAAe,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,OAAO,QAAQ,sBAAsB,YAAY,GAAG,eAAe,GAAG,MAAM,gCAAgC,EAAE,IAAI,CAAC,CAAC;AACjK,gHAAgH;AAChH,mHAAmH;AACnH,6GAA6G;AAC7G,+GAA+G;AAC/G,MAAM,WAAW,GAAG,6FAA6F,CAAC;AAClH,MAAM,aAAa,GAAG,0EAA0E,CAAC;AACjG,MAAM,WAAW,GACf,+JAA+J,CAAC;AAElK,gHAAgH;AAChH,uGAAuG;AACvG,mHAAmH;AACnH,MAAM,eAAe,GACnB,mSAAmS,CAAC;AACtS,kHAAkH;AAClH,4GAA4G;AAC5G,MAAM,cAAc,GAClB,0HAA0H,CAAC;AAE7H,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,GAAG,IAAI;SACX,OAAO,CAAC,iBAAiB,EAAE,OAAO,QAAQ,IAAI,CAAC;SAC/C,OAAO,CAAC,eAAe,EAAE,KAAK,QAAQ,EAAE,CAAC;SACzC,OAAO,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;SACrC,OAAO,CAAC,aAAa,EAAE,KAAK,QAAQ,EAAE,CAAC;SACvC,OAAO,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;SACrC,OAAO,CAAC,eAAe,EAAE,KAAK,QAAQ,EAAE,CAAC;SACzC,OAAO,CAAC,cAAc,EAAE,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAc,EAAE,MAAe,EAAE,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAA,GAAG,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpI,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { MAX_TURN_TEXT_CHARS } from './blocks.js';
2
+ import type { CaptureEnvelope } from './claude-scanner.js';
3
+ import { type SubagentUsage, type Usage } from './usage.js';
4
+ import type { HookAgent, HookTurn, TurnMessage } from '../hooks/types.js';
5
+ export { MAX_TURN_TEXT_CHARS };
6
+ /**
7
+ * Cap on the characters of EARLIER messages sent with a turn (the current turn's own user message is
8
+ * extra, and is itself capped at MAX_TURN_TEXT_CHARS). The payload grows with the conversation, so
9
+ * when the history would exceed this the OLDEST whole exchanges are dropped and the most recent are
10
+ * kept: the server should expect a truncated head, flagged by `metadata.history_truncated`. The newest
11
+ * earlier exchange is always kept, even if it alone is over the cap. Counted in characters (UTF-16
12
+ * code units), not bytes.
13
+ */
14
+ export declare const HISTORY_BUDGET_CHARS = 200000;
15
+ export interface PreparedMessages {
16
+ /** The conversation so far, ending on the current turn's user message (when there is one). */
17
+ messages: TurnMessage[];
18
+ /** Messages dropped from the start of the history to stay inside HISTORY_BUDGET_CHARS (a count of messages in the transcript window: one that redacts to nothing is still counted, and anything older than the window is not). */
19
+ omitted: number;
20
+ /** True when part of the conversation since the skill was invoked is not in `messages`. */
21
+ truncated: boolean;
22
+ /**
23
+ * Tokens the main-thread assistant messages spent across the same exchanges as the history, plus
24
+ * this turn's: a CUMULATIVE total, so a replayed or late older turn can't double count. Absent when
25
+ * the transcript shows none.
26
+ */
27
+ usage?: Usage;
28
+ /** The same, for subagents (other models, so priced separately). Absent when there were none. */
29
+ subagents?: SubagentUsage;
30
+ }
31
+ /**
32
+ * The messages sent with turn `turnIndex`: every earlier message (prompts, thinking, tool calls and results,
33
+ * replies) since the skill was invoked, in order, then this turn's user message. "Since the skill was invoked" is the session's
34
+ * `anchor` — the timestamp of its first captured turn — and only exchanges a hook actually captured
35
+ * (`captured`) are included, so nothing the user said before the skill ran, or while capture was off,
36
+ * is uploaded, however many exchanges the transcript holds that no hook saw (`/compact`, image
37
+ * prompts, a second Stop for one prompt). Without a usable anchor there is no history at all. What is
38
+ * sent is prompts, thinking, tool calls and results and replies, redacted and capped like the turn
39
+ * itself. If the start of the skill's conversation is no longer in the transcript window, or the history is over budget, the OLDEST part is what is missing and
40
+ * `truncated` is set.
41
+ */
42
+ export declare function prepareTurnMessages(turn: HookTurn, turnIndex: number, anchor: string | undefined, captured: ReadonlySet<string>): PreparedMessages;
43
+ /**
44
+ * An Anthropic-shaped envelope for ONE turn captured by a hook, carrying the conversation so far.
45
+ *
46
+ * `request_body.messages` is the conversation since the skill was invoked, in order, ending on the last
47
+ * message before this turn's reply (`prepareTurnMessages`, passed in as `prepared`); `response_body` is this turn's
48
+ * reply. The server keeps one chat log per session and the newest turn replaces the stored one, so a
49
+ * late-arriving older turn (a replay) is ignored by it.
50
+ *
51
+ * The url is `coolhand-hook://<agent>/session/<id>/turn/<n>` — deliberately NOT the
52
+ * `claudecode://session/<id>` scheme `analyze-claude-sessions` uses. The server needs the session id
53
+ * and the turn number from it. `response_body.id` is `<session>:<turn>:<digest>`, and the digest ties
54
+ * it to the turn's content. It is no longer the server's dedup key, but stays unique per turn for
55
+ * debugging and correlation. What goes in is the conversation as structured blocks: prompts, thinking,
56
+ * tool calls, tool results and replies (subagent traffic and injected context excluded), all
57
+ * secret-scrubbed and capped (see blocks.ts).
58
+ */
59
+ export declare function buildTurnEnvelope(agent: HookAgent, turn: HookTurn, turnIndex: number, digest: string, prepared: PreparedMessages,
60
+ /** The skill's text, sent as `request_body.system`. Redacted and capped here again, so state saved by an older version is scrubbed too. */
61
+ system?: string): CaptureEnvelope;
62
+ //# sourceMappingURL=turn-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-envelope.d.ts","sourceRoot":"","sources":["../../src/sessions/turn-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAA6E,MAAM,aAAa,CAAC;AAC7H,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAyE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACnI,OAAO,KAAK,EAAmB,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B,8FAA8F;IAC9F,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,kOAAkO;IAClO,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iGAAiG;IACjG,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AA2CD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,gBAAgB,CA0ElB;AAWD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB;AAC1B,2IAA2I;AAC3I,MAAM,CAAC,EAAE,MAAM,GACd,eAAe,CAqBjB"}
@@ -0,0 +1,182 @@
1
+ import { MAX_TURN_TEXT_CHARS, cleanText, messageSize, sanitizeBlock, sanitizeMessage } from './blocks.js';
2
+ import { addUsage, emptySubagentUsage, emptyUsage, mergeSubagents, totalTokens } from './usage.js';
3
+ export { MAX_TURN_TEXT_CHARS };
4
+ /**
5
+ * Cap on the characters of EARLIER messages sent with a turn (the current turn's own user message is
6
+ * extra, and is itself capped at MAX_TURN_TEXT_CHARS). The payload grows with the conversation, so
7
+ * when the history would exceed this the OLDEST whole exchanges are dropped and the most recent are
8
+ * kept: the server should expect a truncated head, flagged by `metadata.history_truncated`. The newest
9
+ * earlier exchange is always kept, even if it alone is over the cap. Counted in characters (UTF-16
10
+ * code units), not bytes.
11
+ */
12
+ export const HISTORY_BUDGET_CHARS = 200000;
13
+ function promptMessage(userText) {
14
+ return { role: 'user', content: cleanText(userText) };
15
+ }
16
+ /**
17
+ * One exchange as redacted, capped messages: the prompt, then everything the transcript shows after it
18
+ * (thinking, tool calls, tool results, replies) in order. A hand-built exchange with only reply text
19
+ * (no transcript behind it) falls back to prompt + reply.
20
+ */
21
+ function exchangeMessages(exchange) {
22
+ const out = [promptMessage(exchange.userText)];
23
+ if (exchange.messages) {
24
+ for (const raw of exchange.messages) {
25
+ const message = sanitizeMessage(raw);
26
+ if (message) {
27
+ out.push(message);
28
+ }
29
+ }
30
+ }
31
+ else if (exchange.assistantText) {
32
+ const reply = cleanText(exchange.assistantText);
33
+ if (reply) {
34
+ out.push({ role: 'assistant', content: reply });
35
+ }
36
+ }
37
+ return out;
38
+ }
39
+ /** Characters an exchange will occupy once capped (an estimate: redaction usually shortens it, but `[REDACTED]` can replace a shorter secret). */
40
+ function cappedSize(exchange) {
41
+ const prompt = Math.min(exchange.userText.length, MAX_TURN_TEXT_CHARS);
42
+ if (exchange.messages) {
43
+ return prompt + exchange.messages.reduce((n, m) => n + messageSize(m), 0);
44
+ }
45
+ return prompt + Math.min(exchange.assistantText?.length ?? 0, MAX_TURN_TEXT_CHARS);
46
+ }
47
+ /** How many messages an exchange would have contributed (for `omitted`). */
48
+ function messageCount(exchange) {
49
+ return 1 + (exchange.messages ? exchange.messages.length : exchange.assistantText ? 1 : 0);
50
+ }
51
+ /**
52
+ * The messages sent with turn `turnIndex`: every earlier message (prompts, thinking, tool calls and results,
53
+ * replies) since the skill was invoked, in order, then this turn's user message. "Since the skill was invoked" is the session's
54
+ * `anchor` — the timestamp of its first captured turn — and only exchanges a hook actually captured
55
+ * (`captured`) are included, so nothing the user said before the skill ran, or while capture was off,
56
+ * is uploaded, however many exchanges the transcript holds that no hook saw (`/compact`, image
57
+ * prompts, a second Stop for one prompt). Without a usable anchor there is no history at all. What is
58
+ * sent is prompts, thinking, tool calls and results and replies, redacted and capped like the turn
59
+ * itself. If the start of the skill's conversation is no longer in the transcript window, or the history is over budget, the OLDEST part is what is missing and
60
+ * `truncated` is set.
61
+ */
62
+ export function prepareTurnMessages(turn, turnIndex, anchor, captured) {
63
+ const anchorMs = anchor ? Date.parse(anchor) : Number.NaN;
64
+ const earlier = turn.earlier ?? [];
65
+ // Exchanges at or after the anchor that a hook actually captured, in order. One with no usable
66
+ // timestamp can't be placed, so it is left out (fail closed); inside the skill's conversation that
67
+ // is a gap worth flagging. So is an answered exchange no hook captured (capture was off, or the hook
68
+ // was killed): it is never swept up into a later upload. An uncaptured exchange with no reply (a
69
+ // command, an interrupted prompt) is dropped quietly, because there was no turn to capture.
70
+ const since = [];
71
+ let gap = false;
72
+ const usage = emptyUsage();
73
+ const subagents = emptySubagentUsage();
74
+ addUsage(usage, turn.usage);
75
+ mergeSubagents(subagents, turn.subagents);
76
+ if (turnIndex > 1 && !Number.isNaN(anchorMs)) {
77
+ for (const e of earlier) {
78
+ const ms = e.timestamp ? Date.parse(e.timestamp) : Number.NaN;
79
+ if (Number.isNaN(ms)) {
80
+ gap || (gap = since.length > 0);
81
+ }
82
+ else if (ms >= anchorMs) {
83
+ if (e.timestamp && captured.has(e.timestamp)) {
84
+ since.push(e);
85
+ addUsage(usage, e.usage);
86
+ mergeSubagents(subagents, e.subagents);
87
+ }
88
+ else if (e.assistantText) {
89
+ gap = true;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ // Newest first, so the budget keeps the most recent; the newest is kept whatever its size.
95
+ const kept = [];
96
+ let used = 0;
97
+ for (let i = since.length - 1; i >= 0; i--) {
98
+ const size = cappedSize(since[i]);
99
+ if (kept.length > 0 && used + size > HISTORY_BUDGET_CHARS) {
100
+ break;
101
+ }
102
+ kept.unshift(since[i]);
103
+ used += size;
104
+ }
105
+ const dropped = since.slice(0, since.length - kept.length);
106
+ const omitted = dropped.reduce((n, e) => n + messageCount(e), 0);
107
+ const messages = kept.flatMap(exchangeMessages);
108
+ if (turn.userText) {
109
+ messages.push(promptMessage(turn.userText));
110
+ }
111
+ // This turn's own tool loop (everything up to, not including, its final reply).
112
+ for (const raw of turn.currentMessages ?? []) {
113
+ const message = sanitizeMessage(raw);
114
+ if (message) {
115
+ messages.push(message);
116
+ }
117
+ }
118
+ // Did we see where the skill's conversation began? Turn 1 is that start; later turns need the
119
+ // anchor's own exchange to still be in the window.
120
+ const stamps = turn.earlierStamps ?? earlier.flatMap((e) => (e.timestamp ? [e.timestamp] : []));
121
+ // A captured exchange that can't be rendered (an image-only prompt: no text of its own, so there is
122
+ // no user message to pair its reply with) is missing from the history; say so.
123
+ const rendered = new Set(earlier.flatMap((e) => (e.timestamp ? [e.timestamp] : [])));
124
+ const unrenderable = turnIndex > 1 && !Number.isNaN(anchorMs) && stamps.some((st) => Date.parse(st) >= anchorMs && captured.has(st) && !rendered.has(st));
125
+ const anchorSeen = turnIndex <= 1 || (anchor !== undefined && (turn.exchangeTimestamp === anchor || stamps.includes(anchor)));
126
+ return {
127
+ messages,
128
+ omitted,
129
+ truncated: omitted > 0 || gap || unrenderable || !anchorSeen,
130
+ ...(totalTokens(usage) > 0 ? { usage } : {}),
131
+ ...(subagents.calls > 0 ? { subagents } : {}),
132
+ };
133
+ }
134
+ /**
135
+ * The blocks of this turn's reply: the transcript's own (thinking, then text) when it shows the final
136
+ * message, otherwise just the text the hook was given.
137
+ */
138
+ function replyBlocks(turn) {
139
+ const blocks = (turn.finalReply ?? []).map(sanitizeBlock).filter((b) => b !== undefined);
140
+ return blocks.some((b) => b.type === 'text') ? blocks : [{ type: 'text', text: cleanText(turn.assistantText) }];
141
+ }
142
+ /**
143
+ * An Anthropic-shaped envelope for ONE turn captured by a hook, carrying the conversation so far.
144
+ *
145
+ * `request_body.messages` is the conversation since the skill was invoked, in order, ending on the last
146
+ * message before this turn's reply (`prepareTurnMessages`, passed in as `prepared`); `response_body` is this turn's
147
+ * reply. The server keeps one chat log per session and the newest turn replaces the stored one, so a
148
+ * late-arriving older turn (a replay) is ignored by it.
149
+ *
150
+ * The url is `coolhand-hook://<agent>/session/<id>/turn/<n>` — deliberately NOT the
151
+ * `claudecode://session/<id>` scheme `analyze-claude-sessions` uses. The server needs the session id
152
+ * and the turn number from it. `response_body.id` is `<session>:<turn>:<digest>`, and the digest ties
153
+ * it to the turn's content. It is no longer the server's dedup key, but stays unique per turn for
154
+ * debugging and correlation. What goes in is the conversation as structured blocks: prompts, thinking,
155
+ * tool calls, tool results and replies (subagent traffic and injected context excluded), all
156
+ * secret-scrubbed and capped (see blocks.ts).
157
+ */
158
+ export function buildTurnEnvelope(agent, turn, turnIndex, digest, prepared,
159
+ /** The skill's text, sent as `request_body.system`. Redacted and capped here again, so state saved by an older version is scrubbed too. */
160
+ system) {
161
+ return {
162
+ url: `coolhand-hook://${agent}/session/${encodeURIComponent(turn.sessionId)}/turn/${turnIndex}`,
163
+ method: 'POST',
164
+ status_code: 200,
165
+ request_body: {
166
+ model: turn.model,
167
+ ...(system ? { system: cleanText(system) } : {}),
168
+ messages: prepared.messages,
169
+ },
170
+ response_body: {
171
+ id: `${turn.sessionId}:${turnIndex}:${digest.slice(0, 8)}`,
172
+ type: 'message',
173
+ role: 'assistant',
174
+ model: turn.model,
175
+ content: replyBlocks(turn),
176
+ ...(prepared.usage ? { usage: prepared.usage } : {}),
177
+ },
178
+ turnCount: turnIndex,
179
+ projectPath: turn.cwd,
180
+ };
181
+ }
182
+ //# sourceMappingURL=turn-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-envelope.js","sourceRoot":"","sources":["../../src/sessions/turn-envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** Token counts in the shape Claude Code writes on every assistant message (and the ingest API reads). */
2
+ export interface Usage {
3
+ input_tokens: number;
4
+ output_tokens: number;
5
+ cache_read_input_tokens: number;
6
+ cache_creation_input_tokens: number;
7
+ }
8
+ /** What subagents (Agent tool calls) spent, which a single log can't price: they run on other models. */
9
+ export interface SubagentUsage {
10
+ calls: number;
11
+ total: Usage;
12
+ by_model: Record<string, Usage>;
13
+ }
14
+ export declare function emptyUsage(): Usage;
15
+ export declare function emptySubagentUsage(): SubagentUsage;
16
+ /** Add the four token fields of a raw `usage` object (anything else in it is ignored) into `into`. */
17
+ export declare function addUsage(into: Usage, raw: unknown): void;
18
+ export declare function totalTokens(u: Usage): number;
19
+ /** Add one subagent run (an Agent tool result) under the model it ran on. */
20
+ export declare function addSubagent(into: SubagentUsage, model: string, raw: unknown): void;
21
+ /** Fold `from` into `into`. */
22
+ export declare function mergeSubagents(into: SubagentUsage, from: SubagentUsage | undefined): void;
23
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/sessions/usage.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,MAAM,WAAW,KAAK;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,2BAA2B,EAAE,MAAM,CAAC;CACrC;AAED,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CACjC;AAED,wBAAgB,UAAU,IAAI,KAAK,CAElC;AAED,wBAAgB,kBAAkB,IAAI,aAAa,CAElD;AAMD,sGAAsG;AACtG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CASxD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAE5C;AAiBD,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAMlF;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CASzF"}