skein-cli 0.1.0-alpha.1

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 (250) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/bin/skein.mjs +7 -0
  4. package/dist/adapters/aider/conv-reader.d.ts +7 -0
  5. package/dist/adapters/aider/conv-reader.js +203 -0
  6. package/dist/adapters/aider/conv-reader.js.map +1 -0
  7. package/dist/adapters/aider/index.d.ts +2 -0
  8. package/dist/adapters/aider/index.js +123 -0
  9. package/dist/adapters/aider/index.js.map +1 -0
  10. package/dist/adapters/base.d.ts +71 -0
  11. package/dist/adapters/base.js +38 -0
  12. package/dist/adapters/base.js.map +1 -0
  13. package/dist/adapters/claude-code/conv-reader.d.ts +2 -0
  14. package/dist/adapters/claude-code/conv-reader.js +155 -0
  15. package/dist/adapters/claude-code/conv-reader.js.map +1 -0
  16. package/dist/adapters/claude-code/index.d.ts +3 -0
  17. package/dist/adapters/claude-code/index.js +517 -0
  18. package/dist/adapters/claude-code/index.js.map +1 -0
  19. package/dist/adapters/claude-desktop/index.d.ts +2 -0
  20. package/dist/adapters/claude-desktop/index.js +95 -0
  21. package/dist/adapters/claude-desktop/index.js.map +1 -0
  22. package/dist/adapters/codex/index.d.ts +2 -0
  23. package/dist/adapters/codex/index.js +472 -0
  24. package/dist/adapters/codex/index.js.map +1 -0
  25. package/dist/adapters/cursor/index.d.ts +2 -0
  26. package/dist/adapters/cursor/index.js +255 -0
  27. package/dist/adapters/cursor/index.js.map +1 -0
  28. package/dist/adapters/opencode/conv-reader.d.ts +3 -0
  29. package/dist/adapters/opencode/conv-reader.js +190 -0
  30. package/dist/adapters/opencode/conv-reader.js.map +1 -0
  31. package/dist/adapters/opencode/index.d.ts +2 -0
  32. package/dist/adapters/opencode/index.js +349 -0
  33. package/dist/adapters/opencode/index.js.map +1 -0
  34. package/dist/adapters/registry.d.ts +4 -0
  35. package/dist/adapters/registry.js +26 -0
  36. package/dist/adapters/registry.js.map +1 -0
  37. package/dist/cli.d.ts +3 -0
  38. package/dist/cli.js +54 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/commands/conv.d.ts +2 -0
  41. package/dist/commands/conv.js +261 -0
  42. package/dist/commands/conv.js.map +1 -0
  43. package/dist/commands/doctor.d.ts +2 -0
  44. package/dist/commands/doctor.js +85 -0
  45. package/dist/commands/doctor.js.map +1 -0
  46. package/dist/commands/init.d.ts +2 -0
  47. package/dist/commands/init.js +38 -0
  48. package/dist/commands/init.js.map +1 -0
  49. package/dist/commands/memory.d.ts +2 -0
  50. package/dist/commands/memory.js +197 -0
  51. package/dist/commands/memory.js.map +1 -0
  52. package/dist/commands/migrate.d.ts +2 -0
  53. package/dist/commands/migrate.js +102 -0
  54. package/dist/commands/migrate.js.map +1 -0
  55. package/dist/commands/profile.d.ts +2 -0
  56. package/dist/commands/profile.js +183 -0
  57. package/dist/commands/profile.js.map +1 -0
  58. package/dist/commands/redact.d.ts +2 -0
  59. package/dist/commands/redact.js +73 -0
  60. package/dist/commands/redact.js.map +1 -0
  61. package/dist/commands/trace.d.ts +2 -0
  62. package/dist/commands/trace.js +181 -0
  63. package/dist/commands/trace.js.map +1 -0
  64. package/dist/commands/view.d.ts +13 -0
  65. package/dist/commands/view.js +184 -0
  66. package/dist/commands/view.js.map +1 -0
  67. package/dist/commands/watch.d.ts +6 -0
  68. package/dist/commands/watch.js +61 -0
  69. package/dist/commands/watch.js.map +1 -0
  70. package/dist/commands/wrap.d.ts +2 -0
  71. package/dist/commands/wrap.js +131 -0
  72. package/dist/commands/wrap.js.map +1 -0
  73. package/dist/conv/chatgpt-import.d.ts +33 -0
  74. package/dist/conv/chatgpt-import.js +145 -0
  75. package/dist/conv/chatgpt-import.js.map +1 -0
  76. package/dist/conv/cursor.d.ts +12 -0
  77. package/dist/conv/cursor.js +55 -0
  78. package/dist/conv/cursor.js.map +1 -0
  79. package/dist/conv/export.d.ts +40 -0
  80. package/dist/conv/export.js +215 -0
  81. package/dist/conv/export.js.map +1 -0
  82. package/dist/conv/replay/aider.d.ts +24 -0
  83. package/dist/conv/replay/aider.js +56 -0
  84. package/dist/conv/replay/aider.js.map +1 -0
  85. package/dist/conv/replay/claude-code.d.ts +38 -0
  86. package/dist/conv/replay/claude-code.js +80 -0
  87. package/dist/conv/replay/claude-code.js.map +1 -0
  88. package/dist/conv/replay/context.d.ts +25 -0
  89. package/dist/conv/replay/context.js +63 -0
  90. package/dist/conv/replay/context.js.map +1 -0
  91. package/dist/conv/replay/index.d.ts +22 -0
  92. package/dist/conv/replay/index.js +84 -0
  93. package/dist/conv/replay/index.js.map +1 -0
  94. package/dist/conv/search.d.ts +67 -0
  95. package/dist/conv/search.js +379 -0
  96. package/dist/conv/search.js.map +1 -0
  97. package/dist/conv/sink.d.ts +26 -0
  98. package/dist/conv/sink.js +76 -0
  99. package/dist/conv/sink.js.map +1 -0
  100. package/dist/conv/sources/aider.d.ts +13 -0
  101. package/dist/conv/sources/aider.js +95 -0
  102. package/dist/conv/sources/aider.js.map +1 -0
  103. package/dist/conv/sources/claude-code.d.ts +13 -0
  104. package/dist/conv/sources/claude-code.js +189 -0
  105. package/dist/conv/sources/claude-code.js.map +1 -0
  106. package/dist/conv/sources/codex.d.ts +15 -0
  107. package/dist/conv/sources/codex.js +175 -0
  108. package/dist/conv/sources/codex.js.map +1 -0
  109. package/dist/conv/sources/opencode.d.ts +12 -0
  110. package/dist/conv/sources/opencode.js +92 -0
  111. package/dist/conv/sources/opencode.js.map +1 -0
  112. package/dist/conv/store.d.ts +10 -0
  113. package/dist/conv/store.js +75 -0
  114. package/dist/conv/store.js.map +1 -0
  115. package/dist/conv/title.d.ts +9 -0
  116. package/dist/conv/title.js +74 -0
  117. package/dist/conv/title.js.map +1 -0
  118. package/dist/conv/watcher.d.ts +28 -0
  119. package/dist/conv/watcher.js +75 -0
  120. package/dist/conv/watcher.js.map +1 -0
  121. package/dist/conv/zip.d.ts +11 -0
  122. package/dist/conv/zip.js +82 -0
  123. package/dist/conv/zip.js.map +1 -0
  124. package/dist/hf/client.d.ts +41 -0
  125. package/dist/hf/client.js +87 -0
  126. package/dist/hf/client.js.map +1 -0
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.js +6 -0
  129. package/dist/index.js.map +1 -0
  130. package/dist/ir/index.d.ts +24 -0
  131. package/dist/ir/index.js +23 -0
  132. package/dist/ir/index.js.map +1 -0
  133. package/dist/ir/profile.d.ts +55 -0
  134. package/dist/ir/profile.js +177 -0
  135. package/dist/ir/profile.js.map +1 -0
  136. package/dist/memory/index.d.ts +41 -0
  137. package/dist/memory/index.js +109 -0
  138. package/dist/memory/index.js.map +1 -0
  139. package/dist/memory/search.d.ts +24 -0
  140. package/dist/memory/search.js +114 -0
  141. package/dist/memory/search.js.map +1 -0
  142. package/dist/memory/sinks/claude-mem.d.ts +13 -0
  143. package/dist/memory/sinks/claude-mem.js +100 -0
  144. package/dist/memory/sinks/claude-mem.js.map +1 -0
  145. package/dist/memory/sinks/codex.d.ts +12 -0
  146. package/dist/memory/sinks/codex.js +82 -0
  147. package/dist/memory/sinks/codex.js.map +1 -0
  148. package/dist/memory/sinks/context.d.ts +21 -0
  149. package/dist/memory/sinks/context.js +42 -0
  150. package/dist/memory/sinks/context.js.map +1 -0
  151. package/dist/memory/sources/chatgpt.d.ts +22 -0
  152. package/dist/memory/sources/chatgpt.js +98 -0
  153. package/dist/memory/sources/chatgpt.js.map +1 -0
  154. package/dist/memory/sources/claude-mem.d.ts +8 -0
  155. package/dist/memory/sources/claude-mem.js +104 -0
  156. package/dist/memory/sources/claude-mem.js.map +1 -0
  157. package/dist/memory/sources/codex.d.ts +8 -0
  158. package/dist/memory/sources/codex.js +77 -0
  159. package/dist/memory/sources/codex.js.map +1 -0
  160. package/dist/memory/store.d.ts +19 -0
  161. package/dist/memory/store.js +82 -0
  162. package/dist/memory/store.js.map +1 -0
  163. package/dist/proxy/http.d.ts +21 -0
  164. package/dist/proxy/http.js +205 -0
  165. package/dist/proxy/http.js.map +1 -0
  166. package/dist/proxy/recorder.d.ts +35 -0
  167. package/dist/proxy/recorder.js +221 -0
  168. package/dist/proxy/recorder.js.map +1 -0
  169. package/dist/proxy/streaming.d.ts +33 -0
  170. package/dist/proxy/streaming.js +185 -0
  171. package/dist/proxy/streaming.js.map +1 -0
  172. package/dist/redactor/entropy.d.ts +29 -0
  173. package/dist/redactor/entropy.js +98 -0
  174. package/dist/redactor/entropy.js.map +1 -0
  175. package/dist/redactor/index.d.ts +52 -0
  176. package/dist/redactor/index.js +152 -0
  177. package/dist/redactor/index.js.map +1 -0
  178. package/dist/redactor/ner.d.ts +53 -0
  179. package/dist/redactor/ner.js +97 -0
  180. package/dist/redactor/ner.js.map +1 -0
  181. package/dist/redactor/pii-patterns.d.ts +22 -0
  182. package/dist/redactor/pii-patterns.js +187 -0
  183. package/dist/redactor/pii-patterns.js.map +1 -0
  184. package/dist/redactor/secret-patterns.d.ts +27 -0
  185. package/dist/redactor/secret-patterns.js +475 -0
  186. package/dist/redactor/secret-patterns.js.map +1 -0
  187. package/dist/schema/conv.d.ts +698 -0
  188. package/dist/schema/conv.js +85 -0
  189. package/dist/schema/conv.js.map +1 -0
  190. package/dist/schema/index.d.ts +2 -0
  191. package/dist/schema/index.js +3 -0
  192. package/dist/schema/index.js.map +1 -0
  193. package/dist/schema/manifest.d.ts +1531 -0
  194. package/dist/schema/manifest.js +179 -0
  195. package/dist/schema/manifest.js.map +1 -0
  196. package/dist/schema/memory.d.ts +107 -0
  197. package/dist/schema/memory.js +45 -0
  198. package/dist/schema/memory.js.map +1 -0
  199. package/dist/schema/trace.d.ts +164 -0
  200. package/dist/schema/trace.js +89 -0
  201. package/dist/schema/trace.js.map +1 -0
  202. package/dist/trace/consent.d.ts +30 -0
  203. package/dist/trace/consent.js +60 -0
  204. package/dist/trace/consent.js.map +1 -0
  205. package/dist/trace/extract.d.ts +22 -0
  206. package/dist/trace/extract.js +168 -0
  207. package/dist/trace/extract.js.map +1 -0
  208. package/dist/trace/ml-pii.d.ts +33 -0
  209. package/dist/trace/ml-pii.js +35 -0
  210. package/dist/trace/ml-pii.js.map +1 -0
  211. package/dist/trace/push.d.ts +59 -0
  212. package/dist/trace/push.js +141 -0
  213. package/dist/trace/push.js.map +1 -0
  214. package/dist/trace/serialize.d.ts +23 -0
  215. package/dist/trace/serialize.js +67 -0
  216. package/dist/trace/serialize.js.map +1 -0
  217. package/dist/ui/banner.d.ts +2 -0
  218. package/dist/ui/banner.js +17 -0
  219. package/dist/ui/banner.js.map +1 -0
  220. package/dist/ui/box.d.ts +4 -0
  221. package/dist/ui/box.js +38 -0
  222. package/dist/ui/box.js.map +1 -0
  223. package/dist/ui/index.d.ts +5 -0
  224. package/dist/ui/index.js +5 -0
  225. package/dist/ui/index.js.map +1 -0
  226. package/dist/ui/spinner.d.ts +10 -0
  227. package/dist/ui/spinner.js +44 -0
  228. package/dist/ui/spinner.js.map +1 -0
  229. package/dist/ui/table.d.ts +9 -0
  230. package/dist/ui/table.js +55 -0
  231. package/dist/ui/table.js.map +1 -0
  232. package/dist/util/frontmatter.d.ts +6 -0
  233. package/dist/util/frontmatter.js +20 -0
  234. package/dist/util/frontmatter.js.map +1 -0
  235. package/dist/util/fs.d.ts +5 -0
  236. package/dist/util/fs.js +41 -0
  237. package/dist/util/fs.js.map +1 -0
  238. package/dist/util/ids.d.ts +3 -0
  239. package/dist/util/ids.js +16 -0
  240. package/dist/util/ids.js.map +1 -0
  241. package/dist/util/log.d.ts +13 -0
  242. package/dist/util/log.js +33 -0
  243. package/dist/util/log.js.map +1 -0
  244. package/dist/util/paths.d.ts +18 -0
  245. package/dist/util/paths.js +36 -0
  246. package/dist/util/paths.js.map +1 -0
  247. package/dist/version.d.ts +5 -0
  248. package/dist/version.js +32 -0
  249. package/dist/version.js.map +1 -0
  250. package/package.json +86 -0
