@remixhq/core 0.1.14 → 0.1.16

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 (72) hide show
  1. package/dist/api.d.ts +1 -1
  2. package/dist/collab.d.ts +2 -2
  3. package/dist/collab.js +1 -1
  4. package/dist/{contracts-NbV3P_Rl.d.ts → contracts-CHmD-fMj.d.ts} +1 -1
  5. package/dist/history.d.ts +64 -0
  6. package/dist/history.js +529 -0
  7. package/dist/index.d.ts +1 -1
  8. package/package.json +6 -2
  9. package/dist/chunk-2WGZS7CD.js +0 -0
  10. package/dist/chunk-34WDQCPF.js +0 -242
  11. package/dist/chunk-4276ARDF.js +0 -303
  12. package/dist/chunk-4L3ZBZUQ.js +0 -281
  13. package/dist/chunk-54CBEP2W.js +0 -570
  14. package/dist/chunk-55K5GHAZ.js +0 -252
  15. package/dist/chunk-5H5CZKGN.js +0 -691
  16. package/dist/chunk-5NTOJXEZ.js +0 -223
  17. package/dist/chunk-7WUKH3ZD.js +0 -221
  18. package/dist/chunk-AE2HPMUZ.js +0 -80
  19. package/dist/chunk-AEAOYVIL.js +0 -200
  20. package/dist/chunk-B5S3PUIR.js +0 -388
  21. package/dist/chunk-BJFCN2C3.js +0 -46
  22. package/dist/chunk-BNKPTE2U.js +0 -401
  23. package/dist/chunk-C5NBNU32.js +0 -240
  24. package/dist/chunk-CJFGQE7D.js +0 -46
  25. package/dist/chunk-CUUXZSKW.js +0 -611
  26. package/dist/chunk-DCU3646I.js +0 -12
  27. package/dist/chunk-DEWAIK5X.js +0 -11
  28. package/dist/chunk-DRD6EVTT.js +0 -447
  29. package/dist/chunk-DXCL6I4Q.js +0 -399
  30. package/dist/chunk-E4KAGBU7.js +0 -134
  31. package/dist/chunk-E6AYE22H.js +0 -343
  32. package/dist/chunk-EF3677RE.js +0 -93
  33. package/dist/chunk-EVWDYCBL.js +0 -223
  34. package/dist/chunk-EW4PWFHB.js +0 -46
  35. package/dist/chunk-FAZUMWBS.js +0 -93
  36. package/dist/chunk-GEHSFPCD.js +0 -93
  37. package/dist/chunk-GFOBGYW4.js +0 -252
  38. package/dist/chunk-INDDXWAH.js +0 -92
  39. package/dist/chunk-IXWQWFYT.js +0 -342
  40. package/dist/chunk-J3J4PBQ7.js +0 -710
  41. package/dist/chunk-K54U353Z.js +0 -691
  42. package/dist/chunk-K57ZFDGC.js +0 -15
  43. package/dist/chunk-NDA7EJJA.js +0 -286
  44. package/dist/chunk-NK2DA4X6.js +0 -357
  45. package/dist/chunk-OBYR4JHZ.js +0 -374
  46. package/dist/chunk-OJMTW22J.js +0 -286
  47. package/dist/chunk-OMUDRPUI.js +0 -195
  48. package/dist/chunk-ONKKRS2C.js +0 -239
  49. package/dist/chunk-OWFBBWU7.js +0 -196
  50. package/dist/chunk-OZRXDDEL.js +0 -46
  51. package/dist/chunk-P7EM3N73.js +0 -46
  52. package/dist/chunk-POYB6MCQ.js +0 -373
  53. package/dist/chunk-PR5QKMHM.js +0 -46
  54. package/dist/chunk-R44EOUS4.js +0 -288
  55. package/dist/chunk-R7FVSCQW.js +0 -415
  56. package/dist/chunk-RIP2MIZL.js +0 -710
  57. package/dist/chunk-RKMMEML5.js +0 -46
  58. package/dist/chunk-RM2BGDBB.js +0 -400
  59. package/dist/chunk-RREREIGW.js +0 -710
  60. package/dist/chunk-TQHLFQY4.js +0 -448
  61. package/dist/chunk-TY3SSQQK.js +0 -688
  62. package/dist/chunk-UGKPOCN5.js +0 -710
  63. package/dist/chunk-UIGKSCTD.js +0 -406
  64. package/dist/chunk-UWIVJRTI.js +0 -343
  65. package/dist/chunk-VA6WXRWB.js +0 -636
  66. package/dist/chunk-XC2FV57P.js +0 -385
  67. package/dist/chunk-XOQIADCH.js +0 -223
  68. package/dist/chunk-ZAQZKEH4.js +0 -46
  69. package/dist/chunk-ZBMOGUSJ.js +0 -17
  70. package/dist/chunk-ZXP6ENQY.js +0 -244
  71. package/dist/index.cjs +0 -1269
  72. package/dist/index.d.cts +0 -482
package/dist/api.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CoreConfig } from './config.js';
2
2
  import { T as TokenProvider } from './tokenProvider-BWTusyj4.js';
