@antseed/cli 0.1.141 → 0.1.143

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 (85) hide show
  1. package/dist/cli/commands/buyer/sweep.d.ts.map +1 -1
  2. package/dist/cli/commands/buyer/sweep.js +11 -5
  3. package/dist/cli/commands/buyer/sweep.js.map +1 -1
  4. package/dist/cli/commands/payments.d.ts.map +1 -1
  5. package/dist/cli/commands/payments.js +37 -20
  6. package/dist/cli/commands/payments.js.map +1 -1
  7. package/dist/cli/commands/system-proxy/ca-trust-state.d.ts +8 -0
  8. package/dist/cli/commands/system-proxy/ca-trust-state.d.ts.map +1 -0
  9. package/dist/cli/commands/system-proxy/ca-trust-state.js +20 -0
  10. package/dist/cli/commands/system-proxy/ca-trust-state.js.map +1 -0
  11. package/dist/cli/commands/system-proxy/index.d.ts.map +1 -1
  12. package/dist/cli/commands/system-proxy/index.js +2 -0
  13. package/dist/cli/commands/system-proxy/index.js.map +1 -1
  14. package/dist/cli/commands/system-proxy/install-ca.d.ts.map +1 -1
  15. package/dist/cli/commands/system-proxy/install-ca.js +9 -0
  16. package/dist/cli/commands/system-proxy/install-ca.js.map +1 -1
  17. package/dist/cli/commands/system-proxy/start.d.ts.map +1 -1
  18. package/dist/cli/commands/system-proxy/start.js +3 -1
  19. package/dist/cli/commands/system-proxy/start.js.map +1 -1
  20. package/dist/cli/commands/wrapped-tools.d.ts.map +1 -1
  21. package/dist/cli/commands/wrapped-tools.js +7 -0
  22. package/dist/cli/commands/wrapped-tools.js.map +1 -1
  23. package/dist/cli/commands/wrapped-tools.test.js +6 -1
  24. package/dist/cli/commands/wrapped-tools.test.js.map +1 -1
  25. package/dist/proxy/buyer-proxy.d.ts +38 -1
  26. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  27. package/dist/proxy/buyer-proxy.js +340 -23
  28. package/dist/proxy/buyer-proxy.js.map +1 -1
  29. package/dist/proxy/buyer-proxy.test.js +382 -11
  30. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  31. package/dist/proxy/conversation-identity.d.ts +79 -0
  32. package/dist/proxy/conversation-identity.d.ts.map +1 -0
  33. package/dist/proxy/conversation-identity.js +302 -0
  34. package/dist/proxy/conversation-identity.js.map +1 -0
  35. package/dist/proxy/conversation-identity.test.d.ts +2 -0
  36. package/dist/proxy/conversation-identity.test.d.ts.map +1 -0
  37. package/dist/proxy/conversation-identity.test.js +236 -0
  38. package/dist/proxy/conversation-identity.test.js.map +1 -0
  39. package/dist/proxy/conversation-store.d.ts +96 -0
  40. package/dist/proxy/conversation-store.d.ts.map +1 -0
  41. package/dist/proxy/conversation-store.js +242 -0
  42. package/dist/proxy/conversation-store.js.map +1 -0
  43. package/dist/proxy/conversation-store.test.d.ts +2 -0
  44. package/dist/proxy/conversation-store.test.d.ts.map +1 -0
  45. package/dist/proxy/conversation-store.test.js +277 -0
  46. package/dist/proxy/conversation-store.test.js.map +1 -0
  47. package/dist/proxy/count-tokens.d.ts +20 -0
  48. package/dist/proxy/count-tokens.d.ts.map +1 -0
  49. package/dist/proxy/count-tokens.js +75 -0
  50. package/dist/proxy/count-tokens.js.map +1 -0
  51. package/dist/proxy/count-tokens.test.d.ts +2 -0
  52. package/dist/proxy/count-tokens.test.d.ts.map +1 -0
  53. package/dist/proxy/count-tokens.test.js +56 -0
  54. package/dist/proxy/count-tokens.test.js.map +1 -0
  55. package/dist/proxy/request-utils.d.ts +38 -0
  56. package/dist/proxy/request-utils.d.ts.map +1 -1
  57. package/dist/proxy/request-utils.js +84 -8
  58. package/dist/proxy/request-utils.js.map +1 -1
  59. package/dist/proxy/telemetry.d.ts +6 -0
  60. package/dist/proxy/telemetry.d.ts.map +1 -1
  61. package/dist/proxy/telemetry.js +53 -13
  62. package/dist/proxy/telemetry.js.map +1 -1
  63. package/dist/system-proxy/ca-manager.d.ts +67 -0
  64. package/dist/system-proxy/ca-manager.d.ts.map +1 -1
  65. package/dist/system-proxy/ca-manager.js +258 -39
  66. package/dist/system-proxy/ca-manager.js.map +1 -1
  67. package/dist/system-proxy/ca-manager.test.js +50 -1
  68. package/dist/system-proxy/ca-manager.test.js.map +1 -1
  69. package/dist/system-proxy/profiles.d.ts +2 -1
  70. package/dist/system-proxy/profiles.d.ts.map +1 -1
  71. package/dist/system-proxy/profiles.js +55 -4
  72. package/dist/system-proxy/profiles.js.map +1 -1
  73. package/dist/system-proxy/proxy-server.d.ts +18 -1
  74. package/dist/system-proxy/proxy-server.d.ts.map +1 -1
  75. package/dist/system-proxy/proxy-server.js +91 -38
  76. package/dist/system-proxy/proxy-server.js.map +1 -1
  77. package/dist/system-proxy/proxy-server.test.js +142 -13
  78. package/dist/system-proxy/proxy-server.test.js.map +1 -1
  79. package/dist/system-proxy/system-proxy.d.ts +1 -0
  80. package/dist/system-proxy/system-proxy.d.ts.map +1 -1
  81. package/dist/system-proxy/system-proxy.js +57 -15
  82. package/dist/system-proxy/system-proxy.js.map +1 -1
  83. package/dist/system-proxy/types.d.ts +17 -4
  84. package/dist/system-proxy/types.d.ts.map +1 -1
  85. package/package.json +4 -4
