@agent-native/core 0.198.2 → 0.198.3-nightly-20260930124743

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.
package/dist/catalog.json CHANGED
@@ -62,6 +62,6 @@
62
62
  "vite": "8.1.0",
63
63
  "vitest": "^4.1.5",
64
64
  "y-protocols": "^1.0.7",
65
- "yjs": "^13.6.32",
65
+ "yjs": "^13.6.33",
66
66
  "zod": "^4.3.6"
67
67
  }
@@ -1528,7 +1528,10 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
1528
1528
  {
1529
1529
  type: "suggestions.updated",
1530
1530
  ...base,
1531
- suggestions: event.suggestions,
1531
+ suggestions: event.suggestions.map((suggestion) => ({
1532
+ ...suggestion,
1533
+ runId: run.runId,
1534
+ })),
1532
1535
  },
1533
1536
  ];
1534
1537
  case "annotation": {
@@ -1,6 +1,6 @@
1
1
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
- ok?: undefined;
3
2
  error: string;
3
+ ok?: undefined;
4
4
  } | {
5
5
  error?: undefined;
6
6
  ok: boolean;
@@ -132,6 +132,8 @@ export const CORE_ACTION_GROUPS = {
132
132
  "get-usage-alerts": "automation",
133
133
  "manage-usage-alert": "automation",
134
134
  "get-usage-metrics": "automation",
135
+ "get-usage-insights": "automation",
136
+ "get-usage-run": "automation",
135
137
  "get-builder-credit-usage": "automation",
136
138
  "get-builder-credit-status": "automation",
137
139
  "get-builder-referral-info": "automation",
@@ -59,6 +59,6 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
59
59
  summary?: undefined;
60
60
  spans?: undefined;
61
61
  id?: undefined;
62
- error?: undefined;
63
62
  ok: boolean;
63
+ error?: undefined;
64
64
  }>>;
@@ -2,6 +2,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
2
2
  error: string;
3
3
  ok?: undefined;
4
4
  } | {
5
- error?: undefined;
6
5
  ok: boolean;
6
+ error?: undefined;
7
7
  }>>;
@@ -75,9 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
79
78
  id?: undefined;
80
79
  deleted?: undefined;
80
+ found?: undefined;
81
81
  providers: {
82
82
  id: string;
83
83
  label: string;
@@ -89,11 +89,10 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
89
89
  }[];
90
90
  count: number;
91
91
  provider?: undefined;
92
- found?: undefined;
93
92
  registered?: undefined;
94
93
  label?: undefined;
95
- } | {
96
94
  message?: undefined;
95
+ } | {
97
96
  id?: undefined;
98
97
  deleted?: undefined;
99
98
  count?: undefined;
@@ -102,8 +101,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
102
101
  provider: import("../custom-registry.js").CustomProviderConfig;
103
102
  registered?: undefined;
104
103
  label?: undefined;
105
- } | {
106
104
  message?: undefined;
105
+ } | {
107
106
  deleted?: undefined;
108
107
  count?: undefined;
109
108
  providers?: undefined;
@@ -112,22 +111,23 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
115
- } | {
116
114
  message?: undefined;
115
+ } | {
116
+ found?: undefined;
117
117
  count?: undefined;
118
118
  providers?: undefined;
119
119
  provider?: undefined;
120
- found?: undefined;
121
120
  deleted: boolean;
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
126
  deleted?: undefined;
127
+ found?: undefined;
127
128
  count?: undefined;
128
129
  providers?: undefined;
129
130
  provider?: undefined;
130
- found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,10 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- provider?: undefined;
315
- deleted?: undefined;
316
314
  id?: undefined;
317
315
  message?: undefined;
316
+ found?: undefined;
318
317
  providers: {
319
318
  id: string;
320
319
  label: string;
@@ -325,45 +324,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
325
324
  updatedAt: number;
326
325
  }[];
327
326
  count: number;
328
- found?: undefined;
327
+ provider?: undefined;
328
+ deleted?: undefined;
329
329
  registered?: undefined;
330
330
  label?: undefined;
331
331
  } | {
332
- deleted?: undefined;
333
332
  id?: undefined;
334
- count?: undefined;
335
333
  message?: undefined;
336
334
  providers?: undefined;
335
+ count?: undefined;
337
336
  found: boolean;
338
337
  provider: import("../custom-registry.js").CustomProviderConfig;
338
+ deleted?: undefined;
339
339
  registered?: undefined;
340
340
  label?: undefined;
341
341
  } | {
342
- provider?: undefined;
343
- deleted?: undefined;
344
- count?: undefined;
345
342
  message?: undefined;
346
343
  providers?: undefined;
344
+ count?: undefined;
345
+ provider?: undefined;
347
346
  found: boolean;
348
347
  id: string;
348
+ deleted?: undefined;
349
349
  registered?: undefined;
350
350
  label?: undefined;
351
351
  } | {
352
- provider?: undefined;
353
- count?: undefined;
354
352
  message?: undefined;
355
- providers?: undefined;
356
353
  found?: undefined;
354
+ providers?: undefined;
355
+ count?: undefined;
356
+ provider?: undefined;
357
357
  deleted: boolean;
358
358
  id: string;
359
359
  registered?: undefined;
360
360
  label?: undefined;
361
361
  } | {
362
+ found?: undefined;
363
+ providers?: undefined;
364
+ count?: undefined;
362
365
  provider?: undefined;
363
366
  deleted?: undefined;
364
- count?: undefined;
365
- providers?: undefined;
366
- found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -39,8 +39,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
39
39
  error: string;
40
40
  }>;
