remote-codex 0.11.25 → 0.11.27

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.
@@ -45,10 +45,12 @@ import {
45
45
  hiddenInitPrompt,
46
46
  isHiddenContinuationMessage,
47
47
  isHiddenInitMessage,
48
+ limitErrorFromHistoryItems,
48
49
  partialReasoningDelta,
49
50
  partialTextDelta,
50
51
  resultForToolUse,
51
52
  suppressedClaudeToolUseIds,
53
+ taskNotificationToolResult,
52
54
  toolUseFromPartialStart,
53
55
  toolUseToHistoryItem,
54
56
  toolResultBlocks,
@@ -748,6 +750,14 @@ function queryResultError(message: SDKMessage): string | null {
748
750
  return message.errors?.join('\n') || message.stop_reason || 'Claude turn failed.';
749
751
  }
750
752
 
753
+ function statusForHistoricalItems(items: AgentHistoryItem[]) {
754
+ const limitError = limitErrorFromHistoryItems(items);
755
+ return {
756
+ status: limitError ? ('failed' as const) : ('completed' as const),
757
+ error: limitError,
758
+ };
759
+ }
760
+
751
761
  function assistantMessagePayload(message: SDKMessage) {
752
762
  return message.type === 'assistant' ? message.message : null;
753
763
  }
@@ -863,6 +873,10 @@ function streamMessageId(event: unknown) {
863
873
  return messageIdFromPayload(message);
864
874
  }
865
875
 
876
+ function sessionMessageTimestamp(message: SessionMessage) {
877
+ return typeof message.uuid === 'string' ? isoFromUuidV7(message.uuid) : null;
878
+ }
879
+
866
880
  function addOrUpdateItem(state: ActiveClaudeTurn, item: AgentHistoryItem) {
867
881
  if (!state.items.has(item.id)) {
868
882
  state.itemOrder.push(item.id);
@@ -876,6 +890,68 @@ function orderedItems(state: ActiveClaudeTurn) {
876
890
  .filter((item): item is AgentHistoryItem => Boolean(item));
877
891
  }
878
892
 
893
+ function withHistoryItemCreatedAt<T extends AgentHistoryItem>(
894
+ item: T,
895
+ createdAt: string | null | undefined,
896
+ ): T {
897
+ if (item.createdAt || !createdAt) {
898
+ return item;
899
+ }
900
+ return { ...item, createdAt };
901
+ }
902
+
903
+ function latestReasoningItem(state: ActiveClaudeTurn) {
904
+ const lastItemId = state.itemOrder.at(-1);
905
+ const lastItem = lastItemId ? state.items.get(lastItemId) : null;
906
+ return lastItem?.kind === 'reasoning' ? lastItem : null;
907
+ }
908
+
909
+ function joinReasoningText(left: string, right: string) {
910
+ if (!left) {
911
+ return right;
912
+ }
913
+ if (!right) {
914
+ return left;
915
+ }
916
+ if (right.startsWith(left)) {
917
+ return right;
918
+ }
919
+ if (left.endsWith(right)) {
920
+ return left;
921
+ }
922
+ return `${left}\n\n${right}`;
923
+ }
924
+
925
+ function isRunningHistoryItemStatus(status: string | null | undefined) {
926
+ const normalized = status?.trim().toLowerCase();
927
+ return (
928
+ normalized === 'running' ||
929
+ normalized === 'pending' ||
930
+ normalized === 'in_progress' ||
931
+ normalized === 'in progress'
932
+ );
933
+ }
934
+
935
+ function finalizeTurnItems(
936
+ state: ActiveClaudeTurn,
937
+ status: AgentTurn['status'],
938
+ completedAt: string,
939
+ ) {
940
+ return orderedItems(state).map((item) => {
941
+ if (item.kind === 'userMessage') {
942
+ return item;
943
+ }
944
+ if (status === 'completed' && isRunningHistoryItemStatus(item.status)) {
945
+ return {
946
+ ...item,
947
+ status: 'completed',
948
+ createdAt: item.createdAt ?? completedAt,
949
+ };
950
+ }
951
+ return withHistoryItemCreatedAt(item, completedAt);
952
+ });
953
+ }
954
+
879
955
  function stringFromRecord(value: Record<string, unknown>, key: string) {
880
956
  const raw = value[key];
881
957
  return typeof raw === 'string' && raw.trim() ? raw : null;
@@ -1235,7 +1311,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1235
1311
  const sessions = await this.withClaudeConfigEnv(() => this.listSessionsFn({} as ListSessionsOptions));
1236
1312
  return sessions.map((session) => {
1237
1313
  this.knownSessionIds.add(session.sessionId);
1238
- return sessionSummaryFromInfo(session);
1314
+ const summary = sessionSummaryFromInfo(session);
1315
+ const activeTurn = this.activeTurnForSession(summary.providerSessionId);
1316
+ return activeTurn
1317
+ ? {
1318
+ ...summary,
1319
+ status: 'running' as const,
1320
+ updatedAt: summary.updatedAt ?? activeTurn.startedAt,
1321
+ }
1322
+ : summary;
1239
1323
  });
1240
1324
  }
1241
1325
 
@@ -1418,9 +1502,12 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1418
1502
  tools: { type: 'preset', preset: 'claude_code' },
1419
1503
  }),
1420
1504
  });
