@alpaca-editor/core 1.0.4149 → 1.0.4154

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.
@@ -18,20 +18,24 @@ interface AgentCostDisplayProps {
18
18
  numInputTokens: number;
19
19
  numOutputTokens: number;
20
20
  numCachedTokens: number;
21
+ numCacheWriteTokens?: number;
21
22
  totalCost?: number;
22
23
  inputCost?: number;
23
24
  outputCost?: number;
24
25
  cachedCost?: number;
26
+ cacheWriteCost?: number;
25
27
  } | null;
26
28
  /** Cumulative token usage across all messages in the session */
27
29
  totalTokens?: {
28
30
  input: number;
29
31
  output: number;
30
32
  cached: number;
33
+ cacheWrite: number;
31
34
  totalCost: number;
32
35
  inputCost: number;
33
36
  outputCost: number;
34
37
  cachedCost: number;
38
+ cacheWriteCost: number;
35
39
  };
36
40
  /** Cost limit for the agent (in USD) */
37
41
  costLimit?: number | null;
@@ -73,17 +77,20 @@ export function AgentCostDisplay({
73
77
  inputTokens: number,
74
78
  outputTokens: number,
75
79
  cachedTokens: number,
80
+ cacheWriteTokens: number = 0,
76
81
  ) => {
77
82
  // Cached tokens are a subset of input tokens, not additional tokens
78
83
  const freshInputTokens = inputTokens - cachedTokens;
79
84
  const inputCost = (freshInputTokens / 1000000) * 3; // $3 per million tokens
80
85
  const outputCost = (outputTokens / 1000000) * 15; // $15 per million tokens
81
86
  const cachedCost = (cachedTokens / 1000000) * 0.3; // Cached tokens are typically much cheaper
87
+ const cacheWriteCost = (cacheWriteTokens / 1000000) * 3.75; // Cache write tokens cost more
82
88
  return {
83
89
  inputCost,
84
90
  outputCost,
85
91
  cachedCost,
86
- totalCost: inputCost + outputCost + cachedCost,
92
+ cacheWriteCost,
93
+ totalCost: inputCost + outputCost + cachedCost + cacheWriteCost,
87
94
  };
88
95
  };
89
96
 
@@ -94,12 +101,14 @@ export function AgentCostDisplay({
94
101
  inputCost: response.inputCost ?? 0,
95
102
  outputCost: response.outputCost ?? 0,
96
103
  cachedCost: response.cachedCost ?? 0,
104
+ cacheWriteCost: response.cacheWriteCost ?? 0,
97
105
  totalCost: response.totalCost,
98
106
  }
99
107
  : calculateEstimatedCost(
100
108
  response.numInputTokens,
101
109
  response.numOutputTokens,
102
110
  response.numCachedTokens,
111
+ response.numCacheWriteTokens ?? 0,
103
112
  )
104
113
  : null;
105
114
 
@@ -122,9 +131,10 @@ export function AgentCostDisplay({
122
131
  }
123
132
 
124
133
  // Calculate percentage of limit used
125
- const percentUsed = costLimit && costLimit > 0
126
- ? Math.min((displayTotalCost / costLimit) * 100, 100)
127
- : null;
134
+ const percentUsed =
135
+ costLimit && costLimit > 0
136
+ ? Math.min((displayTotalCost / costLimit) * 100, 100)
137
+ : null;
128
138
 
129
139
  // Determine color based on usage
130
140
  const getColorClass = () => {
@@ -145,7 +155,9 @@ export function AgentCostDisplay({
145
155
  onClick={() => setShowCostPopover(!showCostPopover)}
146
156
  >
147
157
  Total Cost: {displayTotalCost.toFixed(2)} {currency}
148
- {costLimit && costLimit > 0 && ` / ${costLimit.toFixed(2)} ${currency}`}
158
+ {costLimit &&
159
+ costLimit > 0 &&
160
+ ` / ${costLimit.toFixed(2)} ${currency}`}
149
161
  </div>
150
162
  </PopoverTrigger>
151
163
  <PopoverContent className="w-80 p-4" align="end" side="bottom">
@@ -168,7 +180,9 @@ export function AgentCostDisplay({
168
180
  {costLimit && costLimit > 0 && (
169
181
  <div className="rounded-md bg-gray-50 p-3">
170
182
  <div className="mb-2 flex items-center justify-between text-xs">
171
- <span className="font-medium text-gray-700">Cost Limit Usage</span>
183
+ <span className="font-medium text-gray-700">
184
+ Cost Limit Usage
185
+ </span>
172
186
  <span className={`font-semibold ${getColorClass()}`}>
173
187
  {percentUsed?.toFixed(1)}%
174
188
  </span>
@@ -235,6 +249,19 @@ export function AgentCostDisplay({
235
249
  </span>
236
250
  </div>
237
251
  )}
252
+
253
+ {totalTokens.cacheWrite > 0 && (
254
+ <div className="flex items-center justify-between">
255
+ <span className="text-xs text-gray-500">
256
+ Cache write tokens:
257
+ </span>
258
+ <span className="text-xs">
259
+ {totalTokens.cacheWrite.toLocaleString()} (
260
+ {(totalTokens.cacheWriteCost ?? 0).toFixed(2)}{" "}
261
+ {currency})
262
+ </span>
263
+ </div>
264
+ )}
238
265
  </>
239
266
  ) : (
240
267
  response &&
@@ -272,6 +299,20 @@ export function AgentCostDisplay({
272
299
  </div>
273
300
  )}
274
301
 
302
+ {response.numCacheWriteTokens &&
303
+ response.numCacheWriteTokens > 0 && (
304
+ <div className="flex items-center justify-between">
305
+ <span className="text-xs text-gray-500">
306
+ Cache write tokens:
307
+ </span>
308
+ <span className="text-xs">
309
+ {response.numCacheWriteTokens.toLocaleString()}
310
+ ({currentCost.cacheWriteCost.toFixed(2)}{" "}
311
+ {currency})
312
+ </span>
313
+ </div>
314
+ )}
315
+
275
316
  <div className="mt-2 text-xs text-gray-400">
276
317
  * Estimated costs based on typical Claude 3.5
277
318
  pricing
@@ -343,8 +343,8 @@ const TodoListPanel = ({
343
343
  // First try to get todos from agent metadata (real-time updates)
344
344
  const metadataTodos = (() => {
345
345
  try {
346
- const context = (agentMetadata as any)?.additionalData?.context;
347
- const todoList = context?.todoList;
346
+ const todoList = (agentMetadata as any)?.additionalData?.todoList;
347
+
348
348
  if (todoList?.items && Array.isArray(todoList.items)) {
349
349
  return todoList.items
350
350
  .map((item: any, idx: number) => ({
@@ -359,9 +359,14 @@ const TodoListPanel = ({
359
359
  sourceTitle: todoList.title,
360
360
  }))
361
361
  .filter((item: any) => item.text);
362
+ } else {
363
+ console.log(
364
+ "📋 No todo list found in metadata. AgentMetadata:",
365
+ agentMetadata,
366
+ );
362
367
  }
363
368
  } catch (e) {
364
- // Fallback to extracting from messages
369
+ console.error("📋 Error extracting todos from metadata:", e);
365
370
  }
366
371
  return null;
367
372
  })();
@@ -575,9 +580,11 @@ const calculateTotalTokens = (messages: AgentChatMessage[]) => {
575
580
  input: acc.input + (message.inputTokens || 0),
576
581
  output: acc.output + (message.outputTokens || 0),
577
582
  cached: acc.cached + (message.cachedInputTokens || 0),
583
+ cacheWrite: acc.cacheWrite,
578
584
  inputCost: acc.inputCost + (message.inputTokenCost || 0),
579
585
  outputCost: acc.outputCost + (message.outputTokenCost || 0),
580
586
  cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
587
+ cacheWriteCost: acc.cacheWriteCost,
581
588
  totalCost: acc.totalCost + (message.totalCost || 0),
582
589
  };
583
590
  },
@@ -585,9 +592,11 @@ const calculateTotalTokens = (messages: AgentChatMessage[]) => {
585
592
  input: 0,
586
593
  output: 0,
587
594
  cached: 0,
595
+ cacheWrite: 0,
588
596
  inputCost: 0,
589
597
  outputCost: 0,
590
598
  cachedCost: 0,
599
+ cacheWriteCost: 0,
591
600
  totalCost: 0,
592
601
  },
593
602
  );
@@ -802,9 +811,11 @@ export function AgentTerminal({
802
811
  input: number;
803
812
  output: number;
804
813
  cached: number;
814
+ cacheWrite?: number;
805
815
  inputCost: number;
806
816
  outputCost: number;
807
817
  cachedCost: number;
818
+ cacheWriteCost?: number;
808
819
  totalCost: number;
809
820
  currency?: string;
810
821
  } | null>(null);
@@ -1050,9 +1061,11 @@ export function AgentTerminal({
1050
1061
  input: Number(data.totalInputTokens) || 0,
1051
1062
  output: Number(data.totalOutputTokens) || 0,
1052
1063
  cached: Number(data.totalCachedTokens) || 0,
1064
+ cacheWrite: Number(data.totalCacheWriteTokens) || 0,
1053
1065
  inputCost: Number(data.totalInputTokenCost) || 0,
1054
1066
  outputCost: Number(data.totalOutputTokenCost) || 0,
1055
1067
  cachedCost: Number(data.totalCachedTokenCost) || 0,
1068
+ cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
1056
1069
  totalCost: Number(data.totalCost) || 0,
1057
1070
  currency: data.currency || "USD",
1058
1071
  });
@@ -1261,9 +1274,11 @@ export function AgentTerminal({
1261
1274
  input: Number(data.totalInputTokens) || 0,
1262
1275
  output: Number(data.totalOutputTokens) || 0,
1263
1276
  cached: Number(data.totalCachedTokens) || 0,
1277
+ cacheWrite: Number(data.totalCacheWriteTokens) || 0,
1264
1278
  inputCost: Number(data.totalInputTokenCost) || 0,
1265
1279
  outputCost: Number(data.totalOutputTokenCost) || 0,
1266
1280
  cachedCost: Number(data.totalCachedTokenCost) || 0,
1281
+ cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
1267
1282
  totalCost: Number(data.totalCost) || 0,
1268
1283
  currency: data.currency || "USD",
1269
1284
  });
@@ -1402,7 +1417,10 @@ export function AgentTerminal({
1402
1417
 
1403
1418
  case "statusUpdate":
1404
1419
  try {
1405
- const kind = (message as any)?.data?.kind;
1420
+ // Check both 'kind' and 'state' for backward compatibility
1421
+ const kind =
1422
+ (message as any)?.data?.kind ||
1423
+ (message as any)?.data?.state;
1406
1424
  if (kind === "streamOpen") {
1407
1425
  setIsConnecting(false);
1408
1426
  // Don't clear waiting state here - let it be cleared when first content arrives
@@ -1417,10 +1435,13 @@ export function AgentTerminal({
1417
1435
  input: Number(totals.totalInputTokens) || 0,
1418
1436
  output: Number(totals.totalOutputTokens) || 0,
1419
1437
  cached: Number(totals.totalCachedInputTokens) || 0,
1438
+ cacheWrite: Number(totals.totalCacheWriteTokens) || 0,
1420
1439
  inputCost: Number(totals.totalInputTokenCost) || 0,
1421
1440
  outputCost: Number(totals.totalOutputTokenCost) || 0,
1422
1441
  cachedCost:
1423
1442
  Number(totals.totalCachedInputTokenCost) || 0,
1443
+ cacheWriteCost:
1444
+ Number(totals.totalCacheWriteTokenCost) || 0,
1424
1445
  totalCost: totalCost,
1425
1446
  currency: totals.currency,
1426
1447
  });
@@ -1538,6 +1559,21 @@ export function AgentTerminal({
1538
1559
  return prevAgent;
1539
1560
  }
1540
1561
  });
1562
+ } else if (kind === "cost_limit_reached") {
1563
+ // Cost limit has been reached - show banner and stop waiting
1564
+ const data = (message as any).data || {};
1565
+ const totalCost = Number(data.totalCost) || 0;
1566
+ if (agent?.costLimit) {
1567
+ setCostLimitExceeded({
1568
+ totalCost: totalCost,
1569
+ costLimit: agent.costLimit,
1570
+ initialCostLimit: agent.costLimit,
1571
+ });
1572
+ }
1573
+ setIsWaitingForResponse(false);
1574
+ setIsConnecting(false);
1575
+ shouldCreateNewMessage.current = false;
1576
+ break;
1541
1577
  } else if (
1542
1578
  kind === "toolApprovalGranted" ||
1543
1579
  kind === "toolApprovalRejected"
@@ -1719,8 +1755,22 @@ export function AgentTerminal({
1719
1755
  case "contextUpdate":
1720
1756
  // Update agent context when backend sends context update
1721
1757
  try {
1758
+ console.log("📥 Received contextUpdate message:", message);
1722
1759
  const updatedContext = message.data as AgentMetadata;
1723
1760
  if (updatedContext) {
1761
+ console.log(
1762
+ "📝 Updating agent metadata with:",
1763
+ updatedContext,
1764
+ );
1765
+
1766
+ // Check if there's a todo list in the context
1767
+ if (updatedContext.additionalData?.todoList) {
1768
+ console.log(
1769
+ "✅ Todo list found in context update:",
1770
+ updatedContext.additionalData.todoList,
1771
+ );
1772
+ }
1773
+
1724
1774
  // Update local metadata state
1725
1775
  setAgentMetadata((prev) => ({
1726
1776
  ...prev,
@@ -1736,13 +1786,14 @@ export function AgentTerminal({
1736
1786
  };
1737
1787
  });
1738
1788
 
1739
- console.log(
1740
- "✅ Context updated from backend:",
1741
- updatedContext,
1789
+ console.log("✅ Context updated from backend successfully");
1790
+ } else {
1791
+ console.warn(
1792
+ "⚠️ Context update received but updatedContext is null/undefined",
1742
1793
  );
1743
1794
  }
1744
1795
  } catch (err) {
1745
- console.error("Error handling context update:", err);
1796
+ console.error("Error handling context update:", err);
1746
1797
  }
1747
1798
  break;
1748
1799
 
@@ -3184,15 +3235,15 @@ export function AgentTerminal({
3184
3235
  <div className="mb-2 flex items-center gap-2">
3185
3236
  <AlertCircle className="h-4 w-4 text-amber-500" strokeWidth={1} />
3186
3237
  <span>
3187
- Cost limit exceeded. Spent ${totalCost.toFixed(4)} / $
3188
- {costLimit.toFixed(4)}.
3238
+ Cost limit exceeded. Spent ${totalCost.toFixed(2)} / $
3239
+ {costLimit.toFixed(2)}.
3189
3240
  </span>
3190
3241
  </div>
3191
3242
  <div className="flex gap-2">
3192
3243
  <button
3193
3244
  className="rounded border border-amber-300 bg-white px-2 py-1 hover:bg-amber-100"
3194
3245
  onClick={async () => {
3195
- if (!agent?.id) return;
3246
+ if (!agent?.id || !editContext?.sessionId) return;
3196
3247
  try {
3197
3248
  // Extend by initial cost limit amount
3198
3249
  const result = await updateAgentCostLimit(agent.id, "extend");
@@ -3205,9 +3256,35 @@ export function AgentTerminal({
3205
3256
  }
3206
3257
 
3207
3258
  setCostLimitExceeded(null);
3208
- await connectToStream(agent);
3259
+
3260
+ // Resume agent execution with empty message
3261
+ const request: StartAgentRequest = {
3262
+ agentId: agent.id,
3263
+ message: "",
3264
+ sessionId: editContext.sessionId,
3265
+ profileId: activeProfile?.id || profiles[0]?.id || "",
3266
+ profile: activeProfile?.name || profiles[0]?.name || "",
3267
+ model: selectedModelId,
3268
+ mode: mode as any,
3269
+ context: agentMetadata,
3270
+ };
3271
+
3272
+ setIsWaitingForResponse(true);
3273
+ isWaitingRef.current = true;
3274
+ setShouldAutoScroll(true);
3275
+
3276
+ await startAgent(request);
3277
+ await connectToStream();
3209
3278
  } catch (e) {
3210
- console.error("Failed to extend cost limit", e);
3279
+ console.error(
3280
+ "Failed to extend cost limit and resume agent",
3281
+ e,
3282
+ );
3283
+ setError(
3284
+ e instanceof Error
3285
+ ? e.message
3286
+ : "Failed to extend cost limit and resume agent",
3287
+ );
3211
3288
  }
3212
3289
  }}
3213
3290
  >
@@ -3589,9 +3666,11 @@ export function AgentTerminal({
3589
3666
  input: liveTotals.input,
3590
3667
  output: liveTotals.output,
3591
3668
  cached: liveTotals.cached,
3669
+ cacheWrite: liveTotals.cacheWrite ?? 0,
3592
3670
  inputCost: liveTotals.inputCost,
3593
3671
  outputCost: liveTotals.outputCost,
3594
3672
  cachedCost: liveTotals.cachedCost,
3673
+ cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
3595
3674
  totalCost: liveTotals.totalCost,
3596
3675
  }
3597
3676
  : totalTokens
@@ -133,21 +133,21 @@ export function useSocketMessageHandler(deps: {
133
133
  version: 0,
134
134
  });
135
135
  }
136
- } else {
137
- await itemsRepository.refreshItems(
138
- [message.payload.item],
139
- message.payload.changes,
140
- );
141
- const changes = message?.payload?.changes;
142
- const isPropertiesChange = changes?.properties !== false; // default true
143
- if (
144
- isPropertiesChange &&
145
- message.payload.item.id === currentItemDescriptor?.id
146
- ) {
147
- await loadItemVersions();
148
- }
136
+ } else {
137
+ await itemsRepository.refreshItems(
138
+ [message.payload.item],
139
+ message.payload.changes,
140
+ );
141
+ const changes = message?.payload?.changes;
142
+ const isVersionChange = changes?.versions === true;
143
+ if (
144
+ isVersionChange &&
145
+ message.payload.item.id === currentItemDescriptor?.id
146
+ ) {
147
+ await loadItemVersions();
149
148
  }
150
149
  }
150
+ }
151
151
 
152
152
  if (message.type === "item-version-added") {
153
153
  if (currentItemRef.current) {
@@ -13,20 +13,10 @@ export type AiModel = {
13
13
  export type AiProfile = {
14
14
  id: string;
15
15
  name: string;
16
- instructions: string;
17
- managedTodoInstructions?: string;
18
16
  defaultModelId: string | null; // Guid as string, nullable
19
17
  models: AiModel[]; // Array of model objects with id and name
20
18
  prompts: { prompt: string; title: string }[];
21
- errorMessage?: string;
22
- // Whether a new agent should be seeded with current item/selection/field
23
19
  includeEditorContextOnCreate?: boolean;
24
- // When true, the agent should run in managed TODO list mode.
25
- managedTodoMode?: boolean;
26
- // Tools that are allowed when the user switches to "Read-Only" mode
27
- readOnlyModeTools?: string[];
28
- // Optional cost limit in USD, sourced from profile
29
- costLimit?: number | null;
30
20
  };
31
21
 
32
22
  export async function loadAiProfiles(
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.4149";
2
- export const buildDate = "2025-10-07 09:38:14";
1
+ export const version = "1.0.4154";
2
+ export const buildDate = "2025-10-08 13:25:46";