3
- import { E as TurnUsage } from './contracts-NbV3P_Rl.js';
3
+ import { T as TurnUsage } from './contracts-CHmD-fMj.js';
4
4
  import 'zod';
5
5
 
6
6
  type HistoryImportProvider = "claude_code" | "cursor";
package/dist/collab.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CollabApiClient, T as TurnUsagePayload, a as CollabFinalizeTurnResult, b as CollabRecordingPreflight, c as CollabApproveMode, d as CollabApproveResult, M as MergeRequestQueue, e as MergeRequest, I as InvitationScopeType, f as CollabMember, J as JsonObject, A as AppDeltaResponse, g as CollabStatus, h as MergeRequestReview } from './contracts-NbV3P_Rl.js';
2
- export { i as AppHeadResponse, j as AppMember, k as AppMemberRole, l as AppReconcileResponse, m as CollabFinalizeTurnAutoSync, n as CollabFinalizeTurnMode, o as CollabPendingFinalizeState, p as CollabPendingFinalizeSummary, q as CollabRecordingPreflightStatus, r as CollabRepoStateKind, s as CollabStatusBlockedReason, t as CollabStatusRecommendedAction, u as CollabTurn, v as MembershipScopeType, w as MergeRequestStatus, x as ModelCall, O as OrganizationMember, y as OrganizationMemberRole, P as ProjectMember, z as ProjectMemberRole, R as ReconcilePreflightResponse, S as ServerToolUsage, B as SyncLocalResponse, D as SyncUpstreamResponse, E as TurnUsage, F as TurnUsageCaptureSource, G as TurnUsageConfidence, H as TurnUsagePreviousPatch } from './contracts-NbV3P_Rl.js';
1
+ import { C as CollabApiClient, a as TurnUsagePayload, b as CollabFinalizeTurnResult, c as CollabRecordingPreflight, d as CollabApproveMode, e as CollabApproveResult, M as MergeRequestQueue, f as MergeRequest, I as InvitationScopeType, g as CollabMember, J as JsonObject, A as AppDeltaResponse, h as CollabStatus, i as MergeRequestReview } from './contracts-CHmD-fMj.js';
2
+ export { j as AppHeadResponse, k as AppMember, l as AppMemberRole, m as AppReconcileResponse, n as CollabFinalizeTurnAutoSync, o as CollabFinalizeTurnMode, p as CollabPendingFinalizeState, q as CollabPendingFinalizeSummary, r as CollabRecordingPreflightStatus, s as CollabRepoStateKind, t as CollabStatusBlockedReason, u as CollabStatusRecommendedAction, v as CollabTurn, w as MembershipScopeType, x as MergeRequestStatus, y as ModelCall, O as OrganizationMember, z as OrganizationMemberRole, P as ProjectMember, B as ProjectMemberRole, R as ReconcilePreflightResponse, S as ServerToolUsage, D as SyncLocalResponse, E as SyncUpstreamResponse, T as TurnUsage, F as TurnUsageCaptureSource, G as TurnUsageConfidence, H as TurnUsagePreviousPatch } from './contracts-CHmD-fMj.js';
3
3
  import { B as BranchBindingMode } from './binding-WiIRI2fl.js';
4
4
 