1421
- const userItem = userMessageToHistoryItem(`${providerTurnId}:user`, {
1422
- content: input.prompt,
1423
- });
1505
+ const userItem = withHistoryItemCreatedAt(
1506
+ userMessageToHistoryItem(`${providerTurnId}:user`, {
1507
+ content: input.prompt,
1508
+ }),
1509
+ startedAt,
1510
+ );
1424
1511
  const initialItems = input.hidden ? [] : [userItem];
1425
1512
  const state: ActiveClaudeTurn = {
1426
1513
  providerSessionId: input.providerSessionId,
@@ -1511,6 +1598,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1511
1598
  }
1512
1599
  }
1513
1600
 
1601
+ private activeTurnForSession(providerSessionId: string) {
1602
+ for (const state of this.activeTurns.values()) {
1603
+ if (state.providerSessionId === providerSessionId && !state.completed) {
1604
+ return state;
1605
+ }
1606
+ }
1607
+ return null;
1608
+ }
1609
+
1514
1610
  private reconcileActiveTranscriptTurn(
1515
1611
  providerSessionId: string,
1516
1612
  turns: AgentTurn[],
@@ -1594,31 +1690,16 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1594
1690
 
1595
1691
  private async consumeQuery(state: ActiveClaudeTurn) {
1596
1692
  const rawMessages: SDKMessage[] = [];
1693
+ let terminalStatus: AgentTurn['status'] | null = null;
1694
+ let terminalError: string | null = null;
1597
1695
  try {
1598
1696
  for await (const message of state.query) {
1599
1697
  rawMessages.push(message);
1600
- if (state.completed) {
1601
- continue;
1602
- }
1603
1698
  this.consumeMessage(state, message);
1604
1699
  const status = queryResultStatus(message);
1605
1700
  if (status) {
1606
- state.completed = true;
1607
- this.deleteActiveTurn(state);
1608
- this.emitUsage(state);
1609
- this.emitRuntimeEvent({
1610
- type: 'turn.completed',
1611
- provider: 'claude',
1612
- providerSessionId: state.providerSessionId,
1613
- turn: buildAgentTurn({
1614
- providerTurnId: state.providerTurnId,
1615
- startedAt: state.startedAt,
1616
- status: state.interrupted ? 'interrupted' : status,
1617
- error: queryResultError(message),
1618
- items: orderedItems(state),
1619
- rawTurn: rawMessages,
1620
- }),
1621
- });
1701
+ terminalStatus = status;
1702
+ terminalError = queryResultError(message);
1622
1703
  }
1623
1704
  }
1624
1705
 
@@ -1626,6 +1707,13 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1626
1707
  state.completed = true;
1627
1708
  this.deleteActiveTurn(state);
1628
1709
  this.emitUsage(state);
1710
+ const completedAt = new Date().toISOString();
1711
+ const limitError = limitErrorFromHistoryItems(orderedItems(state));
1712
+ const status = state.interrupted
1713
+ ? 'interrupted'
1714
+ : limitError
1715
+ ? 'failed'
1716
+ : terminalStatus ?? 'completed';
1629
1717
  this.emitRuntimeEvent({
1630
1718
  type: 'turn.completed',
1631
1719
  provider: 'claude',
@@ -1633,8 +1721,9 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1633
1721
  turn: buildAgentTurn({
1634
1722
  providerTurnId: state.providerTurnId,
1635
1723
  startedAt: state.startedAt,
1636
- status: state.interrupted ? 'interrupted' : 'completed',
1637
- items: orderedItems(state),
1724
+ status,
1725
+ error: limitError ?? terminalError,
1726
+ items: finalizeTurnItems(state, status, completedAt),
1638
1727
  rawTurn: rawMessages,
1639
1728
  }),
1640
1729
  });
@@ -1656,6 +1745,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1656
1745
 
1657
1746
  private consumeMessage(state: ActiveClaudeTurn, message: SDKMessage) {
1658
1747
  this.captureUsage(state, message);
1748
+ const messageCreatedAt = new Date().toISOString();
1659
1749
 
1660
1750
  if (message.type === 'system' && message.subtype === 'init') {
1661
1751
  this.updateToolboxItemsFromSystemInit(message);
@@ -1677,8 +1767,9 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1677
1767
  event: message.event,
1678
1768
  });
1679
1769
  if (toolItem) {
1680
- addOrUpdateItem(state, toolItem);
1681
- this.emitItem(state, toolItem, 'item.started');
1770
+ const nextItem = withHistoryItemCreatedAt(toolItem, messageCreatedAt);
1771
+ addOrUpdateItem(state, nextItem);
1772
+ this.emitItem(state, nextItem, 'item.started');
1682
1773
  return;
1683
1774
  }
1684
1775
 
@@ -1688,16 +1779,22 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1688
1779
  });
1689
1780
  if (reasoningItem) {
1690
1781
  const existing = state.items.get(reasoningItem.id);
1691
- const nextItem: AgentHistoryItem = existing?.kind === 'reasoning'
1782
+ const previousReasoning = existing?.kind === 'reasoning'
1783
+ ? existing
1784
+ : latestReasoningItem(state);
1785
+ const nextItem: AgentHistoryItem = previousReasoning
1692
1786
  ? {
1693
- ...existing,
1694
- text: `${existing.text}${reasoningItem.text}`,
1695
- status: reasoningItem.status ?? existing.status ?? null,
1787
+ ...previousReasoning,
1788
+ text:
1789
+ previousReasoning.id === reasoningItem.id
1790
+ ? `${previousReasoning.text}${reasoningItem.text}`
1791
+ : joinReasoningText(previousReasoning.text, reasoningItem.text),
1792
+ status: reasoningItem.status ?? previousReasoning.status ?? null,
1696
1793
  }
1697
- : reasoningItem;
1794
+ : withHistoryItemCreatedAt(reasoningItem, messageCreatedAt);
1698
1795
  addOrUpdateItem(state, nextItem);
1699
- this.emitItem(state, nextItem, existing ? 'item.completed' : 'item.started', {
1700
- force: Boolean(existing),
1796
+ this.emitItem(state, nextItem, previousReasoning ? 'item.completed' : 'item.started', {
1797
+ force: Boolean(previousReasoning),
1701
1798
  });
1702
1799
  return;
1703
1800
  }
@@ -1715,6 +1812,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1715
1812
  })
1716
1813
  : markTransientAgentHistoryItem({
1717
1814
  id: delta.itemId,
1815
+ createdAt: messageCreatedAt,
1718
1816
  kind: 'agentMessage',
1719
1817
  text: delta.delta,
1720
1818
  });
@@ -1732,6 +1830,19 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1732
1830
  }
