@agent-native/core 0.157.7 → 0.157.9
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
- package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +258 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +59 -27
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
- package/corpus/templates/slides/actions/export-pptx.ts +261 -71
- package/corpus/templates/slides/actions/import-file.ts +12 -0
- package/corpus/templates/slides/actions/import-pptx.ts +9 -0
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +45 -5
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +69 -1
- package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +316 -64
- package/corpus/templates/slides/server/lib/source-import.ts +10 -1
- package/dist/agent/harness/migrations.d.ts +4 -0
- package/dist/agent/harness/migrations.js +39 -0
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.js +4 -0
- package/dist/agent/run-loop-with-resume.d.ts +1 -0
- package/dist/agent/run-loop-with-resume.js +45 -30
- package/dist/agent/run-migrations.d.ts +8 -0
- package/dist/agent/run-migrations.js +61 -0
- package/dist/chat-threads/migrations.d.ts +1 -0
- package/dist/chat-threads/migrations.js +1 -0
- package/dist/chat-threads/schema-migrations.d.ts +8 -0
- package/dist/chat-threads/schema-migrations.js +60 -0
- package/dist/client/AssistantChat.js +4 -0
- package/dist/client/chat/message-components.d.ts +2 -0
- package/dist/client/chat/message-components.js +11 -1
- package/dist/client/observability/ThumbsFeedback.js +35 -22
- package/dist/client/observability/useObservability.d.ts +1 -1
- package/dist/client/observability/useObservability.js +6 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/feature-flags/a2a-action-route.js +4 -2
- package/dist/feature-flags/permissions.d.ts +2 -0
- package/dist/feature-flags/permissions.js +6 -1
- package/dist/ingestion/pptx.d.ts +14 -1
- package/dist/ingestion/pptx.js +222 -41
- package/dist/localization/core-messages/ar-SA.js +4 -0
- package/dist/localization/core-messages/de-DE.js +4 -0
- package/dist/localization/core-messages/en-US.d.ts +4 -0
- package/dist/localization/core-messages/en-US.js +4 -0
- package/dist/localization/core-messages/es-ES.js +4 -0
- package/dist/localization/core-messages/fr-FR.js +4 -0
- package/dist/localization/core-messages/hi-IN.js +4 -0
- package/dist/localization/core-messages/ja-JP.js +4 -0
- package/dist/localization/core-messages/ko-KR.js +4 -0
- package/dist/localization/core-messages/pt-BR.js +4 -0
- package/dist/localization/core-messages/zh-CN.js +4 -0
- package/dist/localization/core-messages/zh-TW.js +4 -0
- package/dist/localization/core-messages.d.ts +4 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/feedback.js +40 -16
- package/dist/observability/routes.d.ts +1 -1
- package/dist/observability/routes.js +15 -3
- package/dist/observability/store.js +2 -0
- package/dist/observability/traces.js +5 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-routes.d.ts +7 -5
- package/dist/server/action-routes.js +9 -4
- package/dist/server/better-auth-instance.js +4 -0
- package/dist/server/poll.d.ts +4 -4
- package/dist/server/poll.js +45 -13
- package/dist/server/realtime-voice.js +2 -1
- package/dist/server/release-migrations.js +16 -0
- package/dist/usage/migrations.d.ts +4 -0
- package/dist/usage/migrations.js +45 -0
- package/package.json +3 -3
|
@@ -30,6 +30,16 @@ export async function submitFeedback(opts) {
|
|
|
30
30
|
await insertFeedback(entry);
|
|
31
31
|
return entry;
|
|
32
32
|
}
|
|
33
|
+
function unwrapPersistedThreadMessage(value) {
|
|
34
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const record = value;
|
|
38
|
+
const nested = record.message;
|
|
39
|
+
return nested && typeof nested === "object" && !Array.isArray(nested)
|
|
40
|
+
? nested
|
|
41
|
+
: record;
|
|
42
|
+
}
|
|
33
43
|
async function getThreadMessages(threadId) {
|
|
34
44
|
await ensureObservabilityTables();
|
|
35
45
|
const client = getDbExec();
|
|
@@ -47,22 +57,36 @@ async function getThreadMessages(threadId) {
|
|
|
47
57
|
const messages = data.messages ?? data;
|
|
48
58
|
if (!Array.isArray(messages))
|
|
49
59
|
return [];
|
|
50
|
-
return messages
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
return messages.flatMap((value) => {
|
|
61
|
+
const message = unwrapPersistedThreadMessage(value);
|
|
62
|
+
if (!message)
|
|
63
|
+
return [];
|
|
64
|
+
const content = message.content;
|
|
65
|
+
if ((message.role !== "user" && message.role !== "assistant") ||
|
|
66
|
+
(typeof content !== "string" && !Array.isArray(content))) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const text = typeof content === "string"
|
|
70
|
+
? content
|
|
71
|
+
: content
|
|
72
|
+
.filter((part) => Boolean(part) &&
|
|
73
|
+
typeof part === "object" &&
|
|
74
|
+
!Array.isArray(part) &&
|
|
75
|
+
part.type === "text")
|
|
76
|
+
.map((part) => (typeof part.text === "string" ? part.text : ""))
|
|
77
|
+
.join("");
|
|
78
|
+
if (!text)
|
|
79
|
+
return [];
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
role: message.role,
|
|
83
|
+
content: text,
|
|
84
|
+
createdAt: typeof message.createdAt === "number"
|
|
85
|
+
? message.createdAt
|
|
86
|
+
: undefined,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
});
|
|
66
90
|
}
|
|
67
91
|
catch {
|
|
68
92
|
return [];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* GET /traces/:runId — get trace detail (spans + summary)
|
|
9
9
|
* GET /traces/:runId/evals — get evals for a run
|
|
10
10
|
* POST /feedback — submit feedback
|
|
11
|
-
* GET /feedback?since=N&limit=N
|
|
11
|
+
* GET /feedback?since=N&limit=N&feedbackType=text — list feedback entries
|
|
12
12
|
* GET /feedback/stats?since=N — feedback aggregation stats
|
|
13
13
|
* GET /satisfaction?since=N — satisfaction scores
|
|
14
14
|
* GET /evals/stats?since=N — eval stats
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* GET /traces/:runId — get trace detail (spans + summary)
|
|
9
9
|
* GET /traces/:runId/evals — get evals for a run
|
|
10
10
|
* POST /feedback — submit feedback
|
|
11
|
-
* GET /feedback?since=N&limit=N
|
|
11
|
+
* GET /feedback?since=N&limit=N&feedbackType=text — list feedback entries
|
|
12
12
|
* GET /feedback/stats?since=N — feedback aggregation stats
|
|
13
13
|
* GET /satisfaction?since=N — satisfaction scores
|
|
14
14
|
* GET /evals/stats?since=N — eval stats
|
|
@@ -27,6 +27,16 @@ import { track } from "../tracking/registry.js";
|
|
|
27
27
|
import { emitAiFeedbackSurveyEvent } from "./posthog-ai.js";
|
|
28
28
|
import { getObservabilityOverview, getTraceSummaries, getTraceSummary, getTraceSpansForRun, getEvalsForRun, insertFeedback, getFeedback, getFeedbackStats, getSatisfactionScores, getEvalStats, listExperiments, insertExperiment, getExperiment, updateExperiment, getExperimentResults, } from "./store.js";
|
|
29
29
|
import { trackingIdentityProperties } from "./tracking-identity.js";
|
|
30
|
+
const FEEDBACK_TYPES = [
|
|
31
|
+
"thumbs_up",
|
|
32
|
+
"thumbs_down",
|
|
33
|
+
"category",
|
|
34
|
+
"text",
|
|
35
|
+
];
|
|
36
|
+
function isFeedbackType(value) {
|
|
37
|
+
return (typeof value === "string" &&
|
|
38
|
+
FEEDBACK_TYPES.includes(value));
|
|
39
|
+
}
|
|
30
40
|
function nanoid(size = 21) {
|
|
31
41
|
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
32
42
|
let id = "";
|
|
@@ -143,8 +153,7 @@ export function createObservabilityHandler() {
|
|
|
143
153
|
return { error: "Invalid JSON body" };
|
|
144
154
|
}
|
|
145
155
|
const feedbackType = body?.feedbackType;
|
|
146
|
-
if (!feedbackType
|
|
147
|
-
!["thumbs_up", "thumbs_down", "category", "text"].includes(feedbackType)) {
|
|
156
|
+
if (!isFeedbackType(feedbackType)) {
|
|
148
157
|
setResponseStatus(event, 400);
|
|
149
158
|
return { error: "feedbackType is required" };
|
|
150
159
|
}
|
|
@@ -229,6 +238,9 @@ export function createObservabilityHandler() {
|
|
|
229
238
|
return getFeedback({
|
|
230
239
|
sinceMs: parseSince(q),
|
|
231
240
|
limit: parseLimit(q),
|
|
241
|
+
feedbackType: isFeedbackType(q.feedbackType)
|
|
242
|
+
? q.feedbackType
|
|
243
|
+
: undefined,
|
|
232
244
|
userId: owner,
|
|
233
245
|
});
|
|
234
246
|
}
|
|
@@ -197,6 +197,7 @@ export async function ensureObservabilityTables() {
|
|
|
197
197
|
await ensureIndexExists("idx_feedback_thread", `CREATE INDEX IF NOT EXISTS idx_feedback_thread ON agent_feedback (thread_id)`);
|
|
198
198
|
await ensureIndexExists("idx_feedback_created", `CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`);
|
|
199
199
|
await ensureIndexExists("idx_feedback_user", `CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`);
|
|
200
|
+
await ensureIndexExists("idx_feedback_type_created", `CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`);
|
|
200
201
|
await ensureIndexExists("idx_satisfaction_thread", `CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`);
|
|
201
202
|
await ensureIndexExists("idx_satisfaction_user", `CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`);
|
|
202
203
|
await ensureIndexExists("idx_evals_run", `CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`);
|
|
@@ -252,6 +253,7 @@ export async function ensureObservabilityTables() {
|
|
|
252
253
|
`CREATE INDEX IF NOT EXISTS idx_feedback_thread ON agent_feedback (thread_id)`,
|
|
253
254
|
`CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`,
|
|
254
255
|
`CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`,
|
|
256
|
+
`CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`,
|
|
255
257
|
`CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`,
|
|
256
258
|
`CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`,
|
|
257
259
|
`CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`,
|
|
@@ -637,7 +637,11 @@ export async function instrumentAgentLoop(opts) {
|
|
|
637
637
|
catch { }
|
|
638
638
|
let llmCallCount = 0;
|
|
639
639
|
if (usage || runStatus === "error") {
|
|
640
|
-
llmCallCount =
|
|
640
|
+
llmCallCount =
|
|
641
|
+
usage?.llmCalls ??
|
|
642
|
+
// Compatibility for custom loop implementations that predate the
|
|
643
|
+
// attempt counter: a measured run still counts as one call.
|
|
644
|
+
1;
|
|
641
645
|
const generationUsage = usage ?? {
|
|
642
646
|
inputTokens: 0,
|
|
643
647
|
outputTokens: 0,
|
|
@@ -75,10 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
|
|
78
|
+
deleted?: undefined;
|
|
79
79
|
id?: undefined;
|
|
80
80
|
provider?: undefined;
|
|
81
|
-
deleted?: undefined;
|
|
82
81
|
providers: {
|
|
83
82
|
id: string;
|
|
84
83
|
label: string;
|
|
@@ -92,41 +91,42 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
92
91
|
found?: undefined;
|
|
93
92
|
registered?: undefined;
|
|
94
93
|
label?: undefined;
|
|
95
|
-
} | {
|
|
96
94
|
message?: undefined;
|
|
97
|
-
|
|
98
|
-
id?: undefined;
|
|
95
|
+
} | {
|
|
99
96
|
deleted?: undefined;
|
|
97
|
+
id?: undefined;
|
|
100
98
|
providers?: undefined;
|
|
99
|
+
count?: undefined;
|
|
101
100
|
found: boolean;
|
|
102
101
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
103
102
|
registered?: undefined;
|
|
104
103
|
label?: undefined;
|
|
105
|
-
} | {
|
|
106
104
|
message?: undefined;
|
|
107
|
-
|
|
108
|
-
provider?: undefined;
|
|
105
|
+
} | {
|
|
109
106
|
deleted?: undefined;
|
|
107
|
+
provider?: undefined;
|
|
110
108
|
providers?: undefined;
|
|
109
|
+
count?: undefined;
|
|
111
110
|
found: boolean;
|
|
112
111
|
id: string;
|
|
113
112
|
registered?: undefined;
|
|
114
113
|
label?: undefined;
|
|
115
|
-
} | {
|
|
116
114
|
message?: undefined;
|
|
117
|
-
|
|
115
|
+
} | {
|
|
118
116
|
provider?: undefined;
|
|
119
117
|
providers?: undefined;
|
|
118
|
+
count?: undefined;
|
|
120
119
|
found?: undefined;
|
|
121
120
|
deleted: boolean;
|
|
122
121
|
id: string;
|
|
123
122
|
registered?: undefined;
|
|
124
123
|
label?: undefined;
|
|
124
|
+
message?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
count?: undefined;
|
|
127
|
-
provider?: undefined;
|
|
128
126
|
deleted?: undefined;
|
|
127
|
+
provider?: undefined;
|
|
129
128
|
providers?: undefined;
|
|
129
|
+
count?: undefined;
|
|
130
130
|
found?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
@@ -311,8 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
311
311
|
notes?: string | undefined;
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
|
-
|
|
314
|
+
message?: undefined;
|
|
315
315
|
id?: undefined;
|
|
316
|
+
deleted?: undefined;
|
|
316
317
|
providers: {
|
|
317
318
|
id: string;
|
|
318
319
|
label: string;
|
|
@@ -327,41 +328,40 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
327
328
|
found?: undefined;
|
|
328
329
|
registered?: undefined;
|
|
329
330
|
label?: undefined;
|
|
330
|
-
message?: undefined;
|
|
331
331
|
} | {
|
|
332
|
-
|
|
332
|
+
message?: undefined;
|
|
333
|
+
count?: undefined;
|
|
333
334
|
id?: undefined;
|
|
335
|
+
deleted?: undefined;
|
|
334
336
|
providers?: undefined;
|
|
335
|
-
count?: undefined;
|
|
336
337
|
found: boolean;
|
|
337
338
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
338
339
|
registered?: undefined;
|
|
339
340
|
label?: undefined;
|
|
340
|
-
message?: undefined;
|
|
341
341
|
} | {
|
|
342
|
+
message?: undefined;
|
|
343
|
+
count?: undefined;
|
|
342
344
|
deleted?: undefined;
|
|
343
345
|
providers?: undefined;
|
|
344
|
-
count?: undefined;
|
|
345
346
|
provider?: undefined;
|
|
346
347
|
found: boolean;
|
|
347
348
|
id: string;
|
|
348
349
|
registered?: undefined;
|
|
349
350
|
label?: undefined;
|
|
350
|
-
message?: undefined;
|
|
351
351
|
} | {
|
|
352
|
-
|
|
352
|
+
message?: undefined;
|
|
353
353
|
count?: undefined;
|
|
354
|
+
providers?: undefined;
|
|
354
355
|
provider?: undefined;
|
|
355
356
|
found?: undefined;
|
|
356
357
|
deleted: boolean;
|
|
357
358
|
id: string;
|
|
358
359
|
registered?: undefined;
|
|
359
360
|
label?: undefined;
|
|
360
|
-
message?: undefined;
|
|
361
361
|
} | {
|
|
362
|
+
count?: undefined;
|
|
362
363
|
deleted?: undefined;
|
|
363
364
|
providers?: undefined;
|
|
364
|
-
count?: undefined;
|
|
365
365
|
provider?: undefined;
|
|
366
366
|
found?: undefined;
|
|
367
367
|
registered: boolean;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
error: string;
|
|
52
51
|
ok?: undefined;
|
|
52
|
+
error: string;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
|
@@ -14,12 +14,14 @@ export type ActionRouteResolvedCaller = AgentRunOwnerContext & {
|
|
|
14
14
|
* Org to scope the request to, verified from the same credential as the
|
|
15
15
|
* caller identity (e.g. the A2A token's org claim). When omitted, the org
|
|
16
16
|
* is derived from the verified owner email via the framework's owner→org
|
|
17
|
-
* membership lookup.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* membership lookup. An explicit `null` means the verified caller has no
|
|
18
|
+
* org and must not fall back to another membership. The ambient session/org
|
|
19
|
+
* state on the request is never consulted for adapter-resolved callers: a
|
|
20
|
+
* request can carry both a valid A2A bearer and an unrelated browser cookie,
|
|
21
|
+
* and the cookie user's org must not leak into the token caller's request
|
|
22
|
+
* context.
|
|
21
23
|
*/
|
|
22
|
-
orgId?: string;
|
|
24
|
+
orgId?: string | null;
|
|
23
25
|
/** Verified A2A correlation and issuer metadata for the audit row. */
|
|
24
26
|
delegationJti?: string;
|
|
25
27
|
delegationIssuer?: string;
|
|
@@ -3,6 +3,7 @@ import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
|
|
|
3
3
|
import { isActionContractError, isAgentActionStopError } from "../action.js";
|
|
4
4
|
import { isTransientDatabaseError } from "../db/client.js";
|
|
5
5
|
import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
|
|
6
|
+
import { isFeatureFlagAdminEmail } from "../feature-flags/permissions.js";
|
|
6
7
|
import { resolveOrgByDomain, resolveOrgIdForEmail } from "../org/context.js";
|
|
7
8
|
import { readBody } from "../server/h3-helpers.js";
|
|
8
9
|
import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
|
|
@@ -55,7 +56,7 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
|
|
|
55
56
|
if (!claims || !claims.scope.includes(required))
|
|
56
57
|
throw new Error("Invalid feature flag delegation");
|
|
57
58
|
const localOrg = await resolveOrgByDomain(claims.orgDomain);
|
|
58
|
-
if (!localOrg)
|
|
59
|
+
if (!localOrg && !isFeatureFlagAdminEmail(claims.email))
|
|
59
60
|
throw new Error("Invalid feature flag delegation");
|
|
60
61
|
if (actionName === "set-feature-flag" &&
|
|
61
62
|
(await consumeOneTimeJti(claims.jti))) {
|
|
@@ -63,7 +64,7 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
|
|
|
63
64
|
}
|
|
64
65
|
return {
|
|
65
66
|
owner: claims.email,
|
|
66
|
-
orgId: localOrg
|
|
67
|
+
orgId: localOrg?.orgId ?? null,
|
|
67
68
|
anonymous: false,
|
|
68
69
|
delegationJti: claims.jti,
|
|
69
70
|
delegationIssuer: claims.issuer,
|
|
@@ -362,7 +363,8 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
362
363
|
}
|
|
363
364
|
// Org scoping. For adapter-resolved callers the org must come
|
|
364
365
|
// exclusively from the verified credential: the adapter-asserted
|
|
365
|
-
// orgId when present,
|
|
366
|
+
// orgId when present, explicit null when the caller has no org,
|
|
367
|
+
// otherwise the owner-email membership lookup.
|
|
366
368
|
// The request's ambient session/org state (`resolveOrgId`, usually
|
|
367
369
|
// getSession-backed) is deliberately NOT consulted — a request can
|
|
368
370
|
// carry both a valid A2A bearer and an unrelated same-origin browser
|
|
@@ -372,7 +374,10 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
372
374
|
let orgId;
|
|
373
375
|
if (resolvedCaller) {
|
|
374
376
|
orgId = normalizeOrgId(resolvedCaller.orgId);
|
|
375
|
-
if (
|
|
377
|
+
if (resolvedCaller.orgId !== null &&
|
|
378
|
+
!orgId &&
|
|
379
|
+
resolvedCaller.owner &&
|
|
380
|
+
!resolvedCaller.anonymous) {
|
|
376
381
|
orgId = await storedActiveOrgId(resolvedCaller.owner);
|
|
377
382
|
}
|
|
378
383
|
}
|
|
@@ -854,6 +854,10 @@ async function createBetterAuthInstance(config) {
|
|
|
854
854
|
basePath,
|
|
855
855
|
baseURL: appUrl,
|
|
856
856
|
database,
|
|
857
|
+
// Auth schema relations are intentionally not registered here. Keep the
|
|
858
|
+
// experimental relational-query path off so a bundled Drizzle adapter
|
|
859
|
+
// cannot recurse while resolving a session or account join.
|
|
860
|
+
experimental: { joins: false },
|
|
857
861
|
secret,
|
|
858
862
|
emailAndPassword: {
|
|
859
863
|
enabled: true,
|
package/dist/server/poll.d.ts
CHANGED
|
@@ -233,10 +233,10 @@ export declare class AppSyncState {
|
|
|
233
233
|
* bounded index range scan.
|
|
234
234
|
*
|
|
235
235
|
* The `lastDurablePrune` throttle below is per-process, so every serverless
|
|
236
|
-
* worker prunes on its first poll.
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
236
|
+
* worker prunes on its first poll. Postgres workers also take a
|
|
237
|
+
* transaction-scoped advisory lease for each batch: only one worker deletes
|
|
238
|
+
* at a time, but each batch still commits independently so a killed worker
|
|
239
|
+
* cannot hold a long transaction open.
|
|
240
240
|
*/
|
|
241
241
|
private pruneDurableEvents;
|
|
242
242
|
persistSyncEvent(event: ChangeEvent, dedupeKey?: string, presetId?: string): Promise<void>;
|
package/dist/server/poll.js
CHANGED
|
@@ -42,6 +42,12 @@ const DURABLE_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
|
42
42
|
*/
|
|
43
43
|
const DURABLE_PRUNE_BATCH = 10_000;
|
|
44
44
|
const DURABLE_PRUNE_MAX_BATCHES = 40;
|
|
45
|
+
/**
|
|
46
|
+
* Postgres advisory-lock key for the per-database retention worker. The lock
|
|
47
|
+
* is transaction-scoped so a serverless worker that is killed cannot leave a
|
|
48
|
+
* lease behind for another worker to clear.
|
|
49
|
+
*/
|
|
50
|
+
const DURABLE_PRUNE_LOCK_KEY = "agent-native:sync-events-prune";
|
|
45
51
|
/**
|
|
46
52
|
* How far back a cold-started process replays durable action markers.
|
|
47
53
|
* Covers the gap between a separate action process writing its marker and this
|
|
@@ -455,10 +461,10 @@ export class AppSyncState {
|
|
|
455
461
|
* bounded index range scan.
|
|
456
462
|
*
|
|
457
463
|
* The `lastDurablePrune` throttle below is per-process, so every serverless
|
|
458
|
-
* worker prunes on its first poll.
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
464
|
+
* worker prunes on its first poll. Postgres workers also take a
|
|
465
|
+
* transaction-scoped advisory lease for each batch: only one worker deletes
|
|
466
|
+
* at a time, but each batch still commits independently so a killed worker
|
|
467
|
+
* cannot hold a long transaction open.
|
|
462
468
|
*/
|
|
463
469
|
async pruneDurableEvents(client) {
|
|
464
470
|
const now = Date.now();
|
|
@@ -469,17 +475,43 @@ export class AppSyncState {
|
|
|
469
475
|
let deleted = 0;
|
|
470
476
|
try {
|
|
471
477
|
for (let batch = 0; batch < DURABLE_PRUNE_MAX_BATCHES; batch++) {
|
|
472
|
-
const
|
|
478
|
+
const runBatch = async (tx) => {
|
|
479
|
+
if (this.isPg()) {
|
|
480
|
+
const lockResult = await tx.execute({
|
|
481
|
+
sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
|
|
482
|
+
args: [DURABLE_PRUNE_LOCK_KEY],
|
|
483
|
+
});
|
|
484
|
+
const acquired = lockResult.rows[0]?.acquired;
|
|
485
|
+
if (acquired !== true && acquired !== "t")
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
473
488
|
// `id IN (...)` rather than ctid/rowid: both are dialect-specific,
|
|
474
489
|
// and the primary key is indexed on every dialect we ship.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
490
|
+
const result = await tx.execute({
|
|
491
|
+
sql: `DELETE FROM sync_events WHERE id IN (
|
|
492
|
+
SELECT id FROM sync_events WHERE created_at < ?
|
|
493
|
+
ORDER BY created_at, id LIMIT ?
|
|
494
|
+
)`,
|
|
495
|
+
args: [cutoff, DURABLE_PRUNE_BATCH],
|
|
496
|
+
});
|
|
497
|
+
return result.rowsAffected;
|
|
498
|
+
};
|
|
499
|
+
// A Postgres advisory transaction lock is only useful when the lock
|
|
500
|
+
// and delete share a connection and transaction. Never fall back to
|
|
501
|
+
// an uncoordinated delete on a transactionless client.
|
|
502
|
+
const rowsAffected = this.isPg()
|
|
503
|
+
? client.transaction
|
|
504
|
+
? await client.transaction(runBatch)
|
|
505
|
+
: null
|
|
506
|
+
: await runBatch(client);
|
|
507
|
+
if (rowsAffected == null) {
|
|
508
|
+
if (this.isPg() && !client.transaction) {
|
|
509
|
+
throw new Error("Postgres prune requires a database transaction");
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
deleted += rowsAffected;
|
|
514
|
+
if (rowsAffected < DURABLE_PRUNE_BATCH)
|
|
483
515
|
break;
|
|
484
516
|
}
|
|
485
517
|
this.durablePruneFailures = 0;
|
|
@@ -25,7 +25,7 @@ export const REALTIME_VOICE_CAPABILITY_HEADER = "X-Agent-Native-Realtime-Capabil
|
|
|
25
25
|
const OPENAI_REALTIME_CALLS_URL = "https://api.openai.com/v1/realtime/calls";
|
|
26
26
|
const DEFAULT_MODEL = "gpt-realtime-2.1";
|
|
27
27
|
const DEFAULT_VOICE = "marin";
|
|
28
|
-
const DEFAULT_INSTRUCTIONS = "You are the live voice interface for this Agent Native app. Speak naturally, briefly, and conversationally. Use the available function tools when the user asks you to navigate or take an action. Never claim an action succeeded until its tool result confirms success. If a tool requires approval, explain that the user must approve it in chat.";
|
|
28
|
+
const DEFAULT_INSTRUCTIONS = "You are the live voice interface for this Agent Native app. Speak naturally, briefly, and conversationally. Use the available function tools when the user asks you to navigate or take an action. When the user asks about a previous conversation, saved chat details, or something they told you before, search with the `chat-history` tool before saying you cannot access it. Summarize a matching result and open a thread only when the user asks. If the user repeats a request, acknowledge the prior attempt and finish or correct the missing part instead of restarting from scratch or asking the same clarification again. Never claim an action succeeded until its tool result confirms success. If a tool requires approval, explain that the user must approve it in chat.";
|
|
29
29
|
const MAX_INSTRUCTIONS_CHARS = 16_000;
|
|
30
30
|
const MAX_TOOL_DESCRIPTION_CHARS = 2_000;
|
|
31
31
|
const MAX_APPROVAL_KEY_CHARS = 1_024;
|
|
@@ -61,6 +61,7 @@ const REALTIME_VOICE_PRIORITY_TOOLS = [
|
|
|
61
61
|
"set-url-path",
|
|
62
62
|
"set-search-params",
|
|
63
63
|
"view-screen",
|
|
64
|
+
"chat-history",
|
|
64
65
|
TOOL_SEARCH_ACTION_NAME,
|
|
65
66
|
];
|
|
66
67
|
function readSafeHeader(event, name) {
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { CONTEXT_XRAY_MIGRATIONS } from "../agent/context-xray/migrations.js";
|
|
2
|
+
import { AGENT_HARNESS_SESSION_MIGRATIONS, AGENT_HARNESS_SESSION_MIGRATIONS_TABLE, } from "../agent/harness/migrations.js";
|
|
2
3
|
import { OBSERVATIONAL_MEMORY_MIGRATIONS } from "../agent/observational-memory/migrations.js";
|
|
4
|
+
import { AGENT_RUN_MIGRATIONS, AGENT_RUN_MIGRATIONS_TABLE, } from "../agent/run-migrations.js";
|
|
3
5
|
import { AGENT_TOOL_APPROVAL_MIGRATIONS, AGENT_TOOL_APPROVAL_MIGRATIONS_TABLE, } from "../agent/tool-approval-migrations.js";
|
|
6
|
+
import { CHAT_THREAD_SCHEMA_MIGRATIONS, CHAT_THREAD_SCHEMA_MIGRATIONS_TABLE, } from "../chat-threads/schema-migrations.js";
|
|
4
7
|
import { runMigrations } from "../db/migrations.js";
|
|
5
8
|
import { runAutomationRunMigrations } from "../jobs/run-history.js";
|
|
6
9
|
import { runAutomationSchedulerHealthMigrations } from "../jobs/scheduler-health.js";
|
|
7
10
|
import { OAUTH_TOKEN_MIGRATIONS, OAUTH_TOKEN_MIGRATIONS_TABLE, } from "../oauth-tokens/migrations.js";
|
|
8
11
|
import { ORG_MIGRATIONS } from "../org/migrations.js";
|
|
12
|
+
import { USAGE_ALERT_MIGRATIONS, USAGE_ALERT_MIGRATIONS_TABLE, } from "../usage/migrations.js";
|
|
9
13
|
import { runBetterAuthMigrations } from "./better-auth-migrations.js";
|
|
10
14
|
import { IDENTITY_SSO_MIGRATIONS } from "./identity-sso-migrations.js";
|
|
11
15
|
/**
|
|
@@ -23,6 +27,18 @@ export async function runFrameworkReleaseMigrations(nitroApp) {
|
|
|
23
27
|
await runMigrations(OAUTH_TOKEN_MIGRATIONS, {
|
|
24
28
|
table: OAUTH_TOKEN_MIGRATIONS_TABLE,
|
|
25
29
|
})(nitroApp);
|
|
30
|
+
await runMigrations(CHAT_THREAD_SCHEMA_MIGRATIONS, {
|
|
31
|
+
table: CHAT_THREAD_SCHEMA_MIGRATIONS_TABLE,
|
|
32
|
+
})(nitroApp);
|
|
33
|
+
await runMigrations(AGENT_RUN_MIGRATIONS, {
|
|
34
|
+
table: AGENT_RUN_MIGRATIONS_TABLE,
|
|
35
|
+
})(nitroApp);
|
|
36
|
+
await runMigrations(AGENT_HARNESS_SESSION_MIGRATIONS, {
|
|
37
|
+
table: AGENT_HARNESS_SESSION_MIGRATIONS_TABLE,
|
|
38
|
+
})(nitroApp);
|
|
39
|
+
await runMigrations(USAGE_ALERT_MIGRATIONS, {
|
|
40
|
+
table: USAGE_ALERT_MIGRATIONS_TABLE,
|
|
41
|
+
})(nitroApp);
|
|
26
42
|
await runMigrations(ORG_MIGRATIONS, { table: "_org_migrations" })(nitroApp);
|
|
27
43
|
await runMigrations(IDENTITY_SSO_MIGRATIONS, {
|
|
28
44
|
table: "_identity_sso_migrations",
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MigrationEntry } from "../db/migrations.js";
|
|
2
|
+
export declare const USAGE_ALERT_MIGRATIONS_TABLE = "_usage_alert_migrations";
|
|
3
|
+
/** Authoritative release-time schema for usage alert rules and events. */
|
|
4
|
+
export declare const USAGE_ALERT_MIGRATIONS: MigrationEntry[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const USAGE_ALERT_MIGRATIONS_TABLE = "_usage_alert_migrations";
|
|
2
|
+
/** Authoritative release-time schema for usage alert rules and events. */
|
|
3
|
+
export const USAGE_ALERT_MIGRATIONS = [
|
|
4
|
+
{
|
|
5
|
+
version: 1,
|
|
6
|
+
name: "usage-alert-rules-and-events-tables",
|
|
7
|
+
sql: `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS usage_alert_rules (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
scope TEXT NOT NULL,
|
|
11
|
+
owner_email TEXT NOT NULL,
|
|
12
|
+
org_id TEXT,
|
|
13
|
+
app_id TEXT,
|
|
14
|
+
unit TEXT NOT NULL,
|
|
15
|
+
period TEXT NOT NULL,
|
|
16
|
+
limit_value INTEGER NOT NULL,
|
|
17
|
+
channels TEXT NOT NULL,
|
|
18
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
19
|
+
dismissed_window_start INTEGER,
|
|
20
|
+
created_at INTEGER NOT NULL,
|
|
21
|
+
updated_at INTEGER NOT NULL
|
|
22
|
+
);
|
|
23
|
+
CREATE TABLE IF NOT EXISTS usage_alert_events (
|
|
24
|
+
id TEXT PRIMARY KEY,
|
|
25
|
+
rule_id TEXT NOT NULL,
|
|
26
|
+
window_start INTEGER NOT NULL,
|
|
27
|
+
created_at INTEGER NOT NULL
|
|
28
|
+
);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_usage_alert_rules_owner
|
|
30
|
+
ON usage_alert_rules (owner_email, org_id, scope);
|
|
31
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_usage_alert_events_rule_window
|
|
32
|
+
ON usage_alert_events (rule_id, window_start)
|
|
33
|
+
`,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
version: 2,
|
|
37
|
+
name: "usage-alert-default-column",
|
|
38
|
+
sql: `ALTER TABLE usage_alert_rules ADD COLUMN IF NOT EXISTS is_default INTEGER NOT NULL DEFAULT 0`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
version: 3,
|
|
42
|
+
name: "usage-alert-notification-column",
|
|
43
|
+
sql: "ALTER TABLE usage_alert_events ADD COLUMN IF NOT EXISTS notification_id TEXT",
|
|
44
|
+
},
|
|
45
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.157.
|
|
3
|
+
"version": "0.157.9",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -385,7 +385,7 @@
|
|
|
385
385
|
"@tanstack/react-table": "^8.21.3",
|
|
386
386
|
"@uiw/react-codemirror": "^4.25.10",
|
|
387
387
|
"ajv": "^8.20.0",
|
|
388
|
-
"better-auth": "1.6.
|
|
388
|
+
"better-auth": "1.6.28",
|
|
389
389
|
"better-sqlite3": "^12.8.0",
|
|
390
390
|
"botframework-connector": "^4.23.3",
|
|
391
391
|
"clsx": "^2.1.1",
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
"yjs": "^13.6.31",
|
|
427
427
|
"zod": "^4.3.6",
|
|
428
428
|
"@agent-native/recap-cli": "0.5.4",
|
|
429
|
-
"@agent-native/toolkit": "^0.16.
|
|
429
|
+
"@agent-native/toolkit": "^0.16.2"
|
|
430
430
|
},
|
|
431
431
|
"devDependencies": {
|
|
432
432
|
"@ai-sdk/anthropic": "^3.0.71",
|