41
41
  export declare function handleDeleteResource(event: any): Promise<{
42
- ok?: undefined;
43
42
  error: string;
43
+ ok?: undefined;
44
44
  } | {
45
45
  error?: undefined;
46
46
  ok: boolean;
@@ -633,6 +633,11 @@ export async function mergeCoreSharingActions(registry) {
633
633
  "get-usage-metrics",
634
634
  () => import("../usage/actions/get-usage-metrics.js"),
635
635
  ],
636
+ [
637
+ "get-usage-insights",
638
+ () => import("../usage/actions/get-usage-insights.js"),
639
+ ],
640
+ ["get-usage-run", () => import("../usage/actions/get-usage-run.js")],
636
641
  [
637
642
  "get-builder-credit-usage",
638
643
  () => import("../usage/actions/get-builder-credit-usage.js"),
@@ -8,17 +8,17 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
8
8
  * resolveEngine's credential detection.
9
9
  */
10
10
  export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
11
- ok?: undefined;
12
11
  error: string;
13
12
  scope?: undefined;
13
+ ok?: undefined;
14
14
  } | {
15
15
  ok: boolean;
16
16
  error: any;
17
17
  scope?: undefined;
18
18
  } | {
19
- error?: undefined;
20
19
  ok: boolean;
21
20
  scope: "org" | "user";
21
+ error?: undefined;
22
22
  }>>;
23
23
  export interface DefaultModelSelectionRequest {
24
24
  engine: string;
@@ -581,8 +581,8 @@ export declare function createPublicRemoteAgentsHandler(discover?: PublicAgentDi
581
581
  error: string;
582
582
  agents?: undefined;
583
583
  } | {
584
- agents: Omit<import("./agent-discovery.js").DiscoveredAgent, "auth" | "kind">[];
585
584
  error?: undefined;
585
+ agents: Omit<import("./agent-discovery.js").DiscoveredAgent, "auth" | "kind">[];
586
586
  }>>;
587
587
  export declare function getBuilderConnectErrorDisposition(error: unknown, connectAttemptId: string | null): "correlated" | "legacy" | null;
588
588
  export declare function getBuilderConnectErrorKey(ownerEmail: string, connectAttemptId?: string | null): string;
@@ -54,8 +54,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
54
54
  expiresAt?: undefined;
55
55
  ttlSeconds?: undefined;
56
56
  } | {
57
+ error?: undefined;
57
58
  token: string;
58
59
  expiresAt: string;
59
60
  ttlSeconds: number;
60
- error?: undefined;
61
61
  }>>;
@@ -0,0 +1,7 @@
1
+ declare const _default: import("../../action.js").ActionDefinition<{
2
+ sinceDays?: unknown;
3
+ scope?: "me" | "workspace";
4
+ userEmail?: string;
5
+ appId?: string;
6
+ }, import("../insights-store.js").UsageInsights>;
7
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { defineAction } from "../../action.js";
3
+ import { getUsageInsights } from "../insights-store.js";
4
+ import { resolveUsageAppKey } from "../store.js";
5
+ export default defineAction({
6
+ description: "Get the Usage page's performance breakdown for an app: spend split by cache reads, cache writes, fresh input and output, savings versus no prompt caching, and recent prompts with per-run LLM calls, tool calls, tool lookups and cache misses.",
7
+ http: { method: "GET" },
8
+ schema: z.object({
9
+ sinceDays: z.coerce.number().int().min(1).max(365).default(30),
10
+ scope: z.enum(["me", "workspace"]).default("me"),
11
+ userEmail: z.string().trim().min(1).optional(),
12
+ appId: z.string().trim().max(200).optional(),
13
+ }),
14
+ run: async ({ sinceDays, scope, userEmail, appId }, ctx) => {
15
+ if (!ctx?.userEmail)
16
+ throw new Error("Not authenticated.");
17
+ return getUsageInsights({ sinceDays, scope, userEmail }, {
18
+ ownerEmail: ctx.userEmail,
19
+ orgId: ctx.orgId,
20
+ app: resolveUsageAppKey(appId),
21
+ });
22
+ },
23
+ });
@@ -0,0 +1,7 @@
1
+ declare const _default: import("../../action.js").ActionDefinition<{
2
+ runId: string;
3
+ scope?: "me" | "workspace";
4
+ userEmail?: string;
5
+ appId?: string;
6
+ }, import("../insights-store.js").UsageRunDetail>;
7
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { defineAction } from "../../action.js";
3
+ import { getUsageRun } from "../insights-store.js";
4
+ import { resolveUsageAppKey } from "../store.js";
5
+ export default defineAction({
6
+ description: "Get one prompt's step-by-step cost breakdown for the Usage page: every LLM call with tokens, cache hit and cost, every tool call with status, cache misses with their likely cause, and automated eval scores.",
7
+ http: { method: "GET" },
8
+ schema: z.object({
9
+ runId: z.string().trim().min(1).max(200),
10
+ scope: z.enum(["me", "workspace"]).default("me"),
11
+ userEmail: z.string().trim().min(1).optional(),
12
+ appId: z.string().trim().max(200).optional(),
13
+ }),
14
+ run: async ({ runId, scope, userEmail, appId }, ctx) => {
15
+ if (!ctx?.userEmail)
16
+ throw new Error("Not authenticated.");
17
+ return getUsageRun({ runId, scope, userEmail }, {
18
+ ownerEmail: ctx.userEmail,
19
+ orgId: ctx.orgId,
20
+ app: resolveUsageAppKey(appId),
21
+ });
22
+ },
23
+ });
@@ -0,0 +1,127 @@
1
+ import { type UsageMetricsAccessInput, type UsageMetricsScope } from "./metrics-store.js";
2
+ /** Run insights are always read for one app, never across all apps. */
3
+ type RunAccessInput = UsageMetricsAccessInput & {
4
+ app: string;
5
+ };
6
+ /** Spend split by how the provider billed each token, in cents. */
7
+ export interface UsageCostBreakdown {
8
+ cacheReadCents: number;
9
+ cacheWriteCents: number;
10
+ uncachedInputCents: number;
11
+ outputCents: number;
12
+ /** Recorded spend for the rows; the category splits above are token-priced estimates. */
13
+ totalCents: number;
14
+ /** The same tokens priced at list rates, so it is comparable to `noCacheCents`. */
15
+ estimatedCents: number;
16
+ /** What the same tokens would have cost with no prompt caching. */
17
+ noCacheCents: number;
18
+ }
19
+ export interface UsageTokenTotals {
20
+ inputTokens: number;
21
+ outputTokens: number;
22
+ cacheReadTokens: number;
23
+ cacheWriteTokens: number;
24
+ }
25
+ export type UsageScoreSource = "heuristic" | "judge" | "human";
26
+ export interface UsageRunScore {
27
+ source: UsageScoreSource;
28
+ criteria: string;
29
+ score: number;
30
+ reasoning: string | null;
31
+ }
32
+ export interface UsageRunTool {
33
+ name: string;
34
+ calls: number;
35
+ failed: number;
36
+ error: string | null;
37
+ }
38
+ /**
39
+ * The context was re-sent in full when the cache could have been re-used.
40
+ * `tool-lookup`: new tools were loaded; `prefix-changed`: something else at
41
+ * the start of the prompt changed.
42
+ */
43
+ export type UsageRestartCause = "tool-lookup" | "prefix-changed";
44
+ export interface UsageRunRestarts {
45
+ count: number;
46
+ cents: number;
47
+ byCause: Record<UsageRestartCause, {
48
+ count: number;
49
+ cents: number;
50
+ }>;
51
+ }
52
+ export interface UsageRunListItem {
53
+ runId: string;
54
+ threadId: string | null;
55
+ createdAt: number;
56
+ ownerEmail: string;
57
+ label: string;
58
+ model: string;
59
+ prompt: string | null;
60
+ /** `unknown` when the trace was never written or has been cleaned up. */
61
+ status: "success" | "error" | "unknown";
62
+ tokens: UsageTokenTotals;
63
+ cost: UsageCostBreakdown;
64
+ modelCalls: number;
65
+ tools: UsageRunTool[];
66
+ restarts: UsageRunRestarts;
67
+ /** Tool calls that overlapped another call in the same step, and the time that saved. */
68
+ parallel: {
69
+ calls: number;
70
+ savedMs: number;
71
+ };
72
+ /** Failed tool calls in a run that still finished. */
73
+ recoveredErrors: number;
74
+ durationMs: number | null;
75
+ feedback: "up" | "down" | null;
76
+ }
77
+ export interface UsagePeriodTotals {
78
+ runs: number;
79
+ tokens: UsageTokenTotals;
80
+ cost: UsageCostBreakdown;
81
+ }
82
+ export interface UsageInsights {
83
+ sinceDays: number;
84
+ current: UsagePeriodTotals;
85
+ previous: UsagePeriodTotals;
86
+ runs: UsageRunListItem[];
87
+ }
88
+ export interface UsageRunToolCall {
89
+ name: string;
90
+ startedAt: number;
91
+ durationMs: number;
92
+ status: string;
93
+ errorMessage: string | null;
94
+ }
95
+ /** One model call and the tools it asked for. */
96
+ export interface UsageRunTurn {
97
+ index: number;
98
+ model: string;
99
+ durationMs: number;
100
+ tokens: UsageTokenTotals;
101
+ cost: UsageCostBreakdown;
102
+ status: string;
103
+ /** Set only when re-sending the context cost a meaningful amount. */
104
+ restart: {
105
+ cause: UsageRestartCause;
106
+ cents: number;
107
+ } | null;
108
+ /** The provider cache had expired after an idle gap; expected, not a fault. */
109
+ cacheExpired: boolean;
110
+ toolCalls: UsageRunToolCall[];
111
+ }
112
+ export interface UsageRunDetail extends UsageRunListItem {
113
+ reply: string | null;
114
+ turns: UsageRunTurn[];
115
+ scores: UsageRunScore[];
116
+ }
117
+ export declare function getUsageInsights(input: {
118
+ sinceDays?: number;
119
+ scope?: UsageMetricsScope;
120
+ userEmail?: string | null;
121
+ }, accessInput: RunAccessInput): Promise<UsageInsights>;
122
+ export declare function getUsageRun(input: {
123
+ runId: string;
124
+ scope?: UsageMetricsScope;
125
+ userEmail?: string | null;
126
+ }, accessInput: RunAccessInput): Promise<UsageRunDetail | null>;
127
+ export {};
@@ -0,0 +1,462 @@
1
+ import { stablePrefixCacheControl } from "../agent/engine/prompt-cache.js";
2
+ import { getDbExec } from "../db/client.js";
3
+ import { ensureObservabilityTables } from "../observability/store.js";
4
+ import { loadRunExchanges, numberField, resolveScope, stringField, usageAppScope, } from "./metrics-store.js";
5
+ import { calculateCost, ensureUsageTable } from "./store.js";
6
+ const DAY_MS = 24 * 60 * 60 * 1000;
7
+ const RUN_LIMIT = 25;
8
+ // A restart cheaper than this is provider noise, not something to act on.
9
+ const MIN_RESTART_CENTS = 0.5;
10
+ function costBreakdown(tokens, model) {
11
+ const { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens } = tokens;
12
+ const uncached = Math.max(0, inputTokens - cacheReadTokens - cacheWriteTokens);
13
+ const cents = (centicents) => centicents / 100;
14
+ const breakdown = {
15
+ cacheReadCents: cents(calculateCost(cacheReadTokens, 0, model, cacheReadTokens, 0)),
16
+ cacheWriteCents: cents(calculateCost(cacheWriteTokens, 0, model, 0, cacheWriteTokens)),
17
+ uncachedInputCents: cents(calculateCost(uncached, 0, model)),
18
+ outputCents: cents(calculateCost(0, outputTokens, model)),
19
+ noCacheCents: cents(calculateCost(inputTokens, outputTokens, model)),
20
+ };
21
+ const estimatedCents = breakdown.cacheReadCents +
22
+ breakdown.cacheWriteCents +
23
+ breakdown.uncachedInputCents +
24
+ breakdown.outputCents;
25
+ return { ...breakdown, estimatedCents, totalCents: estimatedCents };
26
+ }
27
+ /**
28
+ * Token-priced breakdown for one model's rows, carrying their recorded spend.
29
+ * Unpriced rows are stored as 0, so they fall back to the token estimate.
30
+ */
31
+ function recordedBreakdown(row) {
32
+ const estimate = costBreakdown(tokensFromRow(row), stringField(row, "model"));
33
+ if (numberField(row, "unpriced") > 0)
34
+ return estimate;
35
+ return { ...estimate, totalCents: numberField(row, "cost_cents_x100") / 100 };
36
+ }
37
+ const UNPRICED = `CASE WHEN cost_source = 'unavailable' THEN 1 ELSE 0 END`;
38
+ function addBreakdown(a, b) {
39
+ return {
40
+ cacheReadCents: a.cacheReadCents + b.cacheReadCents,
41
+ cacheWriteCents: a.cacheWriteCents + b.cacheWriteCents,
42
+ uncachedInputCents: a.uncachedInputCents + b.uncachedInputCents,
43
+ outputCents: a.outputCents + b.outputCents,
44
+ totalCents: a.totalCents + b.totalCents,
45
+ estimatedCents: a.estimatedCents + b.estimatedCents,
46
+ noCacheCents: a.noCacheCents + b.noCacheCents,
47
+ };
48
+ }
49
+ const EMPTY_BREAKDOWN = {
50
+ cacheReadCents: 0,
51
+ cacheWriteCents: 0,
52
+ uncachedInputCents: 0,
53
+ outputCents: 0,
54
+ totalCents: 0,
55
+ estimatedCents: 0,
56
+ noCacheCents: 0,
57
+ };
58
+ function emptyTokens() {
59
+ return {
60
+ inputTokens: 0,
61
+ outputTokens: 0,
62
+ cacheReadTokens: 0,
63
+ cacheWriteTokens: 0,
64
+ };
65
+ }
66
+ function addTokens(a, b) {
67
+ return {
68
+ inputTokens: a.inputTokens + b.inputTokens,
69
+ outputTokens: a.outputTokens + b.outputTokens,
70
+ cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
71
+ cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,
72
+ };
73
+ }
74
+ function tokensFromRow(row) {
75
+ return {
76
+ inputTokens: numberField(row, "input_tokens"),
77
+ outputTokens: numberField(row, "output_tokens"),
78
+ cacheReadTokens: numberField(row, "cache_read_tokens"),
79
+ cacheWriteTokens: numberField(row, "cache_write_tokens"),
80
+ };
81
+ }
82
+ const SCORE_SOURCE = {
83
+ automated: "heuristic",
84
+ llm_judge: "judge",
85
+ human: "human",
86
+ };
87
+ /**
88
+ * Extra spend versus the call re-using the previous call's context from cache.
89
+ * Providers that bill no cache writes (OpenAI) only lose the read discount.
90
+ */
91
+ function restartCents(model, tokens, previousInput) {
92
+ const reusable = Math.min(previousInput, tokens.inputTokens);
93
+ const writesBilled = tokens.cacheWriteTokens > 0;
94
+ const ifCached = calculateCost(tokens.inputTokens, 0, model, reusable, writesBilled ? tokens.inputTokens - reusable : 0);
95
+ const actual = calculateCost(tokens.inputTokens, 0, model, tokens.cacheReadTokens, tokens.cacheWriteTokens);
96
+ return Math.max(0, actual - ifCached) / 100;
97
+ }
98
+ function buildTurns(spans) {
99
+ const turns = [];
100
+ // A model that never reports cache tokens has no cache to miss.
101
+ const cachingModels = new Set(spans
102
+ .filter((span) => span.spanType === "llm_call" &&
103
+ span.tokens.cacheReadTokens + span.tokens.cacheWriteTokens > 0)
104
+ .map((span) => span.name));
105
+ // A longer gap than the prompt-cache TTL re-writes the prefix.
106
+ const cacheTtlMs = stablePrefixCacheControl().ttl === "1h" ? 60 * 60 * 1000 : 5 * 60 * 1000;
107
+ let previousStartedAt = 0;
108
+ let toolLookupSinceLastTurn = false;
109
+ for (const span of spans) {
110
+ if (span.spanType === "tool_call") {
111
+ if (span.name === "tool-search")
112
+ toolLookupSinceLastTurn = true;
113
+ turns.at(-1)?.toolCalls.push({
114
+ name: span.name,
115
+ startedAt: span.createdAt,
116
+ durationMs: span.durationMs,
117
+ status: span.status,
118
+ errorMessage: span.errorMessage,
119
+ });
120
+ continue;
121
+ }
122
+ if (span.spanType !== "llm_call")
123
+ continue;
124
+ const previous = turns.at(-1);
125
+ const missed = cachingModels.has(span.name) &&
126
+ previous?.model === span.name &&
127
+ span.tokens.cacheReadTokens < span.tokens.inputTokens / 2;
128
+ const expired = missed && span.createdAt - previousStartedAt > cacheTtlMs;
129
+ let restart = null;
130
+ if (missed && !expired) {
131
+ const cents = restartCents(span.name, span.tokens, previous.tokens.inputTokens);
132
+ if (cents >= MIN_RESTART_CENTS) {
133
+ restart = {
134
+ cause: toolLookupSinceLastTurn ? "tool-lookup" : "prefix-changed",
135
+ cents,
136
+ };
137
+ }
138
+ }
139
+ turns.push({
140
+ index: turns.length + 1,
141
+ model: span.name,
142
+ durationMs: span.durationMs,
143
+ tokens: span.tokens,
144
+ cost: costBreakdown(span.tokens, span.name),
145
+ status: span.status,
146
+ restart,
147
+ cacheExpired: expired,
148
+ toolCalls: [],
149
+ });
150
+ previousStartedAt = span.createdAt;
151
+ toolLookupSinceLastTurn = false;
152
+ }
153
+ return turns;
154
+ }
155
+ function parallelSavings(turns) {
156
+ let calls = 0;
157
+ let savedMs = 0;
158
+ for (const { toolCalls } of turns) {
159
+ if (toolCalls.length < 2)
160
+ continue;
161
+ const start = Math.min(...toolCalls.map((call) => call.startedAt));
162
+ const end = Math.max(...toolCalls.map((call) => call.startedAt + call.durationMs));
163
+ const sequential = toolCalls.reduce((sum, call) => sum + call.durationMs, 0);
164
+ if (sequential - (end - start) <= 0)
165
+ continue;
166
+ savedMs += sequential - (end - start);
167
+ calls += toolCalls.filter((call) => toolCalls.some((other) => other !== call &&
168
+ other.startedAt < call.startedAt + call.durationMs &&
169
+ call.startedAt < other.startedAt + other.durationMs)).length;
170
+ }
171
+ return { calls, savedMs };
172
+ }
173
+ function summarizeTurns(turns) {
174
+ const tools = new Map();
175
+ const restarts = {
176
+ count: 0,
177
+ cents: 0,
178
+ byCause: {
179
+ "tool-lookup": { count: 0, cents: 0 },
180
+ "prefix-changed": { count: 0, cents: 0 },
181
+ },
182
+ };
183
+ for (const turn of turns) {
184
+ if (turn.restart) {
185
+ restarts.count += 1;
186
+ restarts.cents += turn.restart.cents;
187
+ restarts.byCause[turn.restart.cause].count += 1;
188
+ restarts.byCause[turn.restart.cause].cents += turn.restart.cents;
189
+ }
190
+ for (const call of turn.toolCalls) {
191
+ const tool = tools.get(call.name) ?? {
192
+ name: call.name,
193
+ calls: 0,
194
+ failed: 0,
195
+ error: null,
196
+ };
197
+ tool.calls += 1;
198
+ if (call.status === "error") {
199
+ tool.failed += 1;
200
+ tool.error ??= call.errorMessage;
201
+ }
202
+ tools.set(call.name, tool);
203
+ }
204
+ }
205
+ return {
206
+ tools: [...tools.values()].sort((a, b) => b.calls - a.calls),
207
+ restarts,
208
+ };
209
+ }
210
+ async function tracesByRun(runIds) {
211
+ const traces = new Map();
212
+ if (runIds.length === 0)
213
+ return traces;
214
+ await ensureObservabilityTables();
215
+ const { rows } = await getDbExec().execute({
216
+ sql: `SELECT run_id, span_type, name, status, error_message, duration_ms,
217
+ created_at, input_tokens, output_tokens, cache_read_tokens,
218
+ cache_write_tokens
219
+ FROM agent_trace_spans
220
+ WHERE run_id IN (${runIds.map(() => "?").join(", ")})
221
+ ORDER BY created_at ASC`,
222
+ args: runIds,
223
+ });
224
+ const spansByRun = new Map();
225
+ const runSpans = new Map();
226
+ for (const row of rows) {
227
+ const runId = String(row.run_id);
228
+ if (row.span_type === "agent_run") {
229
+ runSpans.set(runId, {
230
+ status: String(row.status),
231
+ durationMs: numberField(row, "duration_ms"),
232
+ });
233
+ continue;
234
+ }
235
+ const spans = spansByRun.get(runId) ?? [];
236
+ spans.push({
237
+ spanType: String(row.span_type),
238
+ name: String(row.name),
239
+ status: String(row.status),
240
+ errorMessage: typeof row.error_message === "string" ? row.error_message : null,
241
+ durationMs: numberField(row, "duration_ms"),
242
+ createdAt: numberField(row, "created_at"),
243
+ tokens: tokensFromRow(row),
244
+ });
245
+ spansByRun.set(runId, spans);
246
+ }
247
+ for (const runId of runIds) {
248
+ const run = runSpans.get(runId);
249
+ traces.set(runId, {
250
+ status: !run ? "unknown" : run.status === "error" ? "error" : "success",
251
+ durationMs: run?.durationMs || null,
252
+ turns: buildTurns(spansByRun.get(runId) ?? []),
253
+ });
254
+ }
255
+ return traces;
256
+ }
257
+ async function scoresByRun(runIds) {
258
+ const scores = new Map();
259
+ if (runIds.length === 0)
260
+ return scores;
261
+ await ensureObservabilityTables();
262
+ const { rows } = await getDbExec().execute({
263
+ sql: `SELECT run_id, eval_type, criteria, score, reasoning FROM agent_evals
264
+ WHERE run_id IN (${runIds.map(() => "?").join(", ")})
265
+ ORDER BY created_at ASC`,
266
+ args: runIds,
267
+ });
268
+ for (const row of rows) {
269
+ const runId = String(row.run_id);
270
+ const list = scores.get(runId) ?? [];
271
+ list.push({
272
+ source: SCORE_SOURCE[String(row.eval_type)] ?? "heuristic",
273
+ criteria: String(row.criteria),
274
+ score: numberField(row, "score"),
275
+ reasoning: typeof row.reasoning === "string" ? row.reasoning : null,
276
+ });
277
+ scores.set(runId, list);
278
+ }
279
+ return scores;
280
+ }
281
+ async function feedbackByRun(runIds) {
282
+ const feedback = new Map();
283
+ if (runIds.length === 0)
284
+ return feedback;
285
+ await ensureObservabilityTables();
286
+ const { rows } = await getDbExec().execute({
287
+ sql: `SELECT run_id, feedback_type FROM agent_feedback
288
+ WHERE run_id IN (${runIds.map(() => "?").join(", ")})
289
+ AND feedback_type IN ('thumbs_up', 'thumbs_down')
290
+ ORDER BY created_at ASC`,
291
+ args: runIds,
292
+ });
293
+ // Ordered oldest first, so the latest vote wins.
294
+ for (const row of rows) {
295
+ feedback.set(String(row.run_id), row.feedback_type === "thumbs_up" ? "up" : "down");
296
+ }
297
+ return feedback;
298
+ }
299
+ const RUN_COLUMNS = `MIN(id) AS id, MIN(created_at) AS created_at,
300
+ MAX(owner_email) AS owner_email, MAX(app) AS app, MAX(label) AS label,
301
+ MAX(model) AS model, MAX(thread_id) AS thread_id, MAX(task_id) AS task_id,
302
+ SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens,
303
+ SUM(cache_read_tokens) AS cache_read_tokens,
304
+ SUM(cache_write_tokens) AS cache_write_tokens,
305
+ SUM(cost_cents_x100) AS cost_cents_x100`;
306
+ function runListItem(row, cost, trace, prompt, feedback) {
307
+ const tokens = tokensFromRow(row);
308
+ const turns = trace?.turns ?? [];
309
+ const { tools, restarts } = summarizeTurns(turns);
310
+ const status = trace?.status ?? "unknown";
311
+ return {
312
+ runId: String(row.run_id),
313
+ threadId: stringField(row, "thread_id") || null,
314
+ createdAt: numberField(row, "created_at"),
315
+ ownerEmail: stringField(row, "owner_email"),
316
+ label: stringField(row, "label") || "chat",
317
+ model: stringField(row, "model") || "unknown",
318
+ prompt,
319
+ status,
320
+ tokens,
321
+ cost,
322
+ modelCalls: turns.length,
323
+ tools,
324
+ restarts,
325
+ parallel: parallelSavings(turns),
326
+ recoveredErrors: status === "success"
327
+ ? tools.reduce((sum, tool) => sum + tool.failed, 0)
328
+ : 0,
329
+ durationMs: trace?.durationMs ?? null,
330
+ feedback,
331
+ };
332
+ }
333
+ /** Per-run spend, pricing each model's tokens at that model's rate. */
334
+ async function costsByRun(runIds, scope) {
335
+ const costs = new Map();
336
+ if (runIds.length === 0)
337
+ return costs;
338
+ const { rows } = await getDbExec().execute({
339
+ sql: `SELECT run_id, model, ${UNPRICED} AS unpriced,
340
+ SUM(input_tokens) AS input_tokens,
341
+ SUM(output_tokens) AS output_tokens,
342
+ SUM(cache_read_tokens) AS cache_read_tokens,
343
+ SUM(cache_write_tokens) AS cache_write_tokens,
344
+ SUM(cost_cents_x100) AS cost_cents_x100
345
+ FROM token_usage
346
+ WHERE run_id IN (${runIds.map(() => "?").join(", ")}) AND ${scope.where}
347
+ GROUP BY 1, 2, 3`,
348
+ args: [...runIds, ...scope.args],
349
+ });
350
+ for (const row of rows) {
351
+ const runId = String(row.run_id);
352
+ costs.set(runId, addBreakdown(costs.get(runId) ?? EMPTY_BREAKDOWN, recordedBreakdown(row)));
353
+ }
354
+ return costs;
355
+ }
356
+ export async function getUsageInsights(input, accessInput) {
357
+ await ensureUsageTable();
358
+ const sinceDays = Math.max(1, Math.min(365, input.sinceDays ?? 30));
359
+ const sinceMs = Date.now() - sinceDays * DAY_MS;
360
+ const previousSinceMs = sinceMs - sinceDays * DAY_MS;
361
+ const appScope = usageAppScope(accessInput.app.trim());
362
+ const resolved = await resolveScope(accessInput, input.scope === "workspace" ? "workspace" : "me", input.userEmail);
363
+ const scopeWhere = `${appScope.where} AND ${resolved.ownerScope.where}`;
364
+ const scopeArgs = [...appScope.args, ...resolved.ownerScope.args];
365
+ // Only prompt-linked usage, so "spent on N prompts" divides like with like.
366
+ const period = "CASE WHEN created_at >= ? THEN 'current' ELSE 'previous' END";
367
+ const periodWhere = `${scopeWhere} AND created_at >= ? AND run_id IS NOT NULL`;
368
+ const periodArgs = [sinceMs, ...scopeArgs, previousSinceMs];
369
+ const [periodRows, periodRunRows, runRows] = await Promise.all([
370
+ getDbExec().execute({
371
+ sql: `SELECT ${period} AS period, model, ${UNPRICED} AS unpriced,
372
+ SUM(input_tokens) AS input_tokens, SUM(output_tokens) AS output_tokens,
373
+ SUM(cache_read_tokens) AS cache_read_tokens,
374
+ SUM(cache_write_tokens) AS cache_write_tokens,
375
+ SUM(cost_cents_x100) AS cost_cents_x100
376
+ FROM token_usage WHERE ${periodWhere}
377
+ GROUP BY 1, 2, 3`,
378
+ args: periodArgs,
379
+ }),
380
+ getDbExec().execute({
381
+ sql: `SELECT ${period} AS period, COUNT(DISTINCT run_id) AS runs
382
+ FROM token_usage WHERE ${periodWhere}
383
+ GROUP BY 1`,
384
+ args: periodArgs,
385
+ }),
386
+ getDbExec().execute({
387
+ sql: `SELECT run_id, ${RUN_COLUMNS}
388
+ FROM token_usage
389
+ WHERE ${scopeWhere} AND created_at >= ? AND run_id IS NOT NULL
390
+ GROUP BY run_id
391
+ ORDER BY MIN(created_at) DESC
392
+ LIMIT ${RUN_LIMIT}`,
393
+ args: [...scopeArgs, sinceMs],
394
+ }),
395
+ ]);
396
+ const periods = {
397
+ current: { runs: 0, tokens: emptyTokens(), cost: EMPTY_BREAKDOWN },
398
+ previous: { runs: 0, tokens: emptyTokens(), cost: EMPTY_BREAKDOWN },
399
+ };
400
+ for (const row of periodRows.rows) {
401
+ const totals = periods[row.period === "current" ? "current" : "previous"];
402
+ totals.tokens = addTokens(totals.tokens, tokensFromRow(row));
403
+ totals.cost = addBreakdown(totals.cost, recordedBreakdown(row));
404
+ }
405
+ for (const row of periodRunRows.rows) {
406
+ periods[row.period === "current" ? "current" : "previous"].runs =
407
+ numberField(row, "runs");
408
+ }
409
+ const rawRuns = runRows.rows;
410
+ const runIds = rawRuns.map((row) => String(row.run_id));
411
+ const [exchanges, traces, feedback, costs] = await Promise.all([
412
+ loadRunExchanges(rawRuns),
413
+ tracesByRun(runIds),
414
+ feedbackByRun(runIds),
415
+ costsByRun(runIds, { where: scopeWhere, args: scopeArgs }),
416
+ ]);
417
+ return {
418
+ sinceDays,
419
+ current: periods.current,
420
+ previous: periods.previous,
421
+ runs: rawRuns.map((row) => {
422
+ const runId = String(row.run_id);
423
+ return runListItem(row, costs.get(runId) ?? recordedBreakdown(row), traces.get(runId), exchanges.get(runId)?.prompt ?? null, feedback.get(runId) ?? null);
424
+ }),
425
+ };
426
+ }
427
+ export async function getUsageRun(input, accessInput) {
428
+ await ensureUsageTable();
429
+ const appScope = usageAppScope(accessInput.app.trim());
430
+ const resolved = await resolveScope(accessInput, input.scope === "workspace" ? "workspace" : "me", input.userEmail);
431
+ const { rows } = await getDbExec().execute({
432
+ sql: `SELECT run_id, ${RUN_COLUMNS}, COUNT(*) AS row_count
433
+ FROM token_usage
434
+ WHERE run_id = ? AND ${appScope.where} AND ${resolved.ownerScope.where}
435
+ GROUP BY run_id`,
436
+ args: [input.runId, ...appScope.args, ...resolved.ownerScope.args],
437
+ });
438
+ const row = rows[0];
439
+ // The usage row is the access check: spans are only read for a run the
440
+ // caller's scope can already see.
441
+ if (!row || numberField(row, "row_count") === 0)
442
+ return null;
443
+ const runScope = {
444
+ where: `${appScope.where} AND ${resolved.ownerScope.where}`,
445
+ args: [...appScope.args, ...resolved.ownerScope.args],
446
+ };
447
+ const [exchanges, traces, scores, feedback, costs] = await Promise.all([
448
+ loadRunExchanges([row]),
449
+ tracesByRun([input.runId]),
450
+ scoresByRun([input.runId]),
451
+ feedbackByRun([input.runId]),
452
+ costsByRun([input.runId], runScope),
453
+ ]);
454
+ const exchange = exchanges.get(input.runId);
455
+ const trace = traces.get(input.runId);
456
+ return {
457
+ ...runListItem(row, costs.get(input.runId) ?? recordedBreakdown(row), trace, exchange?.prompt ?? null, feedback.get(input.runId) ?? null),
458
+ reply: exchange?.reply ?? null,
459
+ turns: trace?.turns ?? [],
460
+ scores: scores.get(input.runId) ?? [],
461
+ };
462
+ }
@@ -189,13 +189,34 @@ export interface UsageMetricsAccessInput {
189
189
  orgId?: string | null;
190
190
  app: UsageAppSelection;
191
191
  }
192
+ interface MemberRecord {
193
+ email: string;
194
+ role: string | null;
195
+ }
192
196
  interface QueryScope {
193
197
  where: string;
194
198
  args: unknown[];
195
199
  }
200
+ export declare function numberField(row: Record<string, unknown>, key: string): number;
201
+ export declare function stringField(row: Record<string, unknown>, key: string): string;
196
202
  export declare function normalizeUsageAppKey(value: string): string;
197
203
  export declare function usageAppScope(app: string): QueryScope;
198
204
  export declare function canViewWorkspaceUsage(input: Pick<UsageMetricsAccessInput, "ownerEmail" | "orgId">): Promise<boolean>;
205
+ export declare function resolveScope(input: UsageMetricsAccessInput, scope: UsageMetricsScope, requestedUserEmail?: string | null): Promise<{
206
+ ownerScope: QueryScope;
207
+ /** The lowercased owner emails `ownerScope` selects. */
208
+ ownerEmails: string[];
209
+ /** The org predicate inside `ownerScope`, for tables with an `org_id`. */
210
+ orgScope: QueryScope;
211
+ selectedUserEmail: string | null;
212
+ members: MemberRecord[];
213
+ access: UsageMetricsAccess;
214
+ }>;
215
+ /** Each run's own prompt and the agent's reply to it, keyed by run id. */
216
+ export declare function loadRunExchanges(rows: Array<Record<string, unknown>>): Promise<Map<string, {
217
+ prompt: string | null;
218
+ reply: string | null;
219
+ }>>;
199
220
  export declare function listAppUsageMetrics(input: {
200
221
  sinceDays?: number;
201
222
  scope?: UsageMetricsScope;
@@ -1,5 +1,6 @@
1
1
  import { getAppConfig } from "../app-config/index.js";
2
2
  import { getDbExec } from "../db/client.js";
3
+ import { splitAgentChatContextFromMessage } from "../shared/agent-chat-context.js";
3
4
  import { ForbiddenError } from "../sharing/access.js";
4
5
  import { isSelfScopedUsageRead, usageOrgScope } from "./org-scope.js";
5
6
  import { builderCreditsFromCostCents, ensureUsageTable, resolveUsageAppKey, usageBillingForEngine, MIXED_USAGE_BILLING, } from "./store.js";
@@ -15,10 +16,10 @@ export const USAGE_APP_FILTER_CURRENT = "current";
15
16
  /** Key of the series that folds every key past a breakdown's limit. */
16
17
  export const USAGE_OTHER_BREAKDOWN_KEY = "other";
17
18
  const NO_APP_FILTER = { where: "", args: [] };
18
- function numberField(row, key) {
19
+ export function numberField(row, key) {
19
20
  return Number(row[key] ?? 0) || 0;
20
21
  }
21
- function stringField(row, key) {
22
+ export function stringField(row, key) {
22
23
  return String(row[key] ?? "");
23
24
  }
24
25
  function nullableStringField(row, key) {
@@ -123,7 +124,7 @@ export async function canViewWorkspaceUsage(input) {
123
124
  const role = await getOrgRole(input.orgId?.trim() || null, normalizeEmail(input.ownerEmail));
124
125
  return role === "owner" || role === "admin";
125
126
  }
126
- async function resolveScope(input, scope, requestedUserEmail) {
127
+ export async function resolveScope(input, scope, requestedUserEmail) {
127
128
  const viewerEmail = normalizeEmail(input.ownerEmail);
128
129
  const orgId = input.orgId?.trim() || null;
129
130
  const role = await getOrgRole(orgId, viewerEmail);
@@ -525,7 +526,11 @@ function parseJson(value) {
525
526
  return null;
526
527
  }
527
528
  }
529
+ /** Message text without the hidden `<context>` block apps append to prompts. */
528
530
  function promptText(value) {
531
+ return splitAgentChatContextFromMessage(rawMessageText(value)).message;
532
+ }
533
+ function rawMessageText(value) {
529
534
  if (typeof value === "string")
530
535
  return value.trim();
531
536
  if (!Array.isArray(value))
@@ -649,6 +654,65 @@ function promptForTurn(index, taskId, usageCreatedAt) {
649
654
  ? (index.timestampedPrompts[low - 1]?.prompt ?? null)
650
655
  : index.soleUntimestampedPrompt;
651
656
  }
657
+ /** Each run's own prompt and the agent's reply to it, keyed by run id. */
658
+ export async function loadRunExchanges(rows) {
659
+ const exchanges = new Map();
660
+ const threadRefs = [
661
+ ...new Map(rows
662
+ .map((row) => ({
663
+ id: nullableStringField(row, "thread_id"),
664
+ ownerEmail: nullableStringField(row, "owner_email"),
665
+ }))
666
+ .filter((ref) => Boolean(ref.id && ref.ownerEmail))
667
+ .map((ref) => [JSON.stringify([ref.id, ref.ownerEmail]), ref])).values(),
668
+ ];
669
+ if (threadRefs.length === 0)
670
+ return exchanges;
671
+ let threadRows = [];
672
+ try {
673
+ const result = await getDbExec().execute({
674
+ sql: `SELECT id, thread_data FROM chat_threads WHERE ${threadRefs.map(() => "(id = ? AND LOWER(owner_email) = LOWER(?))").join(" OR ")}`,
675
+ args: threadRefs.flatMap((ref) => [ref.id, ref.ownerEmail]),
676
+ });
677
+ threadRows = result.rows;
678
+ }
679
+ catch {
680
+ return exchanges;
681
+ }
682
+ const threads = new Map(threadRows.map((row) => [String(row.id), row.thread_data]));
683
+ for (const row of rows) {
684
+ const runId = nullableStringField(row, "run_id");
685
+ const threadId = nullableStringField(row, "thread_id");
686
+ const threadData = threadId ? threads.get(threadId) : undefined;
687
+ if (!runId || threadData === undefined)
688
+ continue;
689
+ const taskId = nullableStringField(row, "task_id");
690
+ const index = indexThreadPrompts(threadData);
691
+ const prompt = index
692
+ ? promptForTurn(index, taskId, numberField(row, "created_at"))
693
+ : null;
694
+ exchanges.set(runId, {
695
+ prompt: prompt?.prompt ?? null,
696
+ reply: taskId ? replyForTurn(threadData, taskId) : null,
697
+ });
698
+ }
699
+ return exchanges;
700
+ }
701
+ function replyForTurn(threadData, taskId) {
702
+ const messages = parseJson(threadData)?.messages;
703
+ if (!Array.isArray(messages))
704
+ return null;
705
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
706
+ const message = messageRecord(messages[i]);
707
+ if (message?.role !== "assistant" || messageTurnId(message) !== taskId) {
708
+ continue;
709
+ }
710
+ const text = promptText(message.content);
711
+ if (text)
712
+ return text.length > 1200 ? `${text.slice(0, 1199).trimEnd()}…` : text;
713
+ }
714
+ return null;
715
+ }
652
716
  async function hydrateRecentPrompts(rows, builderCreditsEnabled, legacyRowsUseBuilder) {
653
717
  const recentLimit = 12;
654
718
  const threadRefs = [
@@ -189,6 +189,14 @@ const CORE_SHARING_ACTIONS = [
189
189
  name: "get-usage-metrics",
190
190
  specifier: "@agent-native/core/usage/actions/get-usage-metrics",
191
191
  },
192
+ {
193
+ name: "get-usage-insights",
194
+ specifier: "@agent-native/core/usage/actions/get-usage-insights",
195
+ },
196
+ {
197
+ name: "get-usage-run",
198
+ specifier: "@agent-native/core/usage/actions/get-usage-run",
199
+ },
192
200
  {
193
201
  name: "get-builder-credit-usage",
194
202
  specifier: "@agent-native/core/usage/actions/get-builder-credit-usage",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.198.2",
3
+ "version": "0.198.3-nightly-20260930124743",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -389,6 +389,8 @@
389
389
  "./usage/actions/get-usage-alerts": "./dist/usage/actions/get-usage-alerts.js",
390
390
  "./usage/actions/manage-usage-alert": "./dist/usage/actions/manage-usage-alert.js",
391
391
  "./usage/actions/get-usage-metrics": "./dist/usage/actions/get-usage-metrics.js",
392
+ "./usage/actions/get-usage-insights": "./dist/usage/actions/get-usage-insights.js",
393
+ "./usage/actions/get-usage-run": "./dist/usage/actions/get-usage-run.js",
392
394
  "./usage/actions/get-builder-credit-usage": "./dist/usage/actions/get-builder-credit-usage.js",
393
395
  "./usage/actions/get-builder-credit-status": "./dist/usage/actions/get-builder-credit-status.js",
394
396
  "./usage/actions/get-builder-referral-info": "./dist/usage/actions/get-builder-referral-info.js",
@@ -655,9 +657,9 @@
655
657
  "undici": "^7.28.0",
656
658
  "y-protocols": "^1.0.7",
657
659
  "yaml": "^2.9.0",
658
- "yjs": "^13.6.32",
660
+ "yjs": "^13.6.33",
659
661
  "zod": "^4.3.6",
660
- "@agent-native/agentkit": "^0.198.2",
662
+ "@agent-native/agentkit": "^0.198.3-nightly-20260930124743",
661
663
  "@agent-native/recap-cli": "^0.5.52"
662
664
  },
663
665
  "devDependencies": {
@@ -730,7 +732,7 @@
730
732
  "xlsx": "^0.18.5"
731
733
  },
732
734
  "peerDependencies": {
733
- "@agent-native/core-corpus": ">=0.196.0",
735
+ "@agent-native/core-corpus": "0.198.3-nightly-20260930124743",
734
736
  "@ai-sdk/anthropic": ">=3",
735
737
  "@ai-sdk/cohere": ">=3",
736
738
  "@ai-sdk/google": ">=3",