1733
1831
 
1734
1832
  if (message.type === 'user') {
1833
+ const taskNotification = taskNotificationToolResult(message.message);
1834
+ if (taskNotification && !state.suppressedToolUseIds.has(taskNotification.toolUseId)) {
1835
+ const item = resultForToolUse({
1836
+ toolUseId: taskNotification.toolUseId,
1837
+ result: message.tool_use_result ?? taskNotification.result,
1838
+ previous: state.items.get(taskNotification.toolUseId) ?? null,
1839
+ });
1840
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1841
+ addOrUpdateItem(state, nextItem);
1842
+ this.emitItem(state, nextItem, 'item.completed');
1843
+ return;
1844
+ }
1845
+
1735
1846
  const rawToolResults = toolResultBlocks(message.message);
1736
1847
  const toolResults = rawToolResults.filter(
1737
1848
  (toolResult) => !state.suppressedToolUseIds.has(toolResult.toolUseId),
@@ -1743,8 +1854,9 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1743
1854
  result: message.tool_use_result ?? toolResult.result,
1744
1855
  previous: state.items.get(toolResult.toolUseId) ?? null,
1745
1856
  });
1746
- addOrUpdateItem(state, item);
1747
- this.emitItem(state, item, 'item.completed');
1857
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1858
+ addOrUpdateItem(state, nextItem);
1859
+ this.emitItem(state, nextItem, 'item.completed');
1748
1860
  }