@@ -0,0 +1,82 @@
1
+ import { createReadStream, promises as fs } from 'node:fs';
2
+ import { createInterface } from 'node:readline';
3
+ import { dirname, join } from 'node:path';
4
+ import { ensureDir } from '../util/fs.js';
5
+ import { redactText } from '../redactor/index.js';
6
+ import { getToolPaths } from '../util/paths.js';
7
+ import { MemorySchema } from '../schema/memory.js';
8
+ /**
9
+ * Append-only JSONL store for `skein-memory/1` events.
10
+ *
11
+ * Each line is one Memory event. Updates emit a new line with the same
12
+ * `id` and a fresher `updatedAt`. Consumers (search, list, import) keep
13
+ * the latest version per id during read.
14
+ *
15
+ * Every captured line passes through the synchronous redactor before
16
+ * disk write — same guarantees as the conversation sink.
17
+ */
18
+ const SUBDIR = 'memory';
19
+ export function memoryLogPath() {
20
+ const paths = getToolPaths();
21
+ return join(paths.skeinDir, SUBDIR, 'memories.jsonl');
22
+ }
23
+ export async function appendMemories(memories) {
24
+ const path = memoryLogPath();
25
+ await ensureDir(dirname(path));
26
+ let bytes = 0;
27
+ const lines = [];
28
+ for (const memory of memories) {
29
+ const validated = MemorySchema.parse(memory);
30
+ const raw = `${JSON.stringify(validated)}\n`;
31
+ const redacted = redactText(raw).text;
32
+ lines.push(redacted);
33
+ bytes += Buffer.byteLength(redacted, 'utf8');
34
+ }
35
+ if (lines.length === 0)
36
+ return { appended: 0, bytes: 0 };
37
+ await fs.appendFile(path, lines.join(''), 'utf8');
38
+ return { appended: lines.length, bytes };
39
+ }
40
+ /**
41
+ * Read the log and return the latest version of every Memory, keyed by id.
42
+ *
43
+ * Order is "as-seen" — the underlying file is append-only, so the order
44
+ * matches actual capture/import history. Callers re-sort as needed.
45
+ */
46
+ export async function readMemories() {
47
+ const path = memoryLogPath();
48
+ try {
49
+ await fs.access(path);
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ const latest = new Map();
55
+ const stream = createReadStream(path, { encoding: 'utf8' });
56
+ const rl = createInterface({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
57
+ for await (const line of rl) {
58
+ if (!line.trim())
59
+ continue;
60
+ let parsed;
61
+ try {
62
+ parsed = MemorySchema.parse(JSON.parse(line));
63
+ }
64
+ catch {
65
+ continue;
66
+ }
67
+ const existing = latest.get(parsed.id);
68
+ if (!existing || parsed.updatedAt >= existing.updatedAt) {
69
+ latest.set(parsed.id, parsed);
70
+ }
71
+ }
72
+ return [...latest.values()];
73
+ }
74
+ /**
75
+ * Check whether a Memory with the given id is already stored. Used by
76
+ * capture readers to avoid emitting JSONL noise for unchanged rows.
77
+ */
78
+ export async function hasMemory(id) {
79
+ const all = await readMemories();
80
+ return all.some(m => m.id === id);
81
+ }
82
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { ConvEvent } from '../schema/conv.js';
2
+ export interface ProxyOptions {
3
+ port?: number;
4
+ sessionId: string;
5
+ tool: string;
6
+ toolVersion?: string;
7
+ project?: string;
8
+ onEvent: (event: ConvEvent) => Promise<void> | void;
9
+ /** Per-turn harness metadata observed from live traffic (provider/model/effort). */
10
+ onTurnMeta?: (meta: {
11
+ provider: string;
12
+ model?: string;
13
+ effort?: string;
14
+ }) => void;
15
+ }
16
+ export interface RunningProxy {
17
+ url: string;
18
+ port: number;
19
+ close(): Promise<void>;
20
+ }
21
+ export declare function startProxy(opts: ProxyOptions): Promise<RunningProxy>;
@@ -0,0 +1,205 @@
1
+ import { createServer } from 'node:http';
2
+ import { request as httpsRequest } from 'node:https';
3
+ import { Buffer } from 'node:buffer';
4
+ import { redactText } from '../redactor/index.js';
5
+ import { detectProvider, parseRequest, parseResponse, turnToEvents, upstreamHost, } from './recorder.js';
6
+ import { StreamingAggregator } from './streaming.js';
7
+ const HOP_BY_HOP_HEADERS = new Set([
8
+ 'connection',
9
+ 'keep-alive',
10
+ 'proxy-authenticate',
11
+ 'proxy-authorization',
12
+ 'te',
13
+ 'trailers',
14
+ 'transfer-encoding',
15
+ 'upgrade',
16
+ ]);
17
+ let seqCounter = 0;
18
+ export async function startProxy(opts) {
19
+ const server = createServer((req, res) => {
20
+ handle(req, res, opts).catch(err => {
21
+ // Surface upstream failures to the client as a 502; never crash the proxy.
22
+ if (!res.headersSent)
23
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
24
+ res.end(`skein proxy error: ${err.message}`);
25
+ });
26
+ });
27
+ await new Promise(resolve => server.listen(opts.port ?? 0, '127.0.0.1', resolve));
28
+ const addr = server.address();
29
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
30
+ return {
31
+ url: `http://127.0.0.1:${port}`,
32
+ port,
33
+ async close() {
34
+ await closeServer(server);
35
+ },
36
+ };
37
+ }
38
+ async function handle(req, res, opts) {
39
+ const url = req.url ?? '/';
40
+ const provider = detectProvider(url);
41
+ if (!provider) {
42
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
43
+ res.end(`skein proxy: unknown path ${url}`);
44
+ return;
45
+ }
46
+ const reqBody = await readBody(req);
47
+ const reqEffort = extractEffort(reqBody);
48
+ const acceptStream = (req.headers['accept'] ?? '').toString().includes('text/event-stream');
49
+ const upstream = upstreamHost(provider);
50
+ const forwardHeaders = filterHeaders(req.headers, upstream);
51
+ const upstreamReq = httpsRequest({
52
+ method: req.method ?? 'POST',
53
+ host: upstream,
54
+ port: 443,
55
+ path: url,
56
+ headers: forwardHeaders,
57
+ });
58
+ upstreamReq.on('error', err => {
59
+ if (!res.headersSent)
60
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
61
+ res.end(`skein proxy upstream error: ${err.message}`);
62
+ });
63
+ const responsePromise = new Promise((resolve, reject) => {
64
+ upstreamReq.on('response', upstreamRes => {
65
+ const status = upstreamRes.statusCode ?? 200;
66
+ res.writeHead(status, filterResponseHeaders(upstreamRes.headers));
67
+ const contentType = String(upstreamRes.headers['content-type'] ?? '');
68
+ const isStream = acceptStream ||
69
+ contentType.includes('text/event-stream') ||
70
+ contentType.includes('application/x-ndjson');
71
+ const aggregator = isStream ? new StreamingAggregator(provider) : undefined;
72
+ const chunks = [];
73
+ upstreamRes.on('data', (chunk) => {
74
+ if (aggregator)
75
+ aggregator.feed(chunk);
76
+ else
77
+ chunks.push(chunk);
78
+ res.write(chunk);
79
+ });
80
+ upstreamRes.on('end', () => {
81
+ res.end();
82
+ if (aggregator)
83
+ aggregator.finish();
84
+ resolve({
85
+ body: aggregator ? '' : Buffer.concat(chunks).toString('utf8'),
86
+ status,
87
+ headers: upstreamRes.headers,
88
+ aggregator,
89
+ });
90
+ });
91
+ upstreamRes.on('error', reject);
92
+ });
93
+ });
94
+ const requestStart = Date.now();
95
+ upstreamReq.write(reqBody);
96
+ upstreamReq.end();
97
+ let resolved;
98
+ try {
99
+ resolved = await responsePromise;
100
+ }
101
+ catch (err) {
102
+ if (!res.headersSent)
103
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
104
+ res.end(`skein proxy stream error: ${err.message}`);
105
+ return;
106
+ }
107
+ if (resolved.status >= 400)
108
+ return;
109
+ const latencyMs = Date.now() - requestStart;
110
+ const ts = new Date().toISOString();
111
+ const requestParsed = parseRequest(provider, redactText(reqBody.toString('utf8')).text);
112
+ if (!requestParsed)
113
+ return;
114
+ const assistant = resolved.aggregator
115
+ ? resolved.aggregator.result()
116
+ : parseResponse(provider, redactText(resolved.body).text);
117
+ if (assistant) {
118
+ requestParsed.assistant = {
119
+ content: assistant.content,
120
+ ...(typeof assistant.tokensIn === 'number' ? { tokensIn: assistant.tokensIn } : {}),
121
+ ...(typeof assistant.tokensOut === 'number' ? { tokensOut: assistant.tokensOut } : {}),
122
+ };
123
+ if (assistant.model && !requestParsed.model)
124
+ requestParsed.model = assistant.model;
125
+ }
126
+ // Real time-to-answer for this turn (request sent → full response received).
127
+ requestParsed.assistant.latencyMs = latencyMs;
128
+ opts.onTurnMeta?.({
129
+ provider,
130
+ ...(requestParsed.model ? { model: requestParsed.model } : {}),
131
+ ...(reqEffort ? { effort: reqEffort } : {}),
132
+ });
133
+ const events = turnToEvents(requestParsed, opts.sessionId, nextSeqStart(), ts);
134
+ for (const event of events)
135
+ await opts.onEvent(event);
136
+ }
137
+ function readBody(req) {
138
+ return new Promise((resolve, reject) => {
139
+ const chunks = [];
140
+ req.on('data', chunk => chunks.push(chunk));
141
+ req.on('end', () => resolve(Buffer.concat(chunks)));
142
+ req.on('error', reject);
143
+ });
144
+ }
145
+ function filterHeaders(headers, upstreamHostName) {
146
+ const result = {};
147
+ for (const [name, value] of Object.entries(headers)) {
148
+ if (!value)
149
+ continue;
150
+ if (HOP_BY_HOP_HEADERS.has(name.toLowerCase()))
151
+ continue;
152
+ result[name] = value;
153
+ }
154
+ result['host'] = upstreamHostName;
155
+ return result;
156
+ }
157
+ function filterResponseHeaders(headers) {
158
+ const out = {};
159
+ for (const [name, value] of Object.entries(headers)) {
160
+ if (!value)
161
+ continue;
162
+ if (HOP_BY_HOP_HEADERS.has(name.toLowerCase()))
163
+ continue;
164
+ out[name] = value;
165
+ }
166
+ return out;
167
+ }
168
+ function nextSeqStart() {
169
+ seqCounter += 10;
170
+ return seqCounter;
171
+ }
172
+ /**
173
+ * Best-effort, content-free reasoning-effort detection from a request body.
174
+ * Reads only known structural keys; never inspects prompt content.
175
+ */
176
+ function extractEffort(body) {
177
+ try {
178
+ const obj = JSON.parse(body.toString('utf8'));
179
+ const direct = obj['reasoning_effort'] ?? obj['reasoningEffort'];
180
+ if (typeof direct === 'string')
181
+ return direct;
182
+ const reasoning = obj['reasoning'];
183
+ if (reasoning && typeof reasoning === 'object') {
184
+ const effort = reasoning['effort'];
185
+ if (typeof effort === 'string')
186
+ return effort;
187
+ }
188
+ const thinking = obj['thinking'];
189
+ if (thinking && typeof thinking === 'object') {
190
+ const t = thinking;
191
+ if (t['type'] === 'enabled' || typeof t['budget_tokens'] === 'number')
192
+ return 'high';
193
+ }
194
+ return undefined;
195
+ }
196
+ catch {
197
+ return undefined;
198
+ }
199
+ }
200
+ function closeServer(server) {
201
+ return new Promise((resolve, reject) => {
202
+ server.close(err => (err ? reject(err) : resolve()));
203
+ });
204
+ }
205
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/proxy/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ContentBlock, ConvEvent } from '../schema/conv.js';
2
+ import type { Provider } from './streaming.js';
3
+ export interface RecordedTurn {
4
+ provider: Provider;
5
+ model?: string;
6
+ userMessages: {
7
+ role: 'user' | 'system';
8
+ content: ContentBlock[];
9
+ }[];
10
+ assistant: {
11
+ content: ContentBlock[];
12
+ tokensIn?: number;
13
+ tokensOut?: number;
14
+ /** Real time-to-answer in ms, measured by the proxy. */
15
+ latencyMs?: number;
16
+ costUsd?: number;
17
+ };
18
+ }
19
+ export declare function detectProvider(path: string): Provider | undefined;
20
+ export declare function upstreamHost(provider: Provider): string;
21
+ /**
22
+ * Parse the upstream request body into the user-side messages of a turn,
23
+ * normalized into skein-conv ContentBlocks.
24
+ */
25
+ export declare function parseRequest(provider: Provider, body: string): RecordedTurn | undefined;
26
+ /**
27
+ * Parse a non-streaming upstream response body into the assistant turn.
28
+ */
29
+ export declare function parseResponse(provider: Provider, body: string): {
30
+ content: ContentBlock[];
31
+ tokensIn?: number;
32
+ tokensOut?: number;
33
+ model?: string;
34
+ } | undefined;
35
+ export declare function turnToEvents(turn: RecordedTurn, sessionId: string, seqStart: number, ts: string): ConvEvent[];
@@ -0,0 +1,221 @@
1
+ const ANTHROPIC_PATHS = ['/v1/messages'];
2
+ const OPENAI_CHAT_PATHS = ['/v1/chat/completions'];
3
+ const OPENAI_RESPONSES_PATHS = ['/v1/responses'];
4
+ export function detectProvider(path) {
5
+ if (ANTHROPIC_PATHS.some(p => path.startsWith(p)))
6
+ return 'anthropic';
7
+ if (OPENAI_CHAT_PATHS.some(p => path.startsWith(p)))
8
+ return 'openai-chat';
9
+ if (OPENAI_RESPONSES_PATHS.some(p => path.startsWith(p)))
10
+ return 'openai-responses';
11
+ return undefined;
12
+ }
13
+ export function upstreamHost(provider) {
14
+ if (provider === 'anthropic')
15
+ return 'api.anthropic.com';
16
+ return 'api.openai.com';
17
+ }
18
+ /**
19
+ * Parse the upstream request body into the user-side messages of a turn,
20
+ * normalized into skein-conv ContentBlocks.
21
+ */
22
+ export function parseRequest(provider, body) {
23
+ const parsed = safeJson(body);
24
+ if (!parsed)
25
+ return undefined;
26
+ const model = typeof parsed['model'] === 'string' ? parsed['model'] : undefined;
27
+ if (provider === 'anthropic')
28
+ return parseAnthropicRequest(parsed, model);
29
+ return parseOpenAiRequest(parsed, model, provider);
30
+ }
31
+ function parseAnthropicRequest(body, model) {
32
+ const userMessages = [];
33
+ const system = body['system'];
34
+ if (typeof system === 'string' && system.length > 0) {
35
+ userMessages.push({ role: 'system', content: [{ type: 'text', text: system }] });
36
+ }
37
+ else if (Array.isArray(system)) {
38
+ const text = system
39
+ .filter(b => b['type'] === 'text')
40
+ .map(b => String(b['text'] ?? ''))
41
+ .join('\n');
42
+ if (text)
43
+ userMessages.push({ role: 'system', content: [{ type: 'text', text }] });
44
+ }
45
+ const messages = body['messages'] ?? [];
46
+ for (const msg of messages) {
47
+ const role = msg['role'] === 'assistant' ? 'user' : 'user';
48
+ const content = normalizeContent(msg['content']);
49
+ userMessages.push({ role, content });
50
+ }
51
+ return {
52
+ provider: 'anthropic',
53
+ ...(model ? { model } : {}),
54
+ userMessages,
55
+ assistant: { content: [] },
56
+ };
57
+ }
58
+ function parseOpenAiRequest(body, model, provider) {
59
+ const userMessages = [];
60
+ const messages = body['messages'] ?? [];
61
+ for (const msg of messages) {
62
+ const role = msg['role'] ?? 'user';
63
+ const content = normalizeContent(msg['content']);
64
+ if (role === 'system' || role === 'user') {
65
+ userMessages.push({ role, content });
66
+ }
67
+ }
68
+ return {
69
+ provider,
70
+ ...(model ? { model } : {}),
71
+ userMessages,
72
+ assistant: { content: [] },
73
+ };
74
+ }
75
+ /**
76
+ * Parse a non-streaming upstream response body into the assistant turn.
77
+ */
78
+ export function parseResponse(provider, body) {
79
+ const parsed = safeJson(body);
80
+ if (!parsed)
81
+ return undefined;
82
+ if (provider === 'anthropic') {
83
+ const content = normalizeContent(parsed['content']);
84
+ const usage = parsed['usage'];
85
+ const model = typeof parsed['model'] === 'string' ? parsed['model'] : undefined;
86
+ return {
87
+ content,
88
+ ...(typeof usage?.['input_tokens'] === 'number'
89
+ ? { tokensIn: usage['input_tokens'] }
90
+ : {}),
91
+ ...(typeof usage?.['output_tokens'] === 'number'
92
+ ? { tokensOut: usage['output_tokens'] }
93
+ : {}),
94
+ ...(model ? { model } : {}),
95
+ };
96
+ }
97
+ const choices = parsed['choices'];
98
+ if (Array.isArray(choices) && choices.length > 0) {
99
+ const message = choices[0]?.['message'];
100
+ const content = normalizeContent(message?.['content']);
101
+ const usage = parsed['usage'];
102
+ const model = typeof parsed['model'] === 'string' ? parsed['model'] : undefined;
103
+ return {
104
+ content,
105
+ ...(typeof usage?.['prompt_tokens'] === 'number'
106
+ ? { tokensIn: usage['prompt_tokens'] }
107
+ : {}),
108
+ ...(typeof usage?.['completion_tokens'] === 'number'
109
+ ? { tokensOut: usage['completion_tokens'] }
110
+ : {}),
111
+ ...(model ? { model } : {}),
112
+ };
113
+ }
114
+ const output = parsed['output'];
115
+ if (Array.isArray(output)) {
116
+ const text = output
117
+ .filter(o => o['type'] === 'message')
118
+ .flatMap(o => o['content'] ?? [])
119
+ .filter(c => c['type'] === 'output_text')
120
+ .map(c => String(c['text'] ?? ''))
121
+ .join('');
122
+ const usage = parsed['usage'];
123
+ const model = typeof parsed['model'] === 'string' ? parsed['model'] : undefined;
124
+ return {
125
+ content: [{ type: 'text', text }],
126
+ ...(typeof usage?.['input_tokens'] === 'number'
127
+ ? { tokensIn: usage['input_tokens'] }
128
+ : {}),
129
+ ...(typeof usage?.['output_tokens'] === 'number'
130
+ ? { tokensOut: usage['output_tokens'] }
131
+ : {}),
132
+ ...(model ? { model } : {}),
133
+ };
134
+ }
135
+ return undefined;
136
+ }
137
+ export function turnToEvents(turn, sessionId, seqStart, ts) {
138
+ const events = [];
139
+ let seq = seqStart;
140
+ for (const msg of turn.userMessages) {
141
+ events.push({
142
+ type: 'message',
143
+ session: sessionId,
144
+ seq: seq++,
145
+ role: msg.role,
146
+ content: msg.content,
147
+ ts,
148
+ });
149
+ }
150
+ const assistantContent = turn.assistant.content;
151
+ if (assistantContent.length > 0) {
152
+ const tokensIn = turn.assistant.tokensIn;
153
+ const tokensOut = turn.assistant.tokensOut;
154
+ const { latencyMs, costUsd } = turn.assistant;
155
+ events.push({
156
+ type: 'message',
157
+ session: sessionId,
158
+ seq: seq++,
159
+ role: 'assistant',
160
+ content: assistantContent,
161
+ ...(typeof tokensIn === 'number' && typeof tokensOut === 'number'
162
+ ? { tokens: { in: tokensIn, out: tokensOut } }
163
+ : {}),
164
+ ...(typeof latencyMs === 'number' ? { latencyMs } : {}),
165
+ ...(typeof costUsd === 'number' ? { costUsd } : {}),
166
+ ts,
167
+ });
168
+ }
169
+ return events;
170
+ }
171
+ function normalizeContent(raw) {
172
+ if (typeof raw === 'string')
173
+ return [{ type: 'text', text: raw }];
174
+ if (!Array.isArray(raw))
175
+ return [];
176
+ const out = [];
177
+ for (const item of raw) {
178
+ if (!item || typeof item !== 'object')
179
+ continue;
180
+ const type = item['type'];
181
+ if (type === 'text' && typeof item['text'] === 'string') {
182
+ out.push({ type: 'text', text: item['text'] });
183
+ }
184
+ else if (type === 'tool_use') {
185
+ out.push({
186
+ type: 'tool_use',
187
+ id: String(item['id'] ?? ''),
188
+ name: String(item['name'] ?? ''),
189
+ input: item['input'] ?? {},
190
+ });
191
+ }
192
+ else if (type === 'tool_result') {
193
+ out.push({
194
+ type: 'tool_result',
195
+ tool_use_id: String(item['tool_use_id'] ?? ''),
196
+ content: typeof item['content'] === 'string'
197
+ ? item['content']
198
+ : Array.isArray(item['content'])
199
+ ? item['content']
200
+ : '',
201
+ ...(typeof item['is_error'] === 'boolean' ? { is_error: item['is_error'] } : {}),
202
+ });
203
+ }
204
+ else if (type === 'input_text' && typeof item['text'] === 'string') {
205
+ out.push({ type: 'text', text: item['text'] });
206
+ }
207
+ else if (type === 'output_text' && typeof item['text'] === 'string') {
208
+ out.push({ type: 'text', text: item['text'] });
209
+ }
210
+ }
211
+ return out;
212
+ }
213
+ function safeJson(raw) {
214
+ try {
215
+ return JSON.parse(raw);
216
+ }
217
+ catch {
218
+ return undefined;
219
+ }
220
+ }
221
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/proxy/recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { ContentBlock } from '../schema/conv.js';
2
+ export type Provider = 'anthropic' | 'openai-chat' | 'openai-responses';
3
+ /**
4
+ * Aggregator that consumes SSE chunks from an upstream LLM provider and
5
+ * builds a single normalized assistant message with concatenated text and
6
+ * resolved tool_use blocks.
7
+ *
8
+ * Each provider has its own delta shape; the aggregator dispatches on
9
+ * provider at construction time.
10
+ */
11
+ export declare class StreamingAggregator {
12
+ private readonly provider;
13
+ private buffer;
14
+ private text;
15
+ private toolUses;
16
+ private inputTokens;
17
+ private outputTokens;
18
+ private model?;
19
+ private orderedIndices;
20
+ constructor(provider: Provider);
21
+ feed(chunk: Buffer): void;
22
+ /** Call after the upstream stream has ended to flush any remaining buffered line. */
23
+ finish(): void;
24
+ result(): {
25
+ content: ContentBlock[];
26
+ tokensIn?: number;
27
+ tokensOut?: number;
28
+ model?: string;
29
+ };
30
+ private handleLine;
31
+ private handleAnthropic;
32
+ private handleOpenAi;
33
+ }