5
5
  declare function collabFinalizeTurn(params: {
package/dist/collab.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "./chunk-HZNEDSRS.js";
1
2
  import {
2
3
  getCollabBindingPath,
3
4
  readCollabBinding,
@@ -8,7 +9,6 @@ import {
8
9
  writeCollabBindingSnapshot,
9
10
  writeJsonAtomic
10
11
  } from "./chunk-YCFLOHJV.js";
11
- import "./chunk-HZNEDSRS.js";
12
12
  import {
13
13
  applyUnifiedDiffToWorktree,
14
14
  assertRepoSnapshotUnchanged,
@@ -674,4 +674,4 @@ type CollabApiClient = {
674
674
  }): Promise<unknown>;
675
675
  };
676
676
 
677
- export type { AppDeltaResponse as A, SyncLocalResponse as B, CollabApiClient as C, SyncUpstreamResponse as D, TurnUsage as E, TurnUsageCaptureSource as F, TurnUsageConfidence as G, TurnUsagePreviousPatch as H, InvitationScopeType as I, JsonObject as J, MergeRequestQueue as M, OrganizationMember as O, ProjectMember as P, ReconcilePreflightResponse as R, ServerToolUsage as S, TurnUsagePayload as T, CollabFinalizeTurnResult as a, CollabRecordingPreflight as b, CollabApproveMode as c, CollabApproveResult as d, MergeRequest as e, CollabMember as f, CollabStatus as g, MergeRequestReview as h, AppHeadResponse as i, AppMember as j, AppMemberRole as k, AppReconcileResponse as l, CollabFinalizeTurnAutoSync as m, CollabFinalizeTurnMode as n, CollabPendingFinalizeState as o, CollabPendingFinalizeSummary as p, CollabRecordingPreflightStatus as q, CollabRepoStateKind as r, CollabStatusBlockedReason as s, CollabStatusRecommendedAction as t, CollabTurn as u, MembershipScopeType as v, MergeRequestStatus as w, ModelCall as x, OrganizationMemberRole as y, ProjectMemberRole as z };
677
+ export type { AppDeltaResponse as A, ProjectMemberRole as B, CollabApiClient as C, SyncLocalResponse as D, SyncUpstreamResponse as E, TurnUsageCaptureSource as F, TurnUsageConfidence as G, TurnUsagePreviousPatch as H, InvitationScopeType as I, JsonObject as J, MergeRequestQueue as M, OrganizationMember as O, ProjectMember as P, ReconcilePreflightResponse as R, ServerToolUsage as S, TurnUsage as T, TurnUsagePayload as a, CollabFinalizeTurnResult as b, CollabRecordingPreflight as c, CollabApproveMode as d, CollabApproveResult as e, MergeRequest as f, CollabMember as g, CollabStatus as h, MergeRequestReview as i, AppHeadResponse as j, AppMember as k, AppMemberRole as l, AppReconcileResponse as m, CollabFinalizeTurnAutoSync as n, CollabFinalizeTurnMode as o, CollabPendingFinalizeState as p, CollabPendingFinalizeSummary as q, CollabRecordingPreflightStatus as r, CollabRepoStateKind as s, CollabStatusBlockedReason as t, CollabStatusRecommendedAction as u, CollabTurn as v, MembershipScopeType as w, MergeRequestStatus as x, ModelCall as y, OrganizationMemberRole as z };
@@ -0,0 +1,64 @@
1
+ import { T as TurnUsage } from './contracts-CHmD-fMj.js';
2
+
3
+ type TranscriptEvent = Record<string, unknown>;
4
+ type ReadTranscriptResult = {
5
+ ok: true;
6
+ events: TranscriptEvent[];
7
+ } | {
8
+ ok: false;
9
+ reason: "transcript_not_found" | "transcript_unreadable";
10
+ };
11
+ declare function readAndParseTranscript(transcriptPath: string): Promise<ReadTranscriptResult>;
12
+
13
+ declare const HARVESTER_WARNING_CODES: readonly ["cache_split_unavailable", "unknown_server_tool", "transcript_truncated", "subagent_model_differs", "server_tool_count_mismatch"];
14
+ type HarvesterWarningCode = (typeof HARVESTER_WARNING_CODES)[number];
15
+ type HarvestWarning = {
16
+ code: HarvesterWarningCode;
17
+ message: string;
18
+ };
19
+ type HarvestUsageInput = {
20
+ events: TranscriptEvent[];
21
+ sessionId: string;
22
+ promptText: string;
23
+ submittedAt: string;
24
+ checkSubsequentEvent?: boolean;
25
+ nextBoundaryAt?: string | null;
26
+ agent: {
27
+ name: "claude-code";
28
+ version: string | null;
29
+ sessionId: string | null;
30
+ turnId: string | null;
31
+ plan: string | null;
32
+ };
33
+ capturedAt: string;
34
+ extensions: Record<string, unknown> | null;
35
+ };
36
+ type HarvestUsageFailureReason = "no_user_boundary_found" | "no_messages_for_turn";
37
+ type HarvestUsageResult = {
38
+ ok: true;
39
+ usage: TurnUsage;
40
+ boundaryAt: string | null;
41
+ } | {
42
+ ok: false;
43
+ reason: HarvestUsageFailureReason;
44
+ };
45
+ declare function harvestClaudeCodeUsage(input: HarvestUsageInput): HarvestUsageResult;
46
+ type SlicedTurn = {
47
+ sessionId: string;
48
+ promptId: string | null;
49
+ promptText: string | null;
50
+ assistantText: string | null;
51
+ occurredAt: string;
52
+ gitBranch: string | null;
53
+ cwd: string | null;
54
+ usage: TurnUsage;
55
+ };
56
+ type SliceTranscriptInput = {
57
+ events: TranscriptEvent[];
58
+ sessionId: string;
59
+ capturedAt: string;
60
+ extensions?: Record<string, unknown> | null;
61
+ };
62
+ declare function sliceTranscriptIntoTurns(input: SliceTranscriptInput): SlicedTurn[];
63
+
64
+ export { HARVESTER_WARNING_CODES, type HarvestUsageFailureReason, type HarvestUsageInput, type HarvestUsageResult, type HarvestWarning, type HarvesterWarningCode, type ReadTranscriptResult, type SliceTranscriptInput, type SlicedTurn, type TranscriptEvent, harvestClaudeCodeUsage, readAndParseTranscript, sliceTranscriptIntoTurns };
@@ -0,0 +1,529 @@
1
+ // src/history/claudeCodeTranscript.ts
2
+ import fs from "fs/promises";
3
+ async function readAndParseTranscript(transcriptPath) {
4
+ let raw;
5
+ try {
6
+ raw = await fs.readFile(transcriptPath, "utf8");
7
+ } catch (err) {
8
+ const code = err && typeof err === "object" && "code" in err ? err.code : null;
9
+ if (code === "ENOENT") {
10
+ return { ok: false, reason: "transcript_not_found" };
11
+ }
12
+ return { ok: false, reason: "transcript_unreadable" };
13
+ }
14
+ const events = [];
15
+ for (const line of raw.split("\n")) {
16
+ const trimmed = line.trim();
17
+ if (!trimmed) continue;
18
+ try {
19
+ const parsed = JSON.parse(trimmed);
20
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
21
+ events.push(parsed);
22
+ }
23
+ } catch {
24
+ }
25
+ }
26
+ return { ok: true, events };
27
+ }
28
+
29
+ // src/history/claudeCodeUsageHarvester.ts
30
+ var HARVESTER_WARNING_CODES = [
31
+ "cache_split_unavailable",
32
+ "unknown_server_tool",
33
+ "transcript_truncated",
34
+ "subagent_model_differs",
35
+ "server_tool_count_mismatch"
36
+ ];
37
+ var BOUNDARY_TIMESTAMP_TOLERANCE_MS = 500;
38
+ function parseTimestamp(value) {
39
+ if (typeof value !== "string") return null;
40
+ const ms = Date.parse(value);
41
+ return Number.isFinite(ms) ? ms : null;
42
+ }
43
+ function extractUserContentText(message) {
44
+ if (!message || typeof message !== "object") return null;
45
+ const content = message.content;
46
+ if (typeof content === "string") return content;
47
+ if (Array.isArray(content)) {
48
+ const textBlocks = content.filter((block) => Boolean(block) && typeof block === "object").filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text);
49
+ if (textBlocks.length === 0) return null;
50
+ return textBlocks.join("\n");
51
+ }
52
+ return null;
53
+ }
54
+ function isUserBoundary(event) {
55
+ return event.type === "user" && event.isMeta !== true && event.isSidechain !== true;
56
+ }
57
+ function extractAssistantContentText(turnEvents) {
58
+ const chunks = [];
59
+ for (const ev of turnEvents) {
60
+ if (ev.type !== "assistant") continue;
61
+ const text = extractUserContentText(ev.message);
62
+ if (text && text.length > 0) chunks.push(text);
63
+ }
64
+ if (chunks.length === 0) return null;
65
+ const deduped = [];
66
+ for (const chunk of chunks) {
67
+ if (deduped[deduped.length - 1] !== chunk) deduped.push(chunk);
68
+ }
69
+ return deduped.join("\n");
70
+ }
71
+ function findBoundary(sessionEvents, promptText, submittedAt, upperMs) {
72
+ const isWithinUpperBound = (ev) => {
73
+ if (upperMs === null) return true;
74
+ const ms = parseTimestamp(ev.timestamp);
75
+ return ms !== null && ms < upperMs;
76
+ };
77
+ let contentMatch = null;
78
+ for (let i = sessionEvents.length - 1; i >= 0; i--) {
79
+ const ev = sessionEvents[i];
80
+ if (!isUserBoundary(ev)) continue;
81
+ if (!isWithinUpperBound(ev)) continue;
82
+ const text = extractUserContentText(ev.message);
83
+ if (text !== null && text === promptText) {
84
+ contentMatch = ev;
85
+ break;
86
+ }
87
+ }
88
+ if (contentMatch) return { boundary: contentMatch, usedFallback: false };
89
+ const submittedMs = parseTimestamp(submittedAt);
90
+ if (submittedMs === null) return { boundary: null, usedFallback: false };
91
+ for (let i = sessionEvents.length - 1; i >= 0; i--) {
92
+ const ev = sessionEvents[i];
93
+ if (!isUserBoundary(ev)) continue;
94
+ if (!isWithinUpperBound(ev)) continue;
95
+ const ms = parseTimestamp(ev.timestamp);
96
+ if (ms === null) continue;
97
+ if (ms >= submittedMs - BOUNDARY_TIMESTAMP_TOLERANCE_MS) {
98
+ return { boundary: ev, usedFallback: true };
99
+ }
100
+ }
101
+ return { boundary: null, usedFallback: false };
102
+ }
103
+ function asNumberOrNull(value) {
104
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
105
+ }
106
+ function asStringOrNull(value) {
107
+ return typeof value === "string" ? value : null;
108
+ }
109
+ function extractAssistantMessage(event) {
110
+ const message = event.message;
111
+ if (!message || typeof message !== "object") return null;
112
+ const msg = message;
113
+ if (msg.role !== "assistant" && msg.role !== void 0) {
114
+ }
115
+ const content = Array.isArray(msg.content) ? msg.content : [];
116
+ const usage = msg.usage && typeof msg.usage === "object" ? msg.usage : null;
117
+ return {
118
+ id: asStringOrNull(msg.id),
119
+ model: asStringOrNull(msg.model),
120
+ content,
121
+ usage
122
+ };
123
+ }
124
+ function usageIsComplete(usage) {
125
+ if (!usage) return false;
126
+ const hasInput = typeof usage.input_tokens === "number";
127
+ const hasOutput = typeof usage.output_tokens === "number";
128
+ const hasCacheRead = typeof usage.cache_read_input_tokens === "number";
129
+ return hasInput && hasOutput && hasCacheRead;
130
+ }
131
+ function buildModelCall(event, msg) {
132
+ const usage = msg.usage ?? {};
133
+ const cacheCreation = usage.cache_creation && typeof usage.cache_creation === "object" ? usage.cache_creation : null;
134
+ const has5m = cacheCreation && typeof cacheCreation.ephemeral_5m_input_tokens === "number";
135
+ const has1h = cacheCreation && typeof cacheCreation.ephemeral_1h_input_tokens === "number";
136
+ const cacheWrite5mTokens = has5m ? cacheCreation.ephemeral_5m_input_tokens : null;
137
+ const cacheWrite1hTokens = has1h ? cacheCreation.ephemeral_1h_input_tokens : null;
138
+ const splitAvailable = has5m || has1h;
139
+ const cacheWriteTokens = splitAvailable ? null : asNumberOrNull(usage.cache_creation_input_tokens);
140
+ return {
141
+ provider: "anthropic",
142
+ model: msg.model,
143
+ tier: asStringOrNull(usage.service_tier),
144
+ requestId: asStringOrNull(event.requestId),
145
+ timestamp: asStringOrNull(event.timestamp),
146
+ isSidechain: event.isSidechain === true,
147
+ inputTokens: asNumberOrNull(usage.input_tokens),
148
+ outputTokens: asNumberOrNull(usage.output_tokens),
149
+ cacheReadTokens: asNumberOrNull(usage.cache_read_input_tokens),
150
+ cacheWriteTokens,
151
+ cacheWrite5mTokens,
152
+ cacheWrite1hTokens,
153
+ reasoningTokens: null,
154
+ audioInputTokens: null,
155
+ imageInputTokens: null
156
+ };
157
+ }
158
+ function scanServerToolUses(content) {
159
+ const uses = [];
160
+ for (const block of content) {
161
+ if (!block || typeof block !== "object") continue;
162
+ const b = block;
163
+ const id = b.id;
164
+ if (typeof id !== "string" || !id.startsWith("srvtoolu_")) continue;
165
+ const name = typeof b.name === "string" ? b.name : "";
166
+ switch (name) {
167
+ case "web_search":
168
+ uses.push({ tool: "web_search", unit: "per_request", isKnown: true, id, source: "direct" });
169
+ break;
170
+ case "web_fetch":
171
+ uses.push({ tool: "web_fetch", unit: "per_request", isKnown: true, id, source: "direct" });
172
+ break;
173
+ case "code_execution":
174
+ uses.push({ tool: "code_execution", unit: "invocation", isKnown: true, id, source: "direct" });
175
+ break;
176
+ default:
177
+ uses.push({ tool: name || "unknown", unit: "invocation", isKnown: false, id, source: "direct" });
178
+ break;
179
+ }
180
+ }
181
+ return uses;
182
+ }
183
+ function buildClientToolNameMap(turnEvents) {
184
+ const map = /* @__PURE__ */ new Map();
185
+ for (const ev of turnEvents) {
186
+ if (ev.type !== "assistant") continue;
187
+ const msg = ev.message;
188
+ if (!msg || typeof msg !== "object") continue;
189
+ const content = msg.content;
190
+ if (!Array.isArray(content)) continue;
191
+ for (const block of content) {
192
+ if (!block || typeof block !== "object") continue;
193
+ const b = block;
194
+ if (b.type !== "tool_use") continue;
195
+ const id = b.id;
196
+ const name = b.name;
197
+ if (typeof id !== "string" || typeof name !== "string") continue;
198
+ if (!id.startsWith("toolu_")) continue;
199
+ map.set(id, name);
200
+ }
201
+ }
202
+ return map;
203
+ }
204
+ function scanEmbeddedServerToolUses(turnEvents, clientToolNames) {
205
+ const uses = [];
206
+ for (const ev of turnEvents) {
207
+ if (ev.type !== "user") continue;
208
+ const tur = ev.toolUseResult;
209
+ if (!tur || typeof tur !== "object") continue;
210
+ const results = tur.results;
211
+ if (!Array.isArray(results)) continue;
212
+ let parentToolName = "";
213
+ const userMsg = ev.message;
214
+ if (userMsg && typeof userMsg === "object") {
215
+ const userContent = userMsg.content;
216
+ if (Array.isArray(userContent)) {
217
+ for (const block of userContent) {
218
+ if (!block || typeof block !== "object") continue;
219
+ const b = block;
220
+ if (b.type !== "tool_result") continue;
221
+ const parentId = b.tool_use_id;
222
+ if (typeof parentId === "string") {
223
+ parentToolName = clientToolNames.get(parentId) ?? "";
224
+ break;
225
+ }
226
+ }
227
+ }
228
+ }
229
+ for (const entry of results) {
230
+ if (!entry || typeof entry !== "object") continue;
231
+ const srvId = entry.tool_use_id;
232
+ if (typeof srvId !== "string" || !srvId.startsWith("srvtoolu_")) continue;
233
+ if (parentToolName === "WebFetch") {
234
+ uses.push({ tool: "web_fetch", unit: "per_request", isKnown: true, id: srvId, source: "embedded" });
235
+ } else {
236
+ uses.push({ tool: "web_search", unit: "per_request", isKnown: true, id: srvId, source: "embedded" });
237
+ }
238
+ }
239
+ }
240
+ return uses;
241
+ }
242
+ function dedupeByServerToolId(records) {
243
+ const seen = /* @__PURE__ */ new Map();
244
+ for (const r of records) {
245
+ const existing = seen.get(r.id);
246
+ if (!existing) {
247
+ seen.set(r.id, r);
248
+ continue;
249
+ }
250
+ if (existing.source === "embedded" && r.source === "direct") {
251
+ seen.set(r.id, r);
252
+ }
253
+ }
254
+ return Array.from(seen.values());
255
+ }
256
+ function aggregateServerTools(uses) {
257
+ const map = /* @__PURE__ */ new Map();
258
+ let sawUnknown = false;
259
+ let sawEmbedded = false;
260
+ for (const use of uses) {
261
+ if (!use.isKnown) sawUnknown = true;
262
+ if (use.source === "embedded") sawEmbedded = true;
263
+ const key = `anthropic|${use.tool}|${use.unit}`;
264
+ const existing = map.get(key);
265
+ if (existing) {
266
+ existing.quantity += 1;
267
+ } else {
268
+ map.set(key, { provider: "anthropic", tool: use.tool, unit: use.unit, quantity: 1 });
269
+ }
270
+ }
271
+ return { serverTools: Array.from(map.values()), sawUnknown, sawEmbedded };
272
+ }
273
+ function sumCrossCheckCounts(usageBlocks) {
274
+ const totals = /* @__PURE__ */ new Map();
275
+ const keyFor = (raw) => {
276
+ if (raw === "web_search_requests") return "web_search";
277
+ if (raw === "web_fetch_requests") return "web_fetch";
278
+ return null;
279
+ };
280
+ for (const usage of usageBlocks) {
281
+ const stu = usage.server_tool_use;
282
+ if (!stu || typeof stu !== "object") continue;
283
+ for (const [rawKey, rawVal] of Object.entries(stu)) {
284
+ const mapped = keyFor(rawKey);
285
+ if (!mapped) continue;
286
+ if (typeof rawVal !== "number" || !Number.isFinite(rawVal)) continue;
287
+ totals.set(mapped, (totals.get(mapped) ?? 0) + rawVal);
288
+ }
289
+ }
290
+ return totals;
291
+ }
292
+ function primaryToolCounts(serverTools) {
293
+ const map = /* @__PURE__ */ new Map();
294
+ for (const entry of serverTools) {
295
+ map.set(entry.tool, (map.get(entry.tool) ?? 0) + entry.quantity);
296
+ }
297
+ return map;
298
+ }
299
+ function resolveVersion(events) {
300
+ for (const ev of events) {
301
+ if (typeof ev.version === "string" && ev.version.trim()) return ev.version.trim();
302
+ }
303
+ return null;
304
+ }
305
+ function buildTurnUsage(args) {
306
+ const assistantEvents = args.turnEvents.filter((ev) => ev.type === "assistant");
307
+ if (assistantEvents.length === 0) {
308
+ return { ok: false, reason: "no_messages_for_turn" };
309
+ }
310
+ const warnings = [...args.initialWarnings];
311
+ const calls = [];
312
+ const usageBlocks = [];
313
+ let anyIncomplete = false;
314
+ let anyComplete = false;
315
+ let sawLumpSumFallback = false;
316
+ const collectedServerToolUses = [];
317
+ const mainModels = /* @__PURE__ */ new Set();
318
+ const sidechainModels = /* @__PURE__ */ new Set();
319
+ for (const ev of assistantEvents) {
320
+ const msg = extractAssistantMessage(ev);
321
+ if (!msg) continue;
322
+ if (usageIsComplete(msg.usage)) {
323
+ anyComplete = true;
324
+ } else {
325
+ anyIncomplete = true;
326
+ }
327
+ if (msg.usage) usageBlocks.push(msg.usage);
328
+ const call = buildModelCall(ev, msg);
329
+ calls.push(call);
330
+ if (call.cacheWrite5mTokens === null && call.cacheWrite1hTokens === null && call.cacheWriteTokens !== null) {
331
+ sawLumpSumFallback = true;
332
+ }
333
+ collectedServerToolUses.push(...scanServerToolUses(msg.content));
334
+ if (call.model) {
335
+ if (call.isSidechain) sidechainModels.add(call.model);
336
+ else mainModels.add(call.model);
337
+ }
338
+ }
339
+ if (calls.length === 0) {
340
+ return { ok: false, reason: "no_messages_for_turn" };
341
+ }
342
+ if (sawLumpSumFallback) {
343
+ warnings.push({
344
+ code: "cache_split_unavailable",
345
+ message: "Assistant message reported lump-sum cache_creation_input_tokens without the 5m/1h split."
346
+ });
347
+ }
348
+ const clientToolNames = buildClientToolNameMap(args.turnEvents);
349
+ const embeddedUses = scanEmbeddedServerToolUses(args.turnEvents, clientToolNames);
350
+ const merged = dedupeByServerToolId([...collectedServerToolUses, ...embeddedUses]);
351
+ const { serverTools, sawUnknown, sawEmbedded } = aggregateServerTools(merged);
352
+ if (sawUnknown) {
353
+ warnings.push({
354
+ code: "unknown_server_tool",
355
+ message: "Encountered a server tool whose name is not in the known list (web_search, web_fetch, code_execution)."
356
+ });
357
+ }
358
+ const crossCheck = sumCrossCheckCounts(usageBlocks);
359
+ const primary = primaryToolCounts(serverTools);
360
+ const allTools = /* @__PURE__ */ new Set([...crossCheck.keys(), ...primary.keys()]);
361
+ for (const tool of allTools) {
362
+ const crossVal = crossCheck.get(tool) ?? 0;
363
+ const primVal = primary.get(tool) ?? 0;
364
+ if (crossVal === primVal) continue;
365
+ if (sawEmbedded && crossVal === 0) continue;
366
+ warnings.push({
367
+ code: "server_tool_count_mismatch",
368
+ message: `Server-tool ${tool} count mismatch: srvtoolu_ scan=${primVal}, usage.server_tool_use=${crossVal}. Trusting srvtoolu_ count.`
369
+ });
370
+ break;
371
+ }
372
+ const subagentMismatch = mainModels.size > 0 && sidechainModels.size > 0 && [...sidechainModels].some((m) => !mainModels.has(m));
373
+ if (subagentMismatch) {
374
+ warnings.push({
375
+ code: "subagent_model_differs",
376
+ message: "At least one sidechain ModelCall uses a model different from the main-chain model in this turn."
377
+ });
378
+ }
379
+ let confidence;
380
+ if (!anyComplete && anyIncomplete) confidence = "unknown";
381
+ else if (anyIncomplete) confidence = "partial";
382
+ else if (anyComplete) confidence = "exact";
383
+ else confidence = "unknown";
384
+ if (args.checkSubsequentEvent) {
385
+ const lastAssistantMs = (() => {
386
+ const msList = assistantEvents.map((ev) => parseTimestamp(ev.timestamp)).filter((ms) => ms !== null);
387
+ return msList.length ? Math.max(...msList) : null;
388
+ })();
389
+ const hasSubsequent = lastAssistantMs !== null && args.sessionEvents.some((ev) => {
390
+ const ms = parseTimestamp(ev.timestamp);
391
+ if (ms === null || ms <= lastAssistantMs) return false;
392
+ if (args.upperMs !== null && ms >= args.upperMs) return false;
393
+ return true;
394
+ });
395
+ if (!hasSubsequent && confidence === "exact") {
396
+ confidence = "partial";
397
+ warnings.push({
398
+ code: "transcript_truncated",
399
+ message: "Previous turn has no subsequent event after its last assistant message; transcript may be truncated."
400
+ });
401
+ }
402
+ }
403
+ const resolvedVersion = args.agent.version ?? resolveVersion(args.turnEvents) ?? resolveVersion(args.sessionEvents);
404
+ const usage = {
405
+ schemaVersion: 1,
406
+ capturedAt: args.capturedAt,
407
+ captureSource: args.captureSource,
408
+ confidence,
409
+ agent: {
410
+ name: args.agent.name,
411
+ version: resolvedVersion,
412
+ sessionId: args.agent.sessionId,
413
+ turnId: args.agent.turnId,
414
+ plan: args.agent.plan
415
+ },
416
+ calls,
417
+ serverTools,
418
+ warnings,
419
+ extensions: args.extensions
420
+ };
421
+ return { ok: true, usage };
422
+ }
423
+ function harvestClaudeCodeUsage(input) {
424
+ const sessionEvents = input.events.filter((ev) => ev.sessionId === input.sessionId);
425
+ const upperMs = input.nextBoundaryAt ? parseTimestamp(input.nextBoundaryAt) : null;
426
+ const { boundary, usedFallback } = findBoundary(
427
+ sessionEvents,
428
+ input.promptText,
429
+ input.submittedAt,
430
+ upperMs
431
+ );
432
+ if (!boundary) {
433
+ return { ok: false, reason: "no_user_boundary_found" };
434
+ }
435
+ const boundaryMs = parseTimestamp(boundary.timestamp);
436
+ if (boundaryMs === null) {
437
+ return { ok: false, reason: "no_user_boundary_found" };
438
+ }
439
+ const turnEvents = sessionEvents.filter((ev) => {
440
+ const ms = parseTimestamp(ev.timestamp);
441
+ if (ms === null || ms <= boundaryMs) return false;
442
+ if (upperMs !== null && ms >= upperMs) return false;
443
+ return true;
444
+ });
445
+ const initialWarnings = [];
446
+ if (usedFallback) {
447
+ initialWarnings.push({
448
+ code: "transcript_truncated",
449
+ message: "Prompt-text equality match failed; used timestamp-tolerance fallback to locate the user boundary."
450
+ });
451
+ }
452
+ const built = buildTurnUsage({
453
+ sessionEvents,
454
+ turnEvents,
455
+ upperMs,
456
+ initialWarnings,
457
+ agent: input.agent,
458
+ capturedAt: input.capturedAt,
459
+ captureSource: "hook",
460
+ extensions: input.extensions,
461
+ checkSubsequentEvent: input.checkSubsequentEvent === true
462
+ });
463
+ if (!built.ok) return built;
464
+ return {
465
+ ok: true,
466
+ usage: built.usage,
467
+ boundaryAt: typeof boundary.timestamp === "string" ? boundary.timestamp : null
468
+ };
469
+ }
470
+ function sliceTranscriptIntoTurns(input) {
471
+ const sessionEvents = input.events.filter((ev) => ev.sessionId === input.sessionId);
472
+ const boundaries = sessionEvents.filter(isUserBoundary);
473
+ const result = [];
474
+ for (let i = 0; i < boundaries.length; i++) {
475
+ const boundary = boundaries[i];
476
+ const nextBoundary = boundaries[i + 1] ?? null;
477
+ const boundaryMs = parseTimestamp(boundary.timestamp);
478
+ if (boundaryMs === null) continue;
479
+ const occurredAt = asStringOrNull(boundary.timestamp);
480
+ if (occurredAt === null) continue;
481
+ const upperMs = nextBoundary ? parseTimestamp(nextBoundary.timestamp) : null;
482
+ const turnEvents = sessionEvents.filter((ev) => {
483
+ const ms = parseTimestamp(ev.timestamp);
484
+ if (ms === null || ms <= boundaryMs) return false;
485
+ if (upperMs !== null && ms >= upperMs) return false;
486
+ return true;
487
+ });
488
+ const promptId = asStringOrNull(boundary.promptId);
489
+ const promptText = extractUserContentText(boundary.message);
490
+ const assistantText = extractAssistantContentText(turnEvents);
491
+ const gitBranch = asStringOrNull(boundary.gitBranch);
492
+ const cwd = asStringOrNull(boundary.cwd);
493
+ const built = buildTurnUsage({
494
+ sessionEvents,
495
+ turnEvents,
496
+ upperMs,
497
+ initialWarnings: [],
498
+ agent: {
499
+ name: "claude-code",
500
+ version: null,
501
+ sessionId: input.sessionId,
502
+ turnId: promptId,
503
+ plan: null
504
+ },
505
+ capturedAt: input.capturedAt,
506
+ captureSource: "historical_import",
507
+ extensions: input.extensions ?? null,
508
+ checkSubsequentEvent: false
509
+ });
510
+ if (!built.ok) continue;
511
+ result.push({
512
+ sessionId: input.sessionId,
513
+ promptId,
514
+ promptText,
515
+ assistantText,
516
+ occurredAt,
517
+ gitBranch,
518
+ cwd,
519
+ usage: built.usage
520
+ });
521
+ }
522
+ return result;
523
+ }
524
+ export {
525
+ HARVESTER_WARNING_CODES,
526
+ harvestClaudeCodeUsage,
527
+ readAndParseTranscript,
528
+ sliceTranscriptIntoTurns
529
+ };
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export { S as SessionStore, a as StoredSession, c as createStoredSessionTokenPro
4
4
  export { createLocalSessionStore, createSupabaseAuthHelpers } from './auth.js';
5
5
  export { AgentMemoryItem, AgentMemoryKind, AgentMemorySearchItem, AgentMemorySearchResponse, AgentMemorySummary, AgentMemoryTimelineResponse, ApiClient, AppContext, AppContextAccessPath, AppReconcileResponse, Bundle, BundlePlatform, BundleStatus, ChangeStepDiffResponse, InitiateBundleRequest, InvitationRecord, MergeRequest, MergeRequestReview, MergeRequestStatus, ReconcilePreflightResponse, SyncLocalResponse, SyncUpstreamResponse, createApiClient } from './api.js';
6
6
  import 'zod';
7
- import './contracts-NbV3P_Rl.js';
7
+ import './contracts-CHmD-fMj.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remixhq/core",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Remix core library",
5
5
  "homepage": "https://github.com/RemixDotOne/remix-core",
6
6
  "license": "MIT",
@@ -46,6 +46,10 @@
46
46
  "types": "./dist/errors.d.ts",
47
47
  "import": "./dist/errors.js"
48
48
  },
49
+ "./history": {
50
+ "types": "./dist/history.d.ts",
51
+ "import": "./dist/history.js"
52
+ },
49
53
  "./repo": {
50
54
  "types": "./dist/repo.d.ts",
51
55
  "import": "./dist/repo.js"
@@ -55,7 +59,7 @@
55
59
  "dist"
56
60
  ],
57
61
  "scripts": {
58
- "build": "tsup src/index.ts src/api.ts src/auth.ts src/binding.ts src/collab.ts src/config.ts src/errors.ts src/repo.ts --dts --format esm",
62
+ "build": "tsup src/index.ts src/api.ts src/auth.ts src/binding.ts src/collab.ts src/config.ts src/errors.ts src/history.ts src/repo.ts --dts --format esm --clean",
59
63
  "typecheck": "tsc -p tsconfig.json --noEmit",
60
64
  "test": "vitest run",
61
65
  "prepack": "npm run build"
File without changes