1749
1861
  return;
1750
1862
  }
@@ -1776,12 +1888,13 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1776
1888
  messageId: assistantMessageId,
1777
1889
  message: payload,
1778
1890
  })) {
1779
- const existing = state.items.get(item.id);
1780
- addOrUpdateItem(state, item);
1781
- if (item.kind !== 'agentMessage' && !existing) {
1782
- this.emitItem(state, item, 'item.started');
1783
- } else if (item.kind !== 'agentMessage' && existing) {
1784
- this.emitItem(state, item, 'item.started', { force: true });
1891
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1892
+ const existing = state.items.get(nextItem.id);
1893
+ addOrUpdateItem(state, nextItem);
1894
+ if (nextItem.kind !== 'agentMessage' && !existing) {
1895
+ this.emitItem(state, nextItem, 'item.started');
1896
+ } else if (nextItem.kind !== 'agentMessage' && existing) {
1897
+ this.emitItem(state, nextItem, 'item.started', { force: true });
1785
1898
  }
1786
1899
  }
1787
1900
  return;
@@ -1796,8 +1909,9 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1796
1909
  result: message.tool_use_result ?? message.message,
1797
1910
  previous: state.items.get(message.parent_tool_use_id) ?? null,
1798
1911
  });
1799
- addOrUpdateItem(state, item);
1800
- this.emitItem(state, item, 'item.completed');
1912
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1913
+ addOrUpdateItem(state, nextItem);
1914
+ this.emitItem(state, nextItem, 'item.completed');
1801
1915
  return;
1802
1916
  }
1803
1917
 
@@ -1817,8 +1931,9 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1817
1931
  status: 'running',
1818
1932
  });
1819
1933
  if (item) {
1820
- addOrUpdateItem(state, item);
1821
- this.emitItem(state, item, 'item.started');
1934
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1935
+ addOrUpdateItem(state, nextItem);
1936
+ this.emitItem(state, nextItem, 'item.started');
1822
1937
  } else {
1823
1938
  state.suppressedToolUseIds.add(toolUseId);
1824
1939
  }
@@ -1840,13 +1955,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1840
1955
  }
1841
1956
  : {
1842
1957
  id: toolUseId,
1958
+ createdAt: messageCreatedAt,
1843
1959
  kind: 'toolCall',
1844
1960
  text: `${message.tool_name} denied`,
1845
1961
  detailText: typeof message.message === 'string' ? message.message : null,
1846
1962
  status: 'denied',
1847
1963
  };
1848
- addOrUpdateItem(state, item);
1849
- this.emitItem(state, item, 'item.completed');
1964
+ const nextItem = withHistoryItemCreatedAt(item, messageCreatedAt);
1965
+ addOrUpdateItem(state, nextItem);
1966
+ this.emitItem(state, nextItem, 'item.completed');
1850
1967
  }
1851
1968
  }
1852
1969
 
@@ -2076,6 +2193,18 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2076
2193
  itemsById: new Map(),
2077
2194
  };
2078
2195
  }
2196
+ const previous = current.items.at(-1);
2197
+ if (item.kind === 'reasoning' && previous?.kind === 'reasoning') {
2198
+ const merged = {
2199
+ ...previous,
2200
+ text: joinReasoningText(previous.text, item.text),
2201
+ status: item.status ?? previous.status ?? null,
2202
+ };
2203
+ current.items[current.items.length - 1] = merged;
2204
+ current.itemsById.set(previous.id, merged);
2205
+ current.itemsById.set(item.id, merged);
2206
+ return;
2207
+ }
2079
2208
  const existingIndex = current.items.findIndex((entry) => entry.id === item.id);
