@agent-native/core 0.7.55 → 0.7.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/a2a/agent-card.d.ts +1 -1
  2. package/dist/a2a/agent-card.d.ts.map +1 -1
  3. package/dist/a2a/agent-card.js +30 -2
  4. package/dist/a2a/agent-card.js.map +1 -1
  5. package/dist/a2a/artifact-response.d.ts +1 -0
  6. package/dist/a2a/artifact-response.d.ts.map +1 -1
  7. package/dist/a2a/artifact-response.js +67 -7
  8. package/dist/a2a/artifact-response.js.map +1 -1
  9. package/dist/a2a/server.d.ts.map +1 -1
  10. package/dist/a2a/server.js +1 -1
  11. package/dist/a2a/server.js.map +1 -1
  12. package/dist/a2a/task-store.d.ts +1 -0
  13. package/dist/a2a/task-store.d.ts.map +1 -1
  14. package/dist/a2a/task-store.js +15 -0
  15. package/dist/a2a/task-store.js.map +1 -1
  16. package/dist/client/AssistantChat.d.ts +15 -0
  17. package/dist/client/AssistantChat.d.ts.map +1 -1
  18. package/dist/client/AssistantChat.js +55 -52
  19. package/dist/client/AssistantChat.js.map +1 -1
  20. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  21. package/dist/client/MultiTabAssistantChat.js +0 -13
  22. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  23. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  24. package/dist/client/composer/TiptapComposer.js +59 -19
  25. package/dist/client/composer/TiptapComposer.js.map +1 -1
  26. package/dist/client/composer/useVoiceDictation.d.ts +4 -1
  27. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  28. package/dist/client/composer/useVoiceDictation.js +246 -8
  29. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  30. package/dist/client/index.d.ts +1 -0
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/client/index.js +1 -0
  33. package/dist/client/index.js.map +1 -1
  34. package/dist/client/resources/ResourcesPanel.js +2 -2
  35. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  36. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  37. package/dist/client/settings/VoiceTranscriptionSection.js +155 -18
  38. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  39. package/dist/client/use-chat-models.d.ts +33 -0
  40. package/dist/client/use-chat-models.d.ts.map +1 -0
  41. package/dist/client/use-chat-models.js +183 -0
  42. package/dist/client/use-chat-models.js.map +1 -0
  43. package/dist/integrations/a2a-continuation-processor.js +29 -15
  44. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  45. package/dist/integrations/adapters/slack.d.ts +2 -2
  46. package/dist/integrations/adapters/slack.js +20 -15
  47. package/dist/integrations/adapters/slack.js.map +1 -1
  48. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  49. package/dist/server/agent-chat-plugin.js +22 -1
  50. package/dist/server/agent-chat-plugin.js.map +1 -1
  51. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  52. package/dist/server/core-routes-plugin.js +6 -0
  53. package/dist/server/core-routes-plugin.js.map +1 -1
  54. package/dist/server/google-realtime-session.d.ts +14 -0
  55. package/dist/server/google-realtime-session.d.ts.map +1 -0
  56. package/dist/server/google-realtime-session.js +155 -0
  57. package/dist/server/google-realtime-session.js.map +1 -0
  58. package/dist/server/voice-providers-status.d.ts +4 -4
  59. package/dist/server/voice-providers-status.d.ts.map +1 -1
  60. package/dist/server/voice-providers-status.js +11 -0
  61. package/dist/server/voice-providers-status.js.map +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ interface GoogleRealtimeSessionResponse {
2
+ websocketUrl: string;
3
+ sessionToken: string;
4
+ websocketProtocol?: string;
5
+ }
6
+ export declare function resolveGoogleRealtimeCredentials(opts: {
7
+ userEmail?: string | null;
8
+ orgId?: string | null;
9
+ }): Promise<string | null>;
10
+ export declare function createGoogleRealtimeSessionHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<GoogleRealtimeSessionResponse | {
11
+ error: any;
12
+ }>>;
13
+ export {};
14
+ //# sourceMappingURL=google-realtime-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-realtime-session.d.ts","sourceRoot":"","sources":["../../src/server/google-realtime-session.ts"],"names":[],"mappings":"AAgBA,UAAU,6BAA6B;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAqCD,wBAAsB,gCAAgC,CAAC,IAAI,EAAE;IAC3D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BzB;AAED,wBAAgB,kCAAkC;;IAqGjD"}
@@ -0,0 +1,155 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { defineEventHandler, getMethod, getRequestHeader, readBody, setResponseStatus, } from "h3";
3
+ import { readAppSecret } from "../secrets/storage.js";
4
+ import { resolveCredential } from "../credentials/index.js";
5
+ import { getSession } from "./auth.js";
6
+ import { getOrgContext } from "../org/context.js";
7
+ import { runWithRequestContext } from "./request-context.js";
8
+ import { resolveBuilderCredentials } from "./credential-provider.js";
9
+ function isSameOriginRequest(event) {
10
+ const host = getRequestHeader(event, "host");
11
+ const origin = getRequestHeader(event, "origin");
12
+ if (origin && host) {
13
+ try {
14
+ const parsed = new URL(origin);
15
+ if (parsed.host === host)
16
+ return true;
17
+ if (parsed.protocol === "tauri:" && parsed.hostname === "localhost") {
18
+ return true;
19
+ }
20
+ if ((parsed.protocol === "http:" || parsed.protocol === "https:") &&
21
+ parsed.hostname === "tauri.localhost" &&
22
+ (host.startsWith("localhost:") || host.startsWith("127.0.0.1:"))) {
23
+ return true;
24
+ }
25
+ if (parsed.protocol === "http:" &&
26
+ (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1") &&
27
+ parsed.port === "1420" &&
28
+ (host.startsWith("localhost:") || host.startsWith("127.0.0.1:"))) {
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ const fetchSite = getRequestHeader(event, "sec-fetch-site");
38
+ if (fetchSite)
39
+ return fetchSite === "same-origin" || fetchSite === "none";
40
+ return true;
41
+ }
42
+ export async function resolveGoogleRealtimeCredentials(opts) {
43
+ if (opts.userEmail) {
44
+ const userSecret = await readAppSecret({
45
+ key: "GOOGLE_APPLICATION_CREDENTIALS",
46
+ scope: "user",
47
+ scopeId: opts.userEmail,
48
+ }).catch(() => null);
49
+ const fromUserSecret = userSecret?.value?.trim();
50
+ if (fromUserSecret)
51
+ return fromUserSecret;
52
+ }
53
+ const stored = await resolveCredential("GOOGLE_APPLICATION_CREDENTIALS", {
54
+ userEmail: opts.userEmail ?? undefined,
55
+ orgId: opts.orgId ?? undefined,
56
+ }).catch(() => undefined);
57
+ const fromSettings = stored?.trim();
58
+ if (fromSettings)
59
+ return fromSettings;
60
+ const envValue = process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim();
61
+ if (!envValue)
62
+ return null;
63
+ if (envValue.startsWith("{"))
64
+ return envValue;
65
+ try {
66
+ const fileContents = await readFile(envValue, "utf8");
67
+ const trimmed = fileContents.trim();
68
+ return trimmed || null;
69
+ }
70
+ catch {
71
+ throw new Error("GOOGLE_APPLICATION_CREDENTIALS points to a file path the framework server could not read");
72
+ }
73
+ }
74
+ export function createGoogleRealtimeSessionHandler() {
75
+ return defineEventHandler(async (event) => {
76
+ if (getMethod(event) !== "POST") {
77
+ setResponseStatus(event, 405);
78
+ return { error: "Method not allowed" };
79
+ }
80
+ if (!isSameOriginRequest(event)) {
81
+ setResponseStatus(event, 403);
82
+ return { error: "Cross-origin request rejected" };
83
+ }
84
+ const session = await getSession(event).catch(() => null);
85
+ if (!session?.email) {
86
+ setResponseStatus(event, 401);
87
+ return { error: "Authentication required" };
88
+ }
89
+ const orgCtx = await getOrgContext(event).catch(() => null);
90
+ const requestContext = {
91
+ userEmail: session.email,
92
+ orgId: orgCtx?.orgId ?? undefined,
93
+ };
94
+ return runWithRequestContext(requestContext, async () => {
95
+ const googleApplicationCredentials = await resolveGoogleRealtimeCredentials({
96
+ userEmail: session.email,
97
+ orgId: orgCtx?.orgId ?? undefined,
98
+ });
99
+ if (!googleApplicationCredentials) {
100
+ setResponseStatus(event, 400);
101
+ return {
102
+ error: "Configure GOOGLE_APPLICATION_CREDENTIALS in Settings to use Google realtime transcription.",
103
+ };
104
+ }
105
+ const builderCreds = await resolveBuilderCredentials();
106
+ if (!builderCreds.privateKey || !builderCreds.publicKey) {
107
+ setResponseStatus(event, 400);
108
+ return {
109
+ error: "Builder must be connected to mint a managed realtime transcription session.",
110
+ };
111
+ }
112
+ const apiHost = process.env.BUILDER_API_HOST || "https://ai-services.builder.io";
113
+ const body = ((await readBody(event).catch(() => ({}))) || {});
114
+ const res = await fetch(`${apiHost}/agent-native/transcribe-stream/session`, {
115
+ method: "POST",
116
+ headers: {
117
+ "Content-Type": "application/json",
118
+ Authorization: `Bearer ${builderCreds.privateKey}`,
119
+ "x-builder-api-key": builderCreds.publicKey,
120
+ ...(builderCreds.userId
121
+ ? { "x-builder-user-id": builderCreds.userId }
122
+ : {}),
123
+ },
124
+ body: JSON.stringify({
125
+ googleApplicationCredentials,
126
+ language: typeof body?.language === "string"
127
+ ? body.language.trim()
128
+ : undefined,
129
+ }),
130
+ }).catch((err) => {
131
+ throw new Error(err?.message || "Failed to reach realtime transcription service");
132
+ });
133
+ if (!res.ok) {
134
+ const errorBody = await res
135
+ .json()
136
+ .catch(() => ({ error: `HTTP ${res.status}` }));
137
+ setResponseStatus(event, res.status);
138
+ return {
139
+ error: typeof errorBody?.error === "string"
140
+ ? errorBody.error
141
+ : `Realtime session failed (${res.status})`,
142
+ };
143
+ }
144
+ const payload = (await res.json());
145
+ if (!payload?.websocketUrl) {
146
+ setResponseStatus(event, 502);
147
+ return {
148
+ error: "Realtime transcription service did not return a websocket URL.",
149
+ };
150
+ }
151
+ return payload;
152
+ });
153
+ });
154
+ }
155
+ //# sourceMappingURL=google-realtime-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-realtime-session.js","sourceRoot":"","sources":["../../src/server/google-realtime-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,iBAAiB,GAElB,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAQrE,SAAS,mBAAmB,CAAC,KAAc;IACzC,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACjD,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YACtC,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IACE,CAAC,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;gBAC7D,MAAM,CAAC,QAAQ,KAAK,iBAAiB;gBACrC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAChE,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IACE,MAAM,CAAC,QAAQ,KAAK,OAAO;gBAC3B,CAAC,MAAM,CAAC,QAAQ,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,CAAC;gBACpE,MAAM,CAAC,IAAI,KAAK,MAAM;gBACtB,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAChE,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAC5D,IAAI,SAAS;QAAE,OAAO,SAAS,KAAK,aAAa,IAAI,SAAS,KAAK,MAAM,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,IAGtD;IACC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC;YACrC,GAAG,EAAE,gCAAgC;YACrC,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,IAAI,CAAC,SAAS;SACxB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACrB,MAAM,cAAc,GAAG,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACjD,IAAI,cAAc;YAAE,OAAO,cAAc,CAAC;IAC5C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,gCAAgC,EAAE;QACvE,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;QACtC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;KAC/B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1B,MAAM,YAAY,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC;IACpC,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,EAAE,CAAC;IACpE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QACpC,OAAO,OAAO,IAAI,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kCAAkC;IAChD,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;QACjD,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC;YAChC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YAChC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC;QACpD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YACpB,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;QAC9C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,cAAc,GAAG;YACrB,SAAS,EAAE,OAAO,CAAC,KAAK;YACxB,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,SAAS;SAClC,CAAC;QAEF,OAAO,qBAAqB,CAAC,cAAc,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,4BAA4B,GAChC,MAAM,gCAAgC,CAAC;gBACrC,SAAS,EAAE,OAAO,CAAC,KAAK;gBACxB,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,SAAS;aAClC,CAAC,CAAC;YACL,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBAClC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAC9B,OAAO;oBACL,KAAK,EACH,4FAA4F;iBAC/F,CAAC;YACJ,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,yBAAyB,EAAE,CAAC;YACvD,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;gBACxD,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAC9B,OAAO;oBACL,KAAK,EACH,6EAA6E;iBAChF,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,gCAAgC,CAAC;YACnE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAE5D,CAAC;YACF,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,GAAG,OAAO,yCAAyC,EACnD;gBACE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,UAAU,YAAY,CAAC,UAAU,EAAE;oBAClD,mBAAmB,EAAE,YAAY,CAAC,SAAS;oBAC3C,GAAG,CAAC,YAAY,CAAC,MAAM;wBACrB,CAAC,CAAC,EAAE,mBAAmB,EAAE,YAAY,CAAC,MAAM,EAAE;wBAC9C,CAAC,CAAC,EAAE,CAAC;iBACR;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,4BAA4B;oBAC5B,QAAQ,EACN,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ;wBAChC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;wBACtB,CAAC,CAAC,SAAS;iBAChB,CAAC;aACH,CACF,CAAC,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE;gBACnB,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,OAAO,IAAI,gDAAgD,CACjE,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,SAAS,GAAG,MAAM,GAAG;qBACxB,IAAI,EAAE;qBACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;gBAClD,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;gBACrC,OAAO;oBACL,KAAK,EACH,OAAO,SAAS,EAAE,KAAK,KAAK,QAAQ;wBAClC,CAAC,CAAC,SAAS,CAAC,KAAK;wBACjB,CAAC,CAAC,4BAA4B,GAAG,CAAC,MAAM,GAAG;iBAChD,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkC,CAAC;YACpE,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;gBAC3B,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAC9B,OAAO;oBACL,KAAK,EACH,gEAAgE;iBACnE,CAAC;YACJ,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport {\n defineEventHandler,\n getMethod,\n getRequestHeader,\n readBody,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { readAppSecret } from \"../secrets/storage.js\";\nimport { resolveCredential } from \"../credentials/index.js\";\nimport { getSession } from \"./auth.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { resolveBuilderCredentials } from \"./credential-provider.js\";\n\ninterface GoogleRealtimeSessionResponse {\n websocketUrl: string;\n sessionToken: string;\n websocketProtocol?: string;\n}\n\nfunction isSameOriginRequest(event: H3Event): boolean {\n const host = getRequestHeader(event, \"host\");\n const origin = getRequestHeader(event, \"origin\");\n if (origin && host) {\n try {\n const parsed = new URL(origin);\n if (parsed.host === host) return true;\n if (parsed.protocol === \"tauri:\" && parsed.hostname === \"localhost\") {\n return true;\n }\n if (\n (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.hostname === \"tauri.localhost\" &&\n (host.startsWith(\"localhost:\") || host.startsWith(\"127.0.0.1:\"))\n ) {\n return true;\n }\n if (\n parsed.protocol === \"http:\" &&\n (parsed.hostname === \"localhost\" || parsed.hostname === \"127.0.0.1\") &&\n parsed.port === \"1420\" &&\n (host.startsWith(\"localhost:\") || host.startsWith(\"127.0.0.1:\"))\n ) {\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n const fetchSite = getRequestHeader(event, \"sec-fetch-site\");\n if (fetchSite) return fetchSite === \"same-origin\" || fetchSite === \"none\";\n return true;\n}\n\nexport async function resolveGoogleRealtimeCredentials(opts: {\n userEmail?: string | null;\n orgId?: string | null;\n}): Promise<string | null> {\n if (opts.userEmail) {\n const userSecret = await readAppSecret({\n key: \"GOOGLE_APPLICATION_CREDENTIALS\",\n scope: \"user\",\n scopeId: opts.userEmail,\n }).catch(() => null);\n const fromUserSecret = userSecret?.value?.trim();\n if (fromUserSecret) return fromUserSecret;\n }\n\n const stored = await resolveCredential(\"GOOGLE_APPLICATION_CREDENTIALS\", {\n userEmail: opts.userEmail ?? undefined,\n orgId: opts.orgId ?? undefined,\n }).catch(() => undefined);\n const fromSettings = stored?.trim();\n if (fromSettings) return fromSettings;\n\n const envValue = process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim();\n if (!envValue) return null;\n if (envValue.startsWith(\"{\")) return envValue;\n\n try {\n const fileContents = await readFile(envValue, \"utf8\");\n const trimmed = fileContents.trim();\n return trimmed || null;\n } catch {\n throw new Error(\n \"GOOGLE_APPLICATION_CREDENTIALS points to a file path the framework server could not read\",\n );\n }\n}\n\nexport function createGoogleRealtimeSessionHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n if (!isSameOriginRequest(event)) {\n setResponseStatus(event, 403);\n return { error: \"Cross-origin request rejected\" };\n }\n\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const orgCtx = await getOrgContext(event).catch(() => null);\n const requestContext = {\n userEmail: session.email,\n orgId: orgCtx?.orgId ?? undefined,\n };\n\n return runWithRequestContext(requestContext, async () => {\n const googleApplicationCredentials =\n await resolveGoogleRealtimeCredentials({\n userEmail: session.email,\n orgId: orgCtx?.orgId ?? undefined,\n });\n if (!googleApplicationCredentials) {\n setResponseStatus(event, 400);\n return {\n error:\n \"Configure GOOGLE_APPLICATION_CREDENTIALS in Settings to use Google realtime transcription.\",\n };\n }\n\n const builderCreds = await resolveBuilderCredentials();\n if (!builderCreds.privateKey || !builderCreds.publicKey) {\n setResponseStatus(event, 400);\n return {\n error:\n \"Builder must be connected to mint a managed realtime transcription session.\",\n };\n }\n\n const apiHost =\n process.env.BUILDER_API_HOST || \"https://ai-services.builder.io\";\n const body = ((await readBody(event).catch(() => ({}))) || {}) as {\n language?: unknown;\n };\n const res = await fetch(\n `${apiHost}/agent-native/transcribe-stream/session`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${builderCreds.privateKey}`,\n \"x-builder-api-key\": builderCreds.publicKey,\n ...(builderCreds.userId\n ? { \"x-builder-user-id\": builderCreds.userId }\n : {}),\n },\n body: JSON.stringify({\n googleApplicationCredentials,\n language:\n typeof body?.language === \"string\"\n ? body.language.trim()\n : undefined,\n }),\n },\n ).catch((err: any) => {\n throw new Error(\n err?.message || \"Failed to reach realtime transcription service\",\n );\n });\n\n if (!res.ok) {\n const errorBody = await res\n .json()\n .catch(() => ({ error: `HTTP ${res.status}` }));\n setResponseStatus(event, res.status);\n return {\n error:\n typeof errorBody?.error === \"string\"\n ? errorBody.error\n : `Realtime session failed (${res.status})`,\n };\n }\n\n const payload = (await res.json()) as GoogleRealtimeSessionResponse;\n if (!payload?.websocketUrl) {\n setResponseStatus(event, 502);\n return {\n error:\n \"Realtime transcription service did not return a websocket URL.\",\n };\n }\n return payload;\n });\n });\n}\n"]}
@@ -4,10 +4,10 @@ export interface VoiceProvidersStatus {
4
4
  openai: boolean;
5
5
  groq: boolean;
6
6
  /**
7
- * Google Speech-to-Text realtime streaming is BYOK-only for v1. This only
8
- * reports whether a service-account credential is configured; the actual
9
- * stream belongs on a dedicated WebSocket -> StreamingRecognize path, not on
10
- * the batch transcribe route.
7
+ * Google Speech-to-Text realtime streaming is BYOK-only for v1. This reports
8
+ * whether a service-account credential is configured; the actual stream runs
9
+ * through the dedicated WebSocket -> StreamingRecognize path, not the batch
10
+ * transcribe route.
11
11
  */
12
12
  googleRealtime: boolean;
13
13
  /** Always true — the Web Speech API is available in WebKit-based clients. */
@@ -1 +1 @@
1
- {"version":3,"file":"voice-providers-status.d.ts","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,OAAO,EAAE,IAAI,CAAC;IACd;;;;;OAKG;IACH,MAAM,EAAE,IAAI,CAAC;CACd;AAED,wBAAgB,iCAAiC;;IAmEhD"}
1
+ {"version":3,"file":"voice-providers-status.d.ts","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,OAAO,EAAE,IAAI,CAAC;IACd;;;;;OAKG;IACH,MAAM,EAAE,IAAI,CAAC;CACd;AAED,wBAAgB,iCAAiC;;IA6EhD"}
@@ -20,6 +20,7 @@ import { getSession } from "./auth.js";
20
20
  import { resolveHasBuilderPrivateKey } from "./credential-provider.js";
21
21
  import { getOrgContext } from "../org/context.js";
22
22
  import { runWithRequestContext } from "./request-context.js";
23
+ import { resolveGoogleRealtimeCredentials } from "./google-realtime-session.js";
23
24
  export function createVoiceProvidersStatusHandler() {
24
25
  return defineEventHandler(async (event) => {
25
26
  if (getMethod(event) !== "GET") {
@@ -29,6 +30,16 @@ export function createVoiceProvidersStatusHandler() {
29
30
  const session = await getSession(event).catch(() => null);
30
31
  async function hasKey(key) {
31
32
  try {
33
+ if (key === "GOOGLE_APPLICATION_CREDENTIALS") {
34
+ const orgCtx = session?.email
35
+ ? await getOrgContext(event).catch(() => null)
36
+ : null;
37
+ const resolved = await resolveGoogleRealtimeCredentials({
38
+ userEmail: session?.email,
39
+ orgId: orgCtx?.orgId ?? undefined,
40
+ });
41
+ return typeof resolved === "string" && resolved.length > 0;
42
+ }
32
43
  const ctx = { userEmail: session?.email };
33
44
  if (!session?.email) {
34
45
  const v = await resolveCredential(key, ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"voice-providers-status.js","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GET /_agent-native/voice-providers/status\n *\n * Reports which voice transcription providers are configured for the\n * current user. The desktop Settings UI uses this to show \"Connect\" vs\n * \"Connected\" status pills next to each provider option.\n *\n * Resolution mirrors `transcribe-voice.ts`: we try the user-scoped\n * encrypted secret first (set via the sidebar settings UI) and fall back\n * to `resolveCredential()` (env var + SQL settings store). Each lookup is\n * wrapped in try/catch — one provider's failure must never break the\n * whole response.\n *\n * Returns booleans only — never the actual key material.\n */\nimport {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { readAppSecret } from \"../secrets/storage.js\";\nimport { resolveCredential } from \"../credentials/index.js\";\nimport { getSession } from \"./auth.js\";\nimport { resolveHasBuilderPrivateKey } from \"./credential-provider.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\n\nexport interface VoiceProvidersStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n /**\n * Google Speech-to-Text realtime streaming is BYOK-only for v1. This only\n * reports whether a service-account credential is configured; the actual\n * stream belongs on a dedicated WebSocket -> StreamingRecognize path, not on\n * the batch transcribe route.\n */\n googleRealtime: boolean;\n /** Always true — the Web Speech API is available in WebKit-based clients. */\n browser: true;\n /**\n * Apple's SFSpeechRecognizer + AVAudioEngine, exposed by the Tauri\n * desktop client. Always reported as `true` from the server — the\n * desktop client gates this on macOS at the Tauri-command boundary, so\n * non-macOS hosts return a clear error instead of attempting to use it.\n */\n native: true;\n}\n\nexport function createVoiceProvidersStatusHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n const session = await getSession(event).catch(() => null);\n\n async function hasKey(key: string): Promise<boolean> {\n try {\n const ctx = { userEmail: session?.email };\n if (!session?.email) {\n const v = await resolveCredential(key, ctx);\n return typeof v === \"string\" && v.length > 0;\n }\n const userSecret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: session.email,\n }).catch(() => null);\n if (userSecret?.value && userSecret.value.length > 0) return true;\n const fallback = await resolveCredential(key, ctx);\n return typeof fallback === \"string\" && fallback.length > 0;\n } catch {\n return false;\n }\n }\n\n let builder = false;\n try {\n const orgCtx = session?.email\n ? await getOrgContext(event).catch(() => null)\n : null;\n const resolve = () => resolveHasBuilderPrivateKey();\n builder =\n (session?.email\n ? await runWithRequestContext(\n {\n userEmail: session.email,\n orgId: orgCtx?.orgId ?? undefined,\n },\n resolve,\n )\n : await resolve()) === true;\n } catch {\n builder = false;\n }\n\n const [gemini, openai, groq, googleRealtime] = await Promise.all([\n hasKey(\"GEMINI_API_KEY\"),\n hasKey(\"OPENAI_API_KEY\"),\n hasKey(\"GROQ_API_KEY\"),\n hasKey(\"GOOGLE_APPLICATION_CREDENTIALS\"),\n ]);\n\n const status: VoiceProvidersStatus = {\n builder,\n gemini,\n openai,\n groq,\n googleRealtime,\n browser: true,\n native: true,\n };\n return status;\n });\n}\n"]}
1
+ {"version":3,"file":"voice-providers-status.js","sourceRoot":"","sources":["../../src/server/voice-providers-status.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GET /_agent-native/voice-providers/status\n *\n * Reports which voice transcription providers are configured for the\n * current user. The desktop Settings UI uses this to show \"Connect\" vs\n * \"Connected\" status pills next to each provider option.\n *\n * Resolution mirrors `transcribe-voice.ts`: we try the user-scoped\n * encrypted secret first (set via the sidebar settings UI) and fall back\n * to `resolveCredential()` (env var + SQL settings store). Each lookup is\n * wrapped in try/catch — one provider's failure must never break the\n * whole response.\n *\n * Returns booleans only — never the actual key material.\n */\nimport {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { readAppSecret } from \"../secrets/storage.js\";\nimport { resolveCredential } from \"../credentials/index.js\";\nimport { getSession } from \"./auth.js\";\nimport { resolveHasBuilderPrivateKey } from \"./credential-provider.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { resolveGoogleRealtimeCredentials } from \"./google-realtime-session.js\";\n\nexport interface VoiceProvidersStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n /**\n * Google Speech-to-Text realtime streaming is BYOK-only for v1. This reports\n * whether a service-account credential is configured; the actual stream runs\n * through the dedicated WebSocket -> StreamingRecognize path, not the batch\n * transcribe route.\n */\n googleRealtime: boolean;\n /** Always true — the Web Speech API is available in WebKit-based clients. */\n browser: true;\n /**\n * Apple's SFSpeechRecognizer + AVAudioEngine, exposed by the Tauri\n * desktop client. Always reported as `true` from the server — the\n * desktop client gates this on macOS at the Tauri-command boundary, so\n * non-macOS hosts return a clear error instead of attempting to use it.\n */\n native: true;\n}\n\nexport function createVoiceProvidersStatusHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"GET\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n const session = await getSession(event).catch(() => null);\n\n async function hasKey(key: string): Promise<boolean> {\n try {\n if (key === \"GOOGLE_APPLICATION_CREDENTIALS\") {\n const orgCtx = session?.email\n ? await getOrgContext(event).catch(() => null)\n : null;\n const resolved = await resolveGoogleRealtimeCredentials({\n userEmail: session?.email,\n orgId: orgCtx?.orgId ?? undefined,\n });\n return typeof resolved === \"string\" && resolved.length > 0;\n }\n const ctx = { userEmail: session?.email };\n if (!session?.email) {\n const v = await resolveCredential(key, ctx);\n return typeof v === \"string\" && v.length > 0;\n }\n const userSecret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: session.email,\n }).catch(() => null);\n if (userSecret?.value && userSecret.value.length > 0) return true;\n const fallback = await resolveCredential(key, ctx);\n return typeof fallback === \"string\" && fallback.length > 0;\n } catch {\n return false;\n }\n }\n\n let builder = false;\n try {\n const orgCtx = session?.email\n ? await getOrgContext(event).catch(() => null)\n : null;\n const resolve = () => resolveHasBuilderPrivateKey();\n builder =\n (session?.email\n ? await runWithRequestContext(\n {\n userEmail: session.email,\n orgId: orgCtx?.orgId ?? undefined,\n },\n resolve,\n )\n : await resolve()) === true;\n } catch {\n builder = false;\n }\n\n const [gemini, openai, groq, googleRealtime] = await Promise.all([\n hasKey(\"GEMINI_API_KEY\"),\n hasKey(\"OPENAI_API_KEY\"),\n hasKey(\"GROQ_API_KEY\"),\n hasKey(\"GOOGLE_APPLICATION_CREDENTIALS\"),\n ]);\n\n const status: VoiceProvidersStatus = {\n builder,\n gemini,\n openai,\n groq,\n googleRealtime,\n browser: true,\n native: true,\n };\n return status;\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.7.55",
3
+ "version": "0.7.57",
4
4
  "type": "module",
5
5
  "description": "Framework for agent-native application development — where AI agents and UI share state via files",
6
6
  "license": "MIT",