@@ -0,0 +1,236 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { extractConversationIdentity, extractFirstUserSnippet, isCompletionRequestPath, isTitleGenerationRequest, } from './conversation-identity.js';
4
+ test('isCompletionRequestPath matches API turn endpoints', () => {
5
+ assert.equal(isCompletionRequestPath('/v1/messages'), true);
6
+ assert.equal(isCompletionRequestPath('/v1/messages?beta=true'), true);
7
+ assert.equal(isCompletionRequestPath('/v1/chat/completions'), true);
8
+ assert.equal(isCompletionRequestPath('/v1/completions'), true);
9
+ assert.equal(isCompletionRequestPath('/v1/responses'), true);
10
+ assert.equal(isCompletionRequestPath('/v1/models'), false);
11
+ assert.equal(isCompletionRequestPath('/_antseed/route'), false);
12
+ });
13
+ test('claude code identity comes from x-claude-code-session-id', () => {
14
+ const identity = extractConversationIdentity({
15
+ 'user-agent': 'claude-cli/2.1.216 (external, sdk-cli)',
16
+ 'x-claude-code-session-id': '43ddc5e7-0b8e-491f-a5d1-f2a6854f689d',
17
+ }, null);
18
+ assert.deepEqual(identity, {
19
+ tool: 'claude-code',
20
+ sessionKey: '43ddc5e7-0b8e-491f-a5d1-f2a6854f689d',
21
+ parentSessionKey: null,
22
+ isUserThread: true,
23
+ });
24
+ });
25
+ test('system proxy source profile wins over SDK client identity for intercepted traffic', () => {
26
+ const identity = extractConversationIdentity({
27
+ 'user-agent': 'claude-cli/2.1.216 (external, sdk-cli)',
28
+ 'x-antseed-system-proxy-source': 'custom-api-anthropic-com',
29
+ 'x-claude-code-session-id': '43ddc5e7-0b8e-491f-a5d1-f2a6854f689d',
30
+ }, null);
31
+ assert.deepEqual(identity, {
32
+ tool: 'custom-api-anthropic-com',
33
+ sessionKey: '43ddc5e7-0b8e-491f-a5d1-f2a6854f689d',
34
+ parentSessionKey: null,
35
+ isUserThread: true,
36
+ });
37
+ });
38
+ test('turn metadata marks a tool-opened thread as not a user chat', () => {
39
+ const meta = (threadSource) => JSON.stringify({
40
+ thread_id: '019f9adc-ad46-7ba1-bdfc-b9612c116462',
41
+ request_kind: 'turn',
42
+ thread_source: threadSource,
43
+ });
44
+ const headers = (turnMetadata) => ({
45
+ 'originator': 'Codex Desktop',
46
+ 'thread-id': '019f9adc-ad46-7ba1-bdfc-b9612c116462',
47
+ 'x-codex-turn-metadata': turnMetadata,
48
+ });
49
+ // Codex titles a new chat from a system thread of its own.
50
+ assert.equal(extractConversationIdentity(headers(meta('system')), null)?.isUserThread, false);
51
+ assert.equal(extractConversationIdentity(headers(meta('user')), null)?.isUserThread, true);
52
+ // Missing, unparseable or silent metadata is taken as a user chat.
53
+ assert.equal(extractConversationIdentity(headers('{not json'), null)?.isUserThread, true);
54
+ assert.equal(extractConversationIdentity(headers('{}'), null)?.isUserThread, true);
55
+ assert.equal(extractConversationIdentity({ 'thread-id': 't1' }, null)?.isUserThread, true);
56
+ });
57
+ test('user-agent product token identifies the tool generically', () => {
58
+ const identity = extractConversationIdentity({ 'user-agent': 'claude-cli/2.1.216 (external, sdk-cli)' }, { metadata: { user_id: JSON.stringify({ device_id: 'abc', session_id: 'sess-123' }) } });
59
+ assert.equal(identity?.tool, 'claude-cli');
60
+ assert.equal(identity?.sessionKey, 'sess-123');
61
+ });
62
+ test('originator header names the client and thread-id wins as session key', () => {
63
+ const identity = extractConversationIdentity({
64
+ 'originator': 'codex_exec',
65
+ 'user-agent': 'codex_exec/0.138.0 (Mac OS 26.2.0; arm64)',
66
+ 'session-id': '019f83b7-0e98-7bc0-9d74-afd5e8844b76',
67
+ 'thread-id': '019f83b7-0e98-7bc0-9d74-afd5e8844b76',
68
+ }, null);
69
+ assert.equal(identity?.tool, 'codex-exec');
70
+ assert.equal(identity?.sessionKey, '019f83b7-0e98-7bc0-9d74-afd5e8844b76');
71
+ });
72
+ test('prompt_cache_key body field works as the session key fallback', () => {
73
+ const identity = extractConversationIdentity({ 'user-agent': 'codex_exec/0.138.0' }, { prompt_cache_key: '019f83b8-2914-7c90-94e6-3b5fc34335cd' });
74
+ assert.equal(identity?.tool, 'codex-exec');
75
+ assert.equal(identity?.sessionKey, '019f83b8-2914-7c90-94e6-3b5fc34335cd');
76
+ });
77
+ test('opencode identity uses x-session-id and carries the parent session', () => {
78
+ const identity = extractConversationIdentity({
79
+ 'user-agent': 'opencode/1.2.3',
80
+ 'x-session-id': 'ses_abc',
81
+ 'x-session-affinity': 'ses_abc',
82
+ 'x-parent-session-id': 'ses_parent',
83
+ }, null);
84
+ assert.deepEqual(identity, { tool: 'opencode', sessionKey: 'ses_abc', parentSessionKey: 'ses_parent', isUserThread: true });
85
+ });
86
+ test('any x-<tool>-session-id header identifies the tool generically', () => {
87
+ const identity = extractConversationIdentity({
88
+ 'user-agent': 'SomeNewTool/0.1',
89
+ 'x-cursor-session-id': 'sess-42',
90
+ }, null);
91
+ assert.deepEqual(identity, { tool: 'cursor', sessionKey: 'sess-42', parentSessionKey: null, isUserThread: true });
92
+ });
93
+ test('x-parent-session-id is never mistaken for a tool session header', () => {
94
+ const identity = extractConversationIdentity({
95
+ 'user-agent': 'opencode/1.0',
96
+ 'x-session-id': 'ses_child',
97
+ 'x-parent-session-id': 'ses_parent',
98
+ }, null);
99
+ assert.deepEqual(identity, { tool: 'opencode', sessionKey: 'ses_child', parentSessionKey: 'ses_parent', isUserThread: true });
100
+ });
101
+ test('requests without any identity return null', () => {
102
+ assert.equal(extractConversationIdentity({ 'user-agent': 'curl/8.0' }, { model: 'x' }), null);
103
+ assert.equal(extractConversationIdentity({}, null), null);
104
+ });
105
+ test('snippet: anthropic messages with string content', () => {
106
+ const snippet = extractFirstUserSnippet({
107
+ messages: [{ role: 'user', content: 'fix the login bug in auth.ts' }],
108
+ });
109
+ assert.equal(snippet, 'fix the login bug in auth.ts');
110
+ });
111
+ test('snippet: anthropic messages skip system-reminder blocks', () => {
112
+ const snippet = extractFirstUserSnippet({
113
+ messages: [{
114
+ role: 'user',
115
+ content: [
116
+ { type: 'text', text: '<system-reminder>context stuff</system-reminder>' },
117
+ { type: 'text', text: 'actual user question here' },
118
+ ],
119
+ }],
120
+ });
121
+ assert.equal(snippet, 'actual user question here');
122
+ });
123
+ test('snippet: responses input items skip environment context', () => {
124
+ const snippet = extractFirstUserSnippet({
125
+ input: [
126
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: '<environment_context>cwd=/tmp</environment_context>' }] },
127
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'say hi' }] },
128
+ ],
129
+ });
130
+ assert.equal(snippet, 'say hi');
131
+ });
132
+ test('snippet: responses string input, whitespace collapsed and truncated', () => {
133
+ assert.equal(extractFirstUserSnippet({ input: 'hello\n\n world' }), 'hello world');
134
+ const long = 'a'.repeat(500);
135
+ const snippet = extractFirstUserSnippet({ input: long });
136
+ assert.ok(snippet !== null && snippet.length <= 80);
137
+ assert.ok(snippet.endsWith('…'));
138
+ });
139
+ test('snippet: xml-ish tags are stripped from labels', () => {
140
+ assert.equal(extractFirstUserSnippet({ messages: [{ role: 'user', content: '<session> try to understand who reviews are from </session>' }] }), 'try to understand who reviews are from');
141
+ // Pure-markup candidates are passed over in favor of real text.
142
+ assert.equal(extractFirstUserSnippet({
143
+ messages: [
144
+ { role: 'user', content: '<context></context>' },
145
+ { role: 'user', content: 'real question' },
146
+ ],
147
+ }), 'real question');
148
+ });
149
+ test('snippet: falls back to machine context when nothing else exists', () => {
150
+ const snippet = extractFirstUserSnippet({
151
+ messages: [{ role: 'user', content: '<system-reminder>only this</system-reminder>' }],
152
+ });
153
+ assert.equal(snippet, 'only this');
154
+ });
155
+ test('snippet: opencode title request surfaces the embedded real prompt', () => {
156
+ // ensureTitle sends [instruction, ...real conversation] on the SAME session.
157
+ const snippet = extractFirstUserSnippet({
158
+ messages: [
159
+ { role: 'user', content: 'Generate a title for this conversation:\n' },
160
+ { role: 'user', content: 'hi' },
161
+ ],
162
+ });
163
+ assert.equal(snippet, 'hi');
164
+ });
165
+ test('snippet: pure title request yields null, never a label', () => {
166
+ assert.equal(extractFirstUserSnippet({
167
+ messages: [{ role: 'user', content: 'Please write a 5-10 word title for the following conversation:\n\nuser: hello' }],
168
+ }), null);
169
+ assert.equal(extractFirstUserSnippet({
170
+ messages: [{ role: 'user', content: 'Generate a brief title that would help the user find this conversation later.' }],
171
+ }), null);
172
+ });
173
+ test('snippet: injected project-doc blobs never label a chat', () => {
174
+ // Codex sends AGENTS.md/CLAUDE.md contents as a user message ahead of the
175
+ // real prompt; the doc must be skipped in favor of the genuine turn.
176
+ assert.equal(extractFirstUserSnippet({
177
+ input: [
178
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: '# AGENTS.md instructions for /Users/shahafan/Development/antseed\n\n# CLAUDE.md --...' }] },
179
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: '<environment_context>cwd=/tmp</environment_context>' }] },
180
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'fix the failing tests' }] },
181
+ ],
182
+ }), 'fix the failing tests');
183
+ // A doc-only request yields null instead of falling back to the blob —
184
+ // even when wrapped in a machine-context tag.
185
+ assert.equal(extractFirstUserSnippet({
186
+ input: [
187
+ { type: 'message', role: 'user', content: [{ type: 'input_text', text: '<user_instructions># AGENTS.md instructions for /repo ...</user_instructions>' }] },
188
+ ],
189
+ }), null);
190
+ });
191
+ test('sanitizeStoredSnippet heals stored project-doc labels to empty', async () => {
192
+ const { sanitizeStoredSnippet } = await import('./conversation-identity.js');
193
+ assert.equal(sanitizeStoredSnippet('# AGENTS.md instructions for /Users/shahafan/Development/antseed # CLAUDE.md --…'), '');
194
+ assert.equal(sanitizeStoredSnippet('fix the login bug'), 'fix the login bug');
195
+ });
196
+ test('snippet: null for empty or non-conversation bodies', () => {
197
+ assert.equal(extractFirstUserSnippet(null), null);
198
+ assert.equal(extractFirstUserSnippet({ model: 'x' }), null);
199
+ assert.equal(extractFirstUserSnippet({ messages: [{ role: 'assistant', content: 'hi' }] }), null);
200
+ });
201
+ test('title turns are recognised so they cannot define a chat model', () => {
202
+ // OpenCode's verbatim shape: the instruction is its own leading message and
203
+ // the conversation being titled follows it, on the chat's own session id.
204
+ assert.equal(isTitleGenerationRequest({
205
+ messages: [
206
+ { role: 'user', content: 'Generate a title for this conversation:\n' },
207
+ { role: 'user', content: 'hi' },
208
+ ],
209
+ }), true);
210
+ // Claude Code's phrasing, as a Responses-style item list.
211
+ assert.equal(isTitleGenerationRequest({
212
+ input: [{
213
+ type: 'message',
214
+ role: 'user',
215
+ content: [{ type: 'input_text', text: 'Please write a 5-10 word title for the following conversation' }],
216
+ }],
217
+ }), true);
218
+ // T3 Code's Claude agent title prompt.
219
+ assert.equal(isTitleGenerationRequest({
220
+ messages: [{ role: 'user', content: 'You write concise thread titles for a coding chat.' }],
221
+ }), true);
222
+ });
223
+ test('real turns are not mistaken for title turns', () => {
224
+ assert.equal(isTitleGenerationRequest({ messages: [{ role: 'user', content: 'hi' }] }), false);
225
+ // Only the leading message counts — a later turn quoting the instruction
226
+ // (it is in the history once titling ran) is still a real turn.
227
+ assert.equal(isTitleGenerationRequest({
228
+ messages: [
229
+ { role: 'user', content: 'fix the failing tests' },
230
+ { role: 'user', content: 'Generate a title for this conversation:' },
231
+ ],
232
+ }), false);
233
+ assert.equal(isTitleGenerationRequest(null), false);
234
+ assert.equal(isTitleGenerationRequest({ model: 'x' }), false);
235
+ });
236
+ //# sourceMappingURL=conversation-identity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-identity.test.js","sourceRoot":"","sources":["../../src/proxy/conversation-identity.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * File-backed store for tool conversations seen by the buyer proxy.
3
+ *
4
+ * Each record maps one tool chat session (identified on the wire — see
5
+ * conversation-identity.ts) to a display label and an optional per-chat
6
+ * routed-model pin. Persists as `conversations.json` in the buyer data dir,
7
+ * written atomically (tmp + rename) with writes serialized behind a queue,
8
+ * mirroring how buyer.state.json is handled. No database involved.
9
+ */
10
+ export type StoredConversation = {
11
+ /** `${tool}:${sessionKey}` — unique per tool chat. */
12
+ id: string;
13
+ tool: string;
14
+ sessionKey: string;
15
+ /** First genuine user prompt, captured when the conversation is first seen. */
16
+ snippet: string;
17
+ /** User-assigned name; overrides the snippet for display when set. */
18
+ label: string | null;
19
+ /** Per-chat route pin as `<peerId>@<service>`. The default route only
20
+ steers a chat's first request: the model that serves it is pinned here
21
+ (see touch), so later default changes affect only new chats. Null only
22
+ until the chat's first resolved request. */
23
+ pinnedModel: string | null;
24
+ /** Model that served the most recent request (`<peerId>@<service>`), for display. */
25
+ lastModel: string | null;
26
+ /** USDC base units authorized for this chat's requests (bigint as string).
27
+ Subagent traffic rolls up into the parent chat, same as everything else
28
+ here. This is what the chat has cost, not what has settled on-chain. */
29
+ spentUsdc: string;
30
+ /** Cumulative tokens across this chat's requests (bigint as string).
31
+ `cachedInputTokens` is the cached SUBSET of `inputTokens`, not a separate
32
+ bucket — fresh input is `inputTokens - cachedInputTokens`. */
33
+ inputTokens: string;
34
+ cachedInputTokens: string;
35
+ outputTokens: string;
36
+ /** Requests whose spend was attributed to this chat. */
37
+ requestCount: number;
38
+ createdAt: number;
39
+ lastActiveAt: number;
40
+ };
41
+ export declare const CONVERSATIONS_FILE = "conversations.json";
42
+ export declare function conversationId(tool: string, sessionKey: string): string;
43
+ export declare class ConversationStore {
44
+ private readonly _dir;
45
+ private readonly _file;
46
+ private readonly _byId;
47
+ private _writeQueue;
48
+ constructor(dataDir: string);
49
+ private _loadSync;
50
+ private _prune;
51
+ /** Serialized atomic write; returns the queued write promise. */
52
+ private _persist;
53
+ /** Wait for pending writes (tests / shutdown). */
54
+ flush(): Promise<void>;
55
+ /**
56
+ * Record activity for a conversation, creating it on first sight. The
57
+ * snippet only sticks at creation — later turns keep the original label.
58
+ *
59
+ * The first model that actually serves the chat becomes its pin: the
60
+ * session default only steers chats that haven't resolved a request yet,
61
+ * so changing the default never re-routes an existing conversation.
62
+ */
63
+ touch(input: {
64
+ tool: string;
65
+ sessionKey: string;
66
+ snippet?: string | null;
67
+ lastModel?: string | null;
68
+ }): StoredConversation;
69
+ /**
70
+ * Add one signed spend delta to a conversation. No-ops for unknown ids —
71
+ * a chat pruned mid-request should not resurrect as a spend-only row.
72
+ *
73
+ * A single request can produce several deltas (the buyer- and seller-driven
74
+ * auth paths both advance the cumulative), so `countRequest` is the caller's
75
+ * to decide: it knows which delta was the first for a given request id.
76
+ *
77
+ * Not persisted immediately: touch() already rewrites the file on every
78
+ * turn, so the counters ride along with the next write. At most the last
79
+ * request's cost is lost on a hard kill.
80
+ */
81
+ addSpend(id: string, delta: {
82
+ amountUsdc: string;
83
+ inputTokens: string;
84
+ cachedInputTokens: string;
85
+ outputTokens: string;
86
+ }, countRequest?: boolean): void;
87
+ /** Newest-activity first (stable sort over reversed insertion order keeps
88
+ same-millisecond ties in true recency order). */
89
+ list(): StoredConversation[];
90
+ get(id: string): StoredConversation | null;
91
+ getPinnedModel(tool: string, sessionKey: string): string | null;
92
+ setLabel(id: string, label: string | null): StoredConversation | null;
93
+ setPinnedModel(id: string, pinnedModel: string | null): StoredConversation | null;
94
+ remove(id: string): boolean;
95
+ }
96
+ //# sourceMappingURL=conversation-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-store.d.ts","sourceRoot":"","sources":["../../src/proxy/conversation-store.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;mDAG+C;IAC/C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,qFAAqF;IACrF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;+EAE2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB;;qEAEiE;IACjE,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAOtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvE;AAuCD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,WAAW,CAAmC;gBAE1C,OAAO,EAAE,MAAM;IAM3B,OAAO,CAAC,SAAS;IAuBjB,OAAO,CAAC,MAAM;IAYd,iEAAiE;IACjE,OAAO,CAAC,QAAQ;IAWhB,kDAAkD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,kBAAkB;IAsC1H;;;;;;;;;;;OAWG;IACH,QAAQ,CACN,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EACnG,YAAY,UAAO,GAClB,IAAI;IA6BP;wDACoD;IACpD,IAAI,IAAI,kBAAkB,EAAE;IAI5B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAI1C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI/D,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,kBAAkB,GAAG,IAAI;IAUrE,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,kBAAkB,GAAG,IAAI;IASjF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAK5B"}
@@ -0,0 +1,242 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { mkdir, rename, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { sanitizeStoredSnippet } from './conversation-identity.js';
5
+ export const CONVERSATIONS_FILE = 'conversations.json';
6
+ /** LRU cap — oldest by lastActiveAt beyond this are pruned. */
7
+ const MAX_CONVERSATIONS = 50;
8
+ /** Conversations idle longer than this are pruned. */
9
+ const MAX_IDLE_MS = 7 * 24 * 60 * 60 * 1000;
10
+ const MAX_LABEL_CHARS = 120;
11
+ export function conversationId(tool, sessionKey) {
12
+ return `${tool}:${sessionKey}`;
13
+ }
14
+ /** Reads a persisted bigint-as-string counter, healing anything unparseable to '0'. */
15
+ function sanitizeCounter(value) {
16
+ if (typeof value !== 'string')
17
+ return '0';
18
+ try {
19
+ const parsed = BigInt(value);
20
+ return parsed > 0n ? parsed.toString() : '0';
21
+ }
22
+ catch {
23
+ return '0';
24
+ }
25
+ }
26
+ function sanitizeRecord(value) {
27
+ if (!value || typeof value !== 'object' || Array.isArray(value))
28
+ return null;
29
+ const record = value;
30
+ const tool = typeof record.tool === 'string' ? record.tool : '';
31
+ const sessionKey = typeof record.sessionKey === 'string' ? record.sessionKey : '';
32
+ if (!tool || !sessionKey)
33
+ return null;
34
+ return {
35
+ id: conversationId(tool, sessionKey),
36
+ tool,
37
+ sessionKey,
38
+ // Persisted snippets are re-cleaned so rows written by older extraction
39
+ // rules (raw XML wrappers, title-request text) heal on reload.
40
+ snippet: typeof record.snippet === 'string' ? sanitizeStoredSnippet(record.snippet) : '',
41
+ label: typeof record.label === 'string' && record.label.length > 0 ? record.label : null,
42
+ pinnedModel: typeof record.pinnedModel === 'string' && record.pinnedModel.length > 0 ? record.pinnedModel : null,
43
+ lastModel: typeof record.lastModel === 'string' && record.lastModel.length > 0 ? record.lastModel : null,
44
+ spentUsdc: sanitizeCounter(record.spentUsdc),
45
+ inputTokens: sanitizeCounter(record.inputTokens),
46
+ cachedInputTokens: sanitizeCounter(record.cachedInputTokens),
47
+ outputTokens: sanitizeCounter(record.outputTokens),
48
+ requestCount: typeof record.requestCount === 'number' && record.requestCount > 0 ? record.requestCount : 0,
49
+ createdAt: typeof record.createdAt === 'number' ? record.createdAt : Date.now(),
50
+ lastActiveAt: typeof record.lastActiveAt === 'number' ? record.lastActiveAt : Date.now(),
51
+ };
52
+ }
53
+ export class ConversationStore {
54
+ _dir;
55
+ _file;
56
+ _byId = new Map();
57
+ _writeQueue = Promise.resolve();
58
+ constructor(dataDir) {
59
+ this._dir = dataDir;
60
+ this._file = join(dataDir, CONVERSATIONS_FILE);
61
+ this._loadSync();
62
+ }
63
+ _loadSync() {
64
+ let raw;
65
+ try {
66
+ raw = readFileSync(this._file, 'utf8');
67
+ }
68
+ catch {
69
+ return; // first run — no file yet
70
+ }
71
+ try {
72
+ const parsed = JSON.parse(raw);
73
+ const records = (parsed.conversations ?? [])
74
+ .map(sanitizeRecord)
75
+ .filter((record) => record !== null)
76
+ .sort((a, b) => a.lastActiveAt - b.lastActiveAt); // map order tracks recency
77
+ for (const record of records) {
78
+ this._byId.set(record.id, record);
79
+ }
80
+ }
81
+ catch { /* corrupted file — start clean, next write repairs it */ }
82
+ this._prune();
83
+ }
84
+ /* The map's insertion order tracks recency (touch() re-inserts), so ties
85
+ on lastActiveAt — e.g. many chats touched in the same millisecond —
86
+ still evict oldest-first. */
87
+ _prune() {
88
+ const cutoff = Date.now() - MAX_IDLE_MS;
89
+ for (const [id, record] of this._byId) {
90
+ if (record.lastActiveAt < cutoff)
91
+ this._byId.delete(id);
92
+ }
93
+ while (this._byId.size > MAX_CONVERSATIONS) {
94
+ const oldest = this._byId.keys().next().value;
95
+ if (oldest === undefined)
96
+ break;
97
+ this._byId.delete(oldest);
98
+ }
99
+ }
100
+ /** Serialized atomic write; returns the queued write promise. */
101
+ _persist() {
102
+ this._writeQueue = this._writeQueue.then(async () => {
103
+ const payload = JSON.stringify({ conversations: [...this._byId.values()] }, null, 2);
104
+ await mkdir(this._dir, { recursive: true });
105
+ const tmp = `${this._file}.tmp`;
106
+ await writeFile(tmp, payload, 'utf8');
107
+ await rename(tmp, this._file);
108
+ }).catch(() => { });
109
+ return this._writeQueue;
110
+ }
111
+ /** Wait for pending writes (tests / shutdown). */
112
+ flush() {
113
+ return this._writeQueue;
114
+ }
115
+ /**
116
+ * Record activity for a conversation, creating it on first sight. The
117
+ * snippet only sticks at creation — later turns keep the original label.
118
+ *
119
+ * The first model that actually serves the chat becomes its pin: the
120
+ * session default only steers chats that haven't resolved a request yet,
121
+ * so changing the default never re-routes an existing conversation.
122
+ */
123
+ touch(input) {
124
+ const id = conversationId(input.tool, input.sessionKey);
125
+ const existing = this._byId.get(id);
126
+ const now = Date.now();
127
+ let record;
128
+ if (existing) {
129
+ record = {
130
+ ...existing,
131
+ lastActiveAt: now,
132
+ lastModel: input.lastModel ?? existing.lastModel,
133
+ pinnedModel: existing.pinnedModel ?? input.lastModel ?? null,
134
+ snippet: existing.snippet || (input.snippet ?? ''),
135
+ };
136
+ }
137
+ else {
138
+ record = {
139
+ id,
140
+ tool: input.tool,
141
+ sessionKey: input.sessionKey,
142
+ snippet: input.snippet ?? '',
143
+ label: null,
144
+ pinnedModel: input.lastModel ?? null,
145
+ lastModel: input.lastModel ?? null,
146
+ spentUsdc: '0',
147
+ inputTokens: '0',
148
+ cachedInputTokens: '0',
149
+ outputTokens: '0',
150
+ requestCount: 0,
151
+ createdAt: now,
152
+ lastActiveAt: now,
153
+ };
154
+ }
155
+ this._byId.delete(id); // re-insert so map order tracks recency
156
+ this._byId.set(id, record);
157
+ this._prune();
158
+ void this._persist();
159
+ return record;
160
+ }
161
+ /**
162
+ * Add one signed spend delta to a conversation. No-ops for unknown ids —
163
+ * a chat pruned mid-request should not resurrect as a spend-only row.
164
+ *
165
+ * A single request can produce several deltas (the buyer- and seller-driven
166
+ * auth paths both advance the cumulative), so `countRequest` is the caller's
167
+ * to decide: it knows which delta was the first for a given request id.
168
+ *
169
+ * Not persisted immediately: touch() already rewrites the file on every
170
+ * turn, so the counters ride along with the next write. At most the last
171
+ * request's cost is lost on a hard kill.
172
+ */
173
+ addSpend(id, delta, countRequest = true) {
174
+ const existing = this._byId.get(id);
175
+ if (!existing)
176
+ return;
177
+ let amount;
178
+ let input;
179
+ let cachedInput;
180
+ let output;
181
+ try {
182
+ amount = BigInt(delta.amountUsdc || '0');
183
+ input = BigInt(delta.inputTokens || '0');
184
+ cachedInput = BigInt(delta.cachedInputTokens || '0');
185
+ output = BigInt(delta.outputTokens || '0');
186
+ }
187
+ catch {
188
+ return;
189
+ }
190
+ // Cached input is a subset of input, so a delta reporting only cached
191
+ // tokens is malformed — clamp rather than let the subset exceed the whole.
192
+ if (cachedInput > input)
193
+ cachedInput = input;
194
+ if (amount <= 0n && input <= 0n && output <= 0n)
195
+ return;
196
+ this._byId.set(id, {
197
+ ...existing,
198
+ spentUsdc: (BigInt(existing.spentUsdc) + (amount > 0n ? amount : 0n)).toString(),
199
+ inputTokens: (BigInt(existing.inputTokens) + (input > 0n ? input : 0n)).toString(),
200
+ cachedInputTokens: (BigInt(existing.cachedInputTokens) + (cachedInput > 0n ? cachedInput : 0n)).toString(),
201
+ outputTokens: (BigInt(existing.outputTokens) + (output > 0n ? output : 0n)).toString(),
202
+ requestCount: existing.requestCount + (countRequest ? 1 : 0),
203
+ });
204
+ }
205
+ /** Newest-activity first (stable sort over reversed insertion order keeps
206
+ same-millisecond ties in true recency order). */
207
+ list() {
208
+ return [...this._byId.values()].reverse().sort((a, b) => b.lastActiveAt - a.lastActiveAt);
209
+ }
210
+ get(id) {
211
+ return this._byId.get(id) ?? null;
212
+ }
213
+ getPinnedModel(tool, sessionKey) {
214
+ return this._byId.get(conversationId(tool, sessionKey))?.pinnedModel ?? null;
215
+ }
216
+ setLabel(id, label) {
217
+ const existing = this._byId.get(id);
218
+ if (!existing)
219
+ return null;
220
+ const clean = label?.replace(/\s+/g, ' ').trim().slice(0, MAX_LABEL_CHARS) || null;
221
+ const record = { ...existing, label: clean };
222
+ this._byId.set(id, record);
223
+ void this._persist();
224
+ return record;
225
+ }
226
+ setPinnedModel(id, pinnedModel) {
227
+ const existing = this._byId.get(id);
228
+ if (!existing)
229
+ return null;
230
+ const record = { ...existing, pinnedModel: pinnedModel || null };
231
+ this._byId.set(id, record);
232
+ void this._persist();
233
+ return record;
234
+ }
235
+ remove(id) {
236
+ const removed = this._byId.delete(id);
237
+ if (removed)
238
+ void this._persist();
239
+ return removed;
240
+ }
241
+ }
242
+ //# sourceMappingURL=conversation-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-store.js","sourceRoot":"","sources":["../../src/proxy/conversation-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conversation-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-store.test.d.ts","sourceRoot":"","sources":["../../src/proxy/conversation-store.test.ts"],"names":[],"mappings":""}