2080
2209
  if (existingIndex >= 0) {
2081
2210
  current.items[existingIndex] = item;
@@ -2087,6 +2216,25 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2087
2216
 
2088
2217
  for (const message of messages) {
2089
2218
  if (message.type === 'user') {
2219
+ const taskNotification = taskNotificationToolResult(message.message);
2220
+ if (taskNotification) {
2221
+ if (suppressedToolUseIds.has(taskNotification.toolUseId)) {
2222
+ continue;
2223
+ }
2224
+ const previous = current?.itemsById.get(taskNotification.toolUseId) ?? null;
2225
+ upsertCurrentItem(
2226
+ withHistoryItemCreatedAt(
2227
+ resultForToolUse({
2228
+ toolUseId: taskNotification.toolUseId,
2229
+ result: message.tool_use_result ?? taskNotification.result,
2230
+ previous,
2231
+ }),
2232
+ sessionMessageTimestamp(message) ?? current?.startedAt,
2233
+ ),
2234
+ );
2235
+ continue;
2236
+ }
2237
+
2090
2238
  const rawToolResults = toolResultBlocks(message.message);
2091
2239
  const toolResults = rawToolResults.filter(
2092
2240
  (toolResult) => !suppressedToolUseIds.has(toolResult.toolUseId),
@@ -2094,11 +2242,16 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2094
2242
  if (toolResults.length > 0) {
2095
2243
  for (const toolResult of toolResults) {
2096
2244
  const previous = current?.itemsById.get(toolResult.toolUseId) ?? null;
2097
- upsertCurrentItem(resultForToolUse({
2098
- toolUseId: toolResult.toolUseId,
2099
- result: toolResult.result,
2100
- previous,
2101
- }));
2245
+ upsertCurrentItem(
2246
+ withHistoryItemCreatedAt(
2247
+ resultForToolUse({
2248
+ toolUseId: toolResult.toolUseId,
2249
+ result: toolResult.result,
2250
+ previous,
2251
+ }),
2252
+ sessionMessageTimestamp(message) ?? current?.startedAt,
2253
+ ),
2254
+ );
2102
2255
  }
2103
2256
  continue;
2104
2257
  }
@@ -2118,24 +2271,28 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2118
2271
  }
2119
2272
  skippingHiddenInit = false;
2120
2273
  if (current && current.items.length > 0) {
2274
+ const outcome = statusForHistoricalItems(current.items);
2121
2275
  turns.push(buildAgentTurn({
2122
2276
  providerTurnId: current.providerTurnId,
2123
2277
  startedAt: current.startedAt,
2124
- status: 'completed',
2278
+ status: outcome.status,
2279
+ error: outcome.error,
2125
2280
  items: current.items,
2126
2281
  }));
2127
2282
  }
2128
2283
  const messageUuid = message.uuid ?? randomUUID();
2284
+ const userStartedAt = isoFromUuidV7(messageUuid);
2129
2285
  const userItem = await this.userMessageToHistoryItem(
2130
2286
  messageUuid,
2131
2287
  message.message,
2132
2288
  context,
2133
2289
  );
2290
+ const stampedUserItem = withHistoryItemCreatedAt(userItem, userStartedAt);
2134
2291
  current = {
2135
2292
  providerTurnId: `claude-turn-${messageUuid}`,
2136
- startedAt: isoFromUuidV7(messageUuid),
2137
- items: [userItem],
2138
- itemsById: new Map([[messageUuid, userItem]]),
2293
+ startedAt: userStartedAt,
2294
+ items: [stampedUserItem],
2295
+ itemsById: new Map([[messageUuid, stampedUserItem]]),
2139
2296
  };
2140
2297
  continue;
2141
2298
  }
@@ -2145,6 +2302,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2145
2302
  }
2146
2303
 
2147
2304
  if (message.type === 'assistant') {
2305
+ const assistantCreatedAt = sessionMessageTimestamp(message) ?? current?.startedAt;
2148
2306
  for (const toolUseId of suppressedClaudeToolUseIds(message.message)) {
2149
2307
  suppressedToolUseIds.add(toolUseId);
2150
2308
  current?.itemsById.delete(toolUseId);
@@ -2153,7 +2311,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2153
2311
  messageId: message.uuid ?? randomUUID(),
2154
2312
  message: message.message,
2155
2313
  })) {
2156
- upsertCurrentItem(item);
2314
+ upsertCurrentItem(withHistoryItemCreatedAt(item, assistantCreatedAt));
2157
2315
  }
2158
2316
  continue;
2159
2317
  }
@@ -2163,21 +2321,28 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
2163
2321
  continue;
2164
2322
  }
2165
2323
  const previous = current?.itemsById.get(message.parent_tool_use_id) ?? null;
2166
- upsertCurrentItem(resultForToolUse({
2167
- toolUseId: message.parent_tool_use_id,
2168
- result: isRecord(message.message) && 'content' in message.message
2169
- ? message.message.content
2170
- : message.message,
2171
- previous,
2172
- }));
2324
+ upsertCurrentItem(
2325
+ withHistoryItemCreatedAt(
2326
+ resultForToolUse({
2327
+ toolUseId: message.parent_tool_use_id,
2328
+ result: isRecord(message.message) && 'content' in message.message
2329
+ ? message.message.content
2330
+ : message.message,
2331
+ previous,
2332
+ }),
2333
+ sessionMessageTimestamp(message) ?? current?.startedAt,
2334
+ ),
2335
+ );
2173
2336
  }
2174
2337
  }
2175
2338
 
2176
2339
  if (current && current.items.length > 0) {
2340
+ const outcome = statusForHistoricalItems(current.items);
2177
2341
  turns.push(buildAgentTurn({
2178
2342
  providerTurnId: current.providerTurnId,
2179
2343
  startedAt: current.startedAt,
2180
- status: 'completed',
2344
+ status: outcome.status,
2345
+ error: outcome.error,
2181
2346
  items: current.items,
2182
2347
  }));
2183
2348
  }
@@ -509,6 +509,7 @@ export function shouldPersistLiveHistoryItem(item: ThreadHistoryItemDto) {
509
509
  item.kind === 'agentToolCall' ||
510
510
  item.kind === 'skillToolCall' ||
511
511
  item.kind === 'toolCall' ||
512
+ item.kind === 'reasoning' ||
512
513
  item.kind === 'webSearch'
513
514
  );
514
515
  }
@@ -71,9 +71,9 @@ describe('modelPricing', () => {
71
71
  expect(estimate?.totalUsd).toBeCloseTo(0.125625, 10);
72
72
  });
73
73
 
74
- it('prices Claude Sonnet and its 1M context option from the local pricing config', () => {
74
+ it('prices current Claude Sonnet aliases from the local pricing config', () => {
75
75
  expect(supportsFastMode('sonnet')).toBe(false);
76
- expect(contextWindowForModel('sonnet')).toBe(200000);
76
+ expect(contextWindowForModel('sonnet')).toBe(1000000);
77
77
  expect(contextWindowForModel('sonnet[1m]')).toBe(1000000);
78
78
 
79
79
  const standardEstimate = estimateTurnPrice(sampleUsage, {
@@ -83,9 +83,9 @@ describe('modelPricing', () => {
83
83
  expect(standardEstimate).toMatchObject({
84
84
  pricingModelKey: 'sonnet',
85
85
  pricingTierKey: 'standard',
86
- inputUsd: 0.003,
87
- cachedInputUsd: 0.00015,
88
- outputUsd: 0.0225,
86
+ inputUsd: 0.002,
87
+ cachedInputUsd: 0.0001,
88
+ outputUsd: 0.015,
89
89
  });
90
90
 
91
91
  const oneMillionEstimate = estimateTurnPrice(sampleUsage, {
@@ -95,22 +95,35 @@ describe('modelPricing', () => {
95
95
  expect(oneMillionEstimate).toMatchObject({
96
96
  pricingModelKey: 'sonnet[1m]',
97
97
  pricingTierKey: 'standard',
98
- inputUsd: 0.006,
99
- cachedInputUsd: 0.0003,
100
- outputUsd: 0.03375,
98
+ inputUsd: 0.002,
99
+ cachedInputUsd: 0.0001,
100
+ outputUsd: 0.015,
101
101
  });
102
102
  });
103
103
 
104
- it('prices current Claude Opus and Haiku aliases from the local pricing config', () => {
105
- expect(contextWindowForModel('claude-opus-4-7')).toBe(200000);
104
+ it('prices current Claude Fable, Opus, and Haiku aliases from the local pricing config', () => {
105
+ expect(contextWindowForModel('fable')).toBe(1000000);
106
+ expect(contextWindowForModel('opus')).toBe(1000000);
106
107
  expect(contextWindowForModel('claude-haiku-4-5')).toBe(200000);
107
108
 
109
+ const fableEstimate = estimateTurnPrice(sampleUsage, {
110
+ pricingModelKey: 'fable',
111
+ pricingTierKey: 'standard',
112
+ });
113
+ expect(fableEstimate).toMatchObject({
114
+ pricingModelKey: 'fable',
115
+ pricingTierKey: 'standard',
116
+ inputUsd: 0.01,
117
+ cachedInputUsd: 0.0005,
118
+ outputUsd: 0.075,
119
+ });
120
+
108
121
  const opusEstimate = estimateTurnPrice(sampleUsage, {
109
- pricingModelKey: 'claude-opus-4-7',
122
+ pricingModelKey: 'opus',
110
123
  pricingTierKey: 'standard',
111
124
  });
112
125
  expect(opusEstimate).toMatchObject({
113
- pricingModelKey: 'claude-opus-4-7',
126
+ pricingModelKey: 'opus',
114
127
  pricingTierKey: 'standard',
115
128
  inputUsd: 0.005,
116
129
  cachedInputUsd: 0.00025,
@@ -130,6 +143,51 @@ describe('modelPricing', () => {
130
143
  });
131
144
  });
132
145
 
146
+ it('prices explicit Claude API model ids, including fast Opus and legacy Opus', () => {
147
+ expect(contextWindowForModel('claude-sonnet-5')).toBe(1000000);
148
+ expect(contextWindowForModel('claude-sonnet-4-6')).toBe(1000000);
149
+ expect(contextWindowForModel('claude-opus-4-8')).toBe(1000000);
150
+ expect(supportsFastMode('claude-opus-4-8')).toBe(true);
151
+ expect(supportsFastMode('claude-opus-4-7')).toBe(true);
152
+ expect(contextWindowForModel('claude-opus-4-1')).toBe(200000);
153
+
154
+ const sonnet5Estimate = estimateTurnPrice(sampleUsage, {
155
+ pricingModelKey: 'claude-sonnet-5',
156
+ pricingTierKey: 'standard',
157
+ });
158
+ expect(sonnet5Estimate).toMatchObject({
159
+ pricingModelKey: 'claude-sonnet-5',
160
+ pricingTierKey: 'standard',
161
+ inputUsd: 0.002,
162
+ cachedInputUsd: 0.0001,
163
+ outputUsd: 0.015,
164
+ });
165
+
166
+ const opusFastEstimate = estimateTurnPrice(sampleUsage, {
167
+ pricingModelKey: 'claude-opus-4-8',
168
+ pricingTierKey: 'fast',
169
+ });
170
+ expect(opusFastEstimate).toMatchObject({
171
+ pricingModelKey: 'claude-opus-4-8',
172
+ pricingTierKey: 'fast',
173
+ inputUsd: 0.01,
174
+ cachedInputUsd: 0.0005,
175
+ outputUsd: 0.075,
176
+ });
177
+
178
+ const legacyOpusEstimate = estimateTurnPrice(sampleUsage, {
179
+ pricingModelKey: 'claude-opus-4-1',
180
+ pricingTierKey: 'standard',
181
+ });
182
+ expect(legacyOpusEstimate).toMatchObject({
183
+ pricingModelKey: 'claude-opus-4-1',
184
+ pricingTierKey: 'standard',
185
+ inputUsd: 0.015,
186
+ cachedInputUsd: 0.00075,
187
+ outputUsd: 0.1125,
188
+ });
189
+ });
190
+
133
191
  it('normalizes Claude date-stamped runtime model names to local pricing keys', () => {
134
192
  expect(contextWindowForModel('claude-sonnet-4-5-20250929')).toBe(200000);
135
193
  expect(supportsFastMode('claude-sonnet-4-5-20250929')).toBe(false);