@agent-native/core 0.176.1 → 0.177.0-nightly-20260828155142
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/set-workspace-feature-flag.ts +2 -2
- package/corpus/templates/analytics/app/components/agents/FeatureFlagsFleetPanel.tsx +16 -1
- package/corpus/templates/analytics/changelog/2026-08-27-show-analytics-s-own-registered-flags-in-fleet-feature-flag-.md +6 -0
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +204 -32
- package/corpus/templates/assets/changelog/2026-08-26-templates-across-brand-kits.md +1 -0
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +61 -68
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +9 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +3 -3
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +8 -3
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +12 -3
- package/corpus/templates/calendar/changelog/2026-08-28-all-day-events-stay-in-the-compact-top-bar-in-day-and-week-v.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-28-event-creators-can-choose-whether-a-meeting-shows-as-free-or.md +6 -0
- package/corpus/templates/clips/app/global.css +0 -27
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -0
- package/corpus/templates/clips/app/routes/download.tsx +131 -16
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +35 -20
- package/corpus/templates/clips/server/routes/api/clips-latest.json.get.ts +77 -22
- package/corpus/templates/forms/actions/response-insights.ts +4 -4
- package/corpus/templates/forms/actions/retry-response-deliveries.ts +26 -0
- package/corpus/templates/forms/server/db/schema.ts +64 -17
- package/corpus/templates/forms/server/handlers/submissions.ts +1022 -94
- package/corpus/templates/forms/server/lib/integrations.ts +100 -33
- package/corpus/templates/forms/server/middleware/00-public-cors.ts +1 -1
- package/corpus/templates/forms/server/plugins/db.ts +40 -0
- package/corpus/templates/plan/agent-native.json +1 -4
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +6 -1
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +2 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -6
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +78 -12
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +4 -4
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +111 -27
- package/corpus/templates/slides/app/components/editor/SlidesLayersPanel.tsx +20 -34
- package/corpus/templates/slides/app/components/editor/slide-style.ts +2 -0
- package/corpus/templates/slides/app/global.css +35 -0
- package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +133 -5
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +675 -0
- package/corpus/templates/slides/app/lib/validate-native-slide-html.ts +1 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +44 -44
- package/corpus/templates/slides/app/root.tsx +1 -22
- package/corpus/templates/slides/changelog/2026-08-27-image-fit-controls-apply-cover-crops-and-position.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-28-shared-slides-with-a-style-block-render-again.md +6 -0
- package/dist/action.d.ts +3 -1
- package/dist/client/FeedbackButton.d.ts +12 -0
- package/dist/client/FeedbackButton.js +62 -28
- package/dist/client/app-providers.js +13 -1
- package/dist/client/feedback-context.d.ts +1 -0
- package/dist/client/feedback-context.js +3 -2
- package/dist/client/host-bridge.d.ts +1 -0
- package/dist/client/observability/ThumbsFeedback.js +73 -10
- package/dist/client/webmcp.d.ts +4 -0
- package/dist/client/webmcp.js +51 -4
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/observability/routes.js +8 -2
- package/dist/observability/store.d.ts +1 -1
- package/dist/observability/store.js +18 -3
- package/dist/observability/types.d.ts +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts +1 -6
- package/dist/server/action-routes.js +58 -17
- package/dist/server/agent-chat-plugin.js +8 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/docs/content/webmcp.mdx +31 -12
- package/package.json +1 -1
package/dist/index.browser.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./config.js";
|
|
|
2
2
|
export { addContextToAgentChat, appendAgentChatContextToMessage, clearAgentChatContext, formatAgentChatContextItemsForPrompt, listAgentChatContext, refreshAgentChatContext, removeAgentChatContextItem, sendToAgentChat, sendToAgentChatAndConfirm, setAgentChatContextItem, setContextToAgentChat, isEmbedMcpChatBridgeActive, useAgentChatContext, useAgentChatGenerating, useDevMode, useSendToAgentChat, AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, openMcpAppHostLink, requestMcpAppDisplayMode, sendMcpAppHostMessage, updateMcpAppModelContext, useMcpAppHostContext, CodeRequiredDialog, AgentNativeI18nProvider, LanguagePicker, getLocaleInitScript, localeDirection, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, useFormatters, useLocale, useT, DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, useDbSync, useFileWatcher, useSession, cn, ApiKeySettings, type AgentChatContextItem, type AgentChatContextMessage, type AgentChatContextMutationOptions, type AgentChatContextRemoveOptions, type AgentChatContextSetOptions, type AgentChatContextState, type AgentChatMessage, type AgentNativeMcpAppHostMessageType, type McpAppDisplayMode, type McpAppHostChatMessage, type McpAppHostCapabilities, type McpAppHostContext, type McpAppHostContextSnapshot, type McpAppModelContextContentPart, type McpAppModelContextUpdate, type CodeRequiredDialogProps, type AuthSession, type UseAgentChatContextResult, type AgentNativeI18nCatalog, type AgentNativeI18nProviderProps, type LocaleCode, type LocaleHydrationPayload, type LocaleMessages, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "./client/index.js";
|
|
3
3
|
export { agentChat } from "./shared/index.js";
|
|
4
4
|
export { parseArgs, camelCaseArgs } from "./scripts/parse-args.js";
|
|
5
|
-
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, isAgentNativeWebMcpSupported, type AgentNativeWebMcpApprovalRequest, type AgentNativeWebMcpClient, type AgentNativeWebMcpClientOptions, type AgentNativeWebMcpRegistration, type AgentNativeWebMcpRegistrationOptions, type AgentNativeWebMcpTool, type AgentNativeWebMcpToolAnnotations, type AgentNativeWebMcpToolExecutionOptions, type AgentNativeWebMcpToolResult, } from "./client/webmcp.js";
|
|
5
|
+
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, createAgentNativeServerActionWebMcpRegistration, isAgentNativeWebMcpSupported, type AgentNativeWebMcpApprovalRequest, type AgentNativeWebMcpClient, type AgentNativeWebMcpClientOptions, type AgentNativeWebMcpRegistration, type AgentNativeWebMcpRegistrationOptions, type AgentNativeWebMcpTool, type AgentNativeWebMcpToolAnnotations, type AgentNativeWebMcpToolExecutionOptions, type AgentNativeWebMcpToolResult, } from "./client/webmcp.js";
|
|
6
6
|
export { defineAction, fail, type FailOptions, ActionContractError, isActionContractError, type ActionContractErrorOptions, AgentActionStopError, isAgentActionStopError, type ActionHttpConfig, type AgentActionStopOptions, MCP_APP_EXTENSION_ID, MCP_APP_MIME_TYPE, MCP_APP_RESOURCE_URI_META_KEY, type ActionMcpAppConfig, type ActionMcpAppCsp, type ActionMcpAppCspBuilder, type ActionMcpAppHtmlBuilder, type ActionMcpAppPermissions, type ActionMcpAppResourceConfig, type ActionMcpAppResourceMeta, } from "./action.js";
|
|
7
7
|
export { isEmailDerivedName, normalizeUserProfileName, resolveUserProfileName, USER_PROFILE_SETTING_KEY, type UserProfile, } from "./user-profile/index.js";
|
|
8
8
|
export { ACTION_CHAT_UI_DATA_CHART_RENDERER, ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER, ACTION_CHAT_UI_DATA_TABLE_RENDERER, ACTION_CHAT_UI_DATA_WIDGET_RENDERER, ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER, ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER, type ActionChatUIConfig, } from "./action-ui.js";
|
package/dist/index.browser.js
CHANGED
|
@@ -8,7 +8,7 @@ export { addContextToAgentChat, appendAgentChatContextToMessage, clearAgentChatC
|
|
|
8
8
|
export { agentChat } from "./shared/index.js";
|
|
9
9
|
// Pure utilities (no Node.js deps — safe for browser and SSR)
|
|
10
10
|
export { parseArgs, camelCaseArgs } from "./scripts/parse-args.js";
|
|
11
|
-
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, isAgentNativeWebMcpSupported, } from "./client/webmcp.js";
|
|
11
|
+
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, createAgentNativeServerActionWebMcpRegistration, isAgentNativeWebMcpSupported, } from "./client/webmcp.js";
|
|
12
12
|
// defineAction — used by template actions, no Node.js deps
|
|
13
13
|
export { defineAction, fail, ActionContractError, isActionContractError, AgentActionStopError, isAgentActionStopError, MCP_APP_EXTENSION_ID, MCP_APP_MIME_TYPE, MCP_APP_RESOURCE_URI_META_KEY, } from "./action.js";
|
|
14
14
|
export { isEmailDerivedName, normalizeUserProfileName, resolveUserProfileName, USER_PROFILE_SETTING_KEY, } from "./user-profile/index.js";
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
46
|
id?: undefined;
|
|
49
|
-
} | {
|
|
50
47
|
error?: undefined;
|
|
51
48
|
ok?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
|
|
53
|
+
error?: undefined;
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
66
65
|
ok: boolean;
|
|
66
|
+
error?: undefined;
|
|
67
67
|
}>>;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* POST /experiments/:id/results — compute experiment results
|
|
20
20
|
* GET /experiments/:id/results — get experiment results
|
|
21
21
|
*/
|
|
22
|
-
import { defineEventHandler, getMethod, getQuery, setResponseStatus, } from "h3";
|
|
22
|
+
import { defineEventHandler, getHeader, getMethod, getQuery, setResponseStatus, } from "h3";
|
|
23
23
|
import { getSession } from "../server/auth.js";
|
|
24
24
|
import { readBody } from "../server/h3-helpers.js";
|
|
25
25
|
import { getRequestContext } from "../server/request-context.js";
|
|
@@ -164,16 +164,22 @@ export function createObservabilityHandler() {
|
|
|
164
164
|
? JSON.stringify(rawValue)
|
|
165
165
|
: String(rawValue);
|
|
166
166
|
const id = nanoid();
|
|
167
|
-
|
|
167
|
+
const idempotencyKey = feedbackType === "text"
|
|
168
|
+
? getHeader(event, "idempotency-key")?.trim() || null
|
|
169
|
+
: null;
|
|
170
|
+
const inserted = await insertFeedback({
|
|
168
171
|
id,
|
|
169
172
|
runId: body.runId ? String(body.runId) : null,
|
|
170
173
|
threadId: body.threadId ? String(body.threadId) : null,
|
|
171
174
|
messageSeq: typeof body.messageSeq === "number" ? body.messageSeq : null,
|
|
172
175
|
feedbackType,
|
|
173
176
|
value,
|
|
177
|
+
idempotencyKey,
|
|
174
178
|
userId: owner,
|
|
175
179
|
createdAt: Date.now(),
|
|
176
180
|
});
|
|
181
|
+
if (!inserted)
|
|
182
|
+
return { id };
|
|
177
183
|
{
|
|
178
184
|
const runId = body.runId ? String(body.runId) : null;
|
|
179
185
|
const threadId = body.threadId ? String(body.threadId) : null;
|
|
@@ -34,7 +34,7 @@ export declare function getLatestTraceSummaryForThread(threadId: string, opts: {
|
|
|
34
34
|
userId: string;
|
|
35
35
|
excludeRunId: string;
|
|
36
36
|
}): Promise<TraceSummary | null>;
|
|
37
|
-
export declare function insertFeedback(entry: FeedbackEntry): Promise<
|
|
37
|
+
export declare function insertFeedback(entry: FeedbackEntry): Promise<boolean>;
|
|
38
38
|
export declare function getFeedback(opts: {
|
|
39
39
|
threadId?: string;
|
|
40
40
|
sinceMs?: number;
|
|
@@ -95,6 +95,7 @@ export async function ensureObservabilityTables() {
|
|
|
95
95
|
message_seq ${intType()},
|
|
96
96
|
feedback_type TEXT NOT NULL,
|
|
97
97
|
value TEXT NOT NULL DEFAULT '',
|
|
98
|
+
idempotency_key TEXT,
|
|
98
99
|
user_id TEXT,
|
|
99
100
|
created_at ${intType()} NOT NULL
|
|
100
101
|
)
|
|
@@ -187,6 +188,7 @@ export async function ensureObservabilityTables() {
|
|
|
187
188
|
for (const table of USER_SCOPED_TABLES) {
|
|
188
189
|
await ensureColumnExists(table, "user_id", `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS user_id TEXT`);
|
|
189
190
|
}
|
|
191
|
+
await ensureColumnExists("agent_feedback", "idempotency_key", `ALTER TABLE agent_feedback ADD COLUMN IF NOT EXISTS idempotency_key TEXT`);
|
|
190
192
|
await ensureIndexExists("idx_trace_spans_run", `CREATE INDEX IF NOT EXISTS idx_trace_spans_run ON agent_trace_spans (run_id)`);
|
|
191
193
|
await ensureIndexExists("idx_trace_spans_thread", `CREATE INDEX IF NOT EXISTS idx_trace_spans_thread ON agent_trace_spans (thread_id)`);
|
|
192
194
|
await ensureIndexExists("idx_trace_spans_created", `CREATE INDEX IF NOT EXISTS idx_trace_spans_created ON agent_trace_spans (created_at)`);
|
|
@@ -198,6 +200,7 @@ export async function ensureObservabilityTables() {
|
|
|
198
200
|
await ensureIndexExists("idx_feedback_created", `CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`);
|
|
199
201
|
await ensureIndexExists("idx_feedback_user", `CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`);
|
|
200
202
|
await ensureIndexExists("idx_feedback_type_created", `CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`);
|
|
203
|
+
await ensureIndexExists("idx_feedback_idempotency", `CREATE UNIQUE INDEX IF NOT EXISTS idx_feedback_idempotency ON agent_feedback (user_id, idempotency_key)`);
|
|
201
204
|
await ensureIndexExists("idx_satisfaction_thread", `CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`);
|
|
202
205
|
await ensureIndexExists("idx_satisfaction_user", `CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`);
|
|
203
206
|
await ensureIndexExists("idx_evals_run", `CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`);
|
|
@@ -241,6 +244,13 @@ export async function ensureObservabilityTables() {
|
|
|
241
244
|
throw err;
|
|
242
245
|
}
|
|
243
246
|
}
|
|
247
|
+
try {
|
|
248
|
+
await client.execute(`ALTER TABLE agent_feedback ADD COLUMN idempotency_key TEXT`);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
if (!isDuplicateColumnError(err))
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
244
254
|
// Indexes for common query patterns
|
|
245
255
|
const indexes = [
|
|
246
256
|
`CREATE INDEX IF NOT EXISTS idx_trace_spans_run ON agent_trace_spans (run_id)`,
|
|
@@ -254,6 +264,7 @@ export async function ensureObservabilityTables() {
|
|
|
254
264
|
`CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`,
|
|
255
265
|
`CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`,
|
|
256
266
|
`CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`,
|
|
267
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_feedback_idempotency ON agent_feedback (user_id, idempotency_key)`,
|
|
257
268
|
`CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`,
|
|
258
269
|
`CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`,
|
|
259
270
|
`CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`,
|
|
@@ -478,10 +489,11 @@ export async function getLatestTraceSummaryForThread(threadId, opts) {
|
|
|
478
489
|
export async function insertFeedback(entry) {
|
|
479
490
|
await ensureObservabilityTables();
|
|
480
491
|
const client = getDbExec();
|
|
481
|
-
await client.execute({
|
|
492
|
+
const result = await client.execute({
|
|
482
493
|
sql: `INSERT INTO agent_feedback
|
|
483
|
-
(id, run_id, thread_id, message_seq, feedback_type, value, user_id, created_at)
|
|
484
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
494
|
+
(id, run_id, thread_id, message_seq, feedback_type, value, idempotency_key, user_id, created_at)
|
|
495
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
496
|
+
ON CONFLICT DO NOTHING`,
|
|
485
497
|
args: [
|
|
486
498
|
entry.id,
|
|
487
499
|
entry.runId,
|
|
@@ -489,10 +501,12 @@ export async function insertFeedback(entry) {
|
|
|
489
501
|
entry.messageSeq,
|
|
490
502
|
entry.feedbackType,
|
|
491
503
|
entry.value,
|
|
504
|
+
entry.idempotencyKey,
|
|
492
505
|
entry.userId,
|
|
493
506
|
entry.createdAt,
|
|
494
507
|
],
|
|
495
508
|
});
|
|
509
|
+
return result.rowsAffected > 0;
|
|
496
510
|
}
|
|
497
511
|
export async function getFeedback(opts) {
|
|
498
512
|
await ensureObservabilityTables();
|
|
@@ -1019,6 +1033,7 @@ function rowToFeedback(row) {
|
|
|
1019
1033
|
messageSeq: row.message_seq != null ? Number(row.message_seq) : null,
|
|
1020
1034
|
feedbackType: row.feedback_type,
|
|
1021
1035
|
value: String(row.value ?? ""),
|
|
1036
|
+
idempotencyKey: row.idempotency_key ? String(row.idempotency_key) : null,
|
|
1022
1037
|
userId: row.user_id ? String(row.user_id) : null,
|
|
1023
1038
|
createdAt: Number(row.created_at),
|
|
1024
1039
|
};
|
|
@@ -51,8 +51,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
51
51
|
error: string;
|
|
52
52
|
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
|
-
ok: boolean;
|
|
55
54
|
error?: undefined;
|
|
55
|
+
ok: boolean;
|
|
56
56
|
}>;
|
|
57
57
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
58
58
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,10 +73,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
+
error?: undefined;
|
|
76
77
|
url: string;
|
|
77
78
|
provider: string;
|
|
78
79
|
storageSetupRequired?: undefined;
|
|
79
|
-
error?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
error: string;
|
|
82
82
|
storageSetupRequired: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -60,10 +60,5 @@ export interface MountActionRoutesOptions {
|
|
|
60
60
|
*/
|
|
61
61
|
actionRouteAuth?: ActionRouteAuthAdapter;
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Mount discovered actions as HTTP endpoints.
|
|
65
|
-
*
|
|
66
|
-
* Only actions from `autoDiscoverActions` (template actions) are mounted.
|
|
67
|
-
* Built-in actions (resource-*, chat-*, shell, etc.) are NOT passed here.
|
|
68
|
-
*/
|
|
69
63
|
export declare function mountActionRoutes(nitroApp: any, actions: Record<string, ActionEntry>, options?: MountActionRoutesOptions): void;
|
|
64
|
+
export declare function mountWebMcpActionRoutes(nitroApp: any, actions: Record<string, ActionEntry>, options?: MountActionRoutesOptions): void;
|
|
@@ -240,21 +240,23 @@ async function resolveRequestAuthCapability(event) {
|
|
|
240
240
|
* Only actions from `autoDiscoverActions` (template actions) are mounted.
|
|
241
241
|
* Built-in actions (resource-*, chat-*, shell, etc.) are NOT passed here.
|
|
242
242
|
*/
|
|
243
|
-
|
|
243
|
+
function mountActionRoutesInternal(nitroApp, actions, options) {
|
|
244
244
|
const mounted = [];
|
|
245
245
|
const app = getH3App(nitroApp);
|
|
246
246
|
for (const [name, entry] of Object.entries(actions)) {
|
|
247
247
|
// Skip agent-only actions
|
|
248
|
-
if (entry.http === false)
|
|
248
|
+
if (entry.http === false && !options?.includeAgentOnly)
|
|
249
249
|
continue;
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const
|
|
250
|
+
const http = entry.http || undefined;
|
|
251
|
+
const method = options?.forcePost ? "POST" : (http?.method ?? "POST");
|
|
252
|
+
const path = options?.forcePost ? name : (http?.path ?? name);
|
|
253
|
+
const routePath = `${options?.routePrefix ?? ROUTE_PREFIX}/${path}`;
|
|
253
254
|
// These two actions authenticate with a scoped A2A bearer rather than a
|
|
254
255
|
// browser session. Let that verifier see the request before the cookie
|
|
255
256
|
// auth guard rejects it; the action route still fails closed on invalid
|
|
256
257
|
// or missing credentials.
|
|
257
|
-
if (
|
|
258
|
+
if (!options?.caller &&
|
|
259
|
+
(name === "list-feature-flags" || name === "set-feature-flag")) {
|
|
258
260
|
registerAuthPublicPaths([routePath], app);
|
|
259
261
|
}
|
|
260
262
|
app.use(routePath, defineEventHandler(async (event) => {
|
|
@@ -329,7 +331,7 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
329
331
|
// through, so a live same-origin session cookie can't silently execute
|
|
330
332
|
// the request as the logged-in user.
|
|
331
333
|
let resolvedCaller = null;
|
|
332
|
-
{
|
|
334
|
+
if (options?.allowDelegatedCaller !== false) {
|
|
333
335
|
let caller;
|
|
334
336
|
try {
|
|
335
337
|
caller = options?.actionRouteAuth?.resolveCaller
|
|
@@ -468,11 +470,12 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
468
470
|
// userEmail / orgId mirror the request context resolved above (do
|
|
469
471
|
// NOT inject a dev identity — leave undefined when unauthenticated).
|
|
470
472
|
try {
|
|
471
|
-
const caller =
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
473
|
+
const caller = options?.caller ??
|
|
474
|
+
(resolvedCaller
|
|
475
|
+
? "a2a"
|
|
476
|
+
: isFrontendActionRequest(event)
|
|
477
|
+
? "frontend"
|
|
478
|
+
: "http");
|
|
476
479
|
const result = await entry.run(params, {
|
|
477
480
|
userEmail,
|
|
478
481
|
orgId: orgId ?? null,
|
|
@@ -574,11 +577,12 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
574
577
|
method: reqMethod,
|
|
575
578
|
tags: {
|
|
576
579
|
action: name,
|
|
577
|
-
caller:
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
580
|
+
caller: options?.caller ??
|
|
581
|
+
(resolvedCaller
|
|
582
|
+
? "a2a"
|
|
583
|
+
: isFrontendActionRequest(event)
|
|
584
|
+
? "frontend"
|
|
585
|
+
: "http"),
|
|
582
586
|
status_code: String(status),
|
|
583
587
|
},
|
|
584
588
|
...(requestId ? { extra: { request_id: requestId } } : {}),
|
|
@@ -598,3 +602,40 @@ export function mountActionRoutes(nitroApp, actions, options) {
|
|
|
598
602
|
if (mounted.length > 0 && process.env.DEBUG)
|
|
599
603
|
console.log(`[action-routes] Mounted ${mounted.length} action route(s): ${mounted.join(", ")}`);
|
|
600
604
|
}
|
|
605
|
+
export function mountActionRoutes(nitroApp, actions, options) {
|
|
606
|
+
mountActionRoutesInternal(nitroApp, actions, options);
|
|
607
|
+
}
|
|
608
|
+
export function mountWebMcpActionRoutes(nitroApp, actions, options) {
|
|
609
|
+
const eligible = Object.fromEntries(Object.entries(actions).filter(([name, entry]) => /^[A-Za-z0-9_.-]{1,128}$/.test(name) &&
|
|
610
|
+
entry.agentTool !== false &&
|
|
611
|
+
entry.needsApproval === undefined));
|
|
612
|
+
if (Object.keys(eligible).length === 0)
|
|
613
|
+
return;
|
|
614
|
+
const app = getH3App(nitroApp);
|
|
615
|
+
app.use("/_agent-native/webmcp/manifest", defineEventHandler(async (event) => {
|
|
616
|
+
if (getMethod(event) !== "GET") {
|
|
617
|
+
setResponseStatus(event, 405);
|
|
618
|
+
return { error: "Method not allowed. Use GET." };
|
|
619
|
+
}
|
|
620
|
+
if (!options?.getOwnerFromEvent) {
|
|
621
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
622
|
+
}
|
|
623
|
+
await options.getOwnerFromEvent(event);
|
|
624
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
625
|
+
return Object.entries(eligible).map(([name, entry]) => ({
|
|
626
|
+
name,
|
|
627
|
+
description: entry.tool.description,
|
|
628
|
+
inputSchema: entry.tool.parameters,
|
|
629
|
+
readOnly: entry.readOnly === true,
|
|
630
|
+
}));
|
|
631
|
+
}));
|
|
632
|
+
mountActionRoutesInternal(nitroApp, eligible, {
|
|
633
|
+
...options,
|
|
634
|
+
routePrefix: "/_agent-native/webmcp/actions",
|
|
635
|
+
includeAgentOnly: true,
|
|
636
|
+
forcePost: true,
|
|
637
|
+
caller: "webmcp",
|
|
638
|
+
actionRouteAuth: undefined,
|
|
639
|
+
allowDelegatedCaller: false,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
@@ -2128,7 +2128,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2128
2128
|
// Ignore — templates without sharing still work.
|
|
2129
2129
|
}
|
|
2130
2130
|
if (Object.keys(httpActions).length > 0) {
|
|
2131
|
-
const { mountActionRoutes } = await import("./action-routes.js");
|
|
2131
|
+
const { mountActionRoutes, mountWebMcpActionRoutes } = await import("./action-routes.js");
|
|
2132
2132
|
if (options?.actionRoutePublicPaths?.length) {
|
|
2133
2133
|
registerAuthPublicPaths(options.actionRoutePublicPaths, getH3App(nitroApp));
|
|
2134
2134
|
}
|
|
@@ -2139,6 +2139,13 @@ export function createAgentChatPlugin(options) {
|
|
|
2139
2139
|
resolveOrgId: options?.resolveOrgId,
|
|
2140
2140
|
actionRouteAuth: options?.actionRouteAuth,
|
|
2141
2141
|
});
|
|
2142
|
+
mountWebMcpActionRoutes(nitroApp, httpActions, {
|
|
2143
|
+
getOwnerFromEvent,
|
|
2144
|
+
getUserNameFromEvent,
|
|
2145
|
+
appId: options?.appId,
|
|
2146
|
+
resolveOrgId: options?.resolveOrgId,
|
|
2147
|
+
actionRouteAuth: options?.actionRouteAuth,
|
|
2148
|
+
});
|
|
2142
2149
|
}
|
|
2143
2150
|
const preRunGitStatusByThread = new Map();
|
|
2144
2151
|
async function recordPreRunGitStatus(threadId) {
|
|
@@ -34,10 +34,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
34
34
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
35
35
|
error: any;
|
|
36
36
|
} | {
|
|
37
|
-
error?: undefined;
|
|
38
37
|
ok: boolean;
|
|
39
38
|
key: string;
|
|
40
39
|
baseUrlKey?: string;
|
|
41
40
|
scope: AgentEngineApiKeyScope;
|
|
41
|
+
error?: undefined;
|
|
42
42
|
}>>;
|
|
43
43
|
export {};
|
package/docs/content/webmcp.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "WebMCP"
|
|
3
|
-
description: "
|
|
3
|
+
description: "Automatically expose Agent-Native actions to browser agents, register explicit client actions, and consume page-local WebMCP tools from Agent-Native chat."
|
|
4
4
|
search: "WebMCP browser tools modelContext registerTool getTools executeTool browser session"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -15,24 +15,42 @@ discover them, and the browser mediates execution.
|
|
|
15
15
|
|
|
16
16
|
The framework supports both directions:
|
|
17
17
|
|
|
18
|
-
<!-- i18n-copy-ignore: oxfmt-only table alignment; no translated prose changed. -->
|
|
19
|
-
|
|
20
18
|
| You want to... | Use |
|
|
21
19
|
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
|
|
22
|
-
| Make
|
|
20
|
+
| Make backend actions available to a browser agent | Automatic on authenticated pages through `AppProviders` |
|
|
21
|
+
| Make page-local client actions available to a browser agent | `createAgentNativeWebMcpRegistration` |
|
|
23
22
|
| Let Agent-Native chat discover and call WebMCP tools in the current page | `createAgentNativeWebMcpClient` plus `webmcp` on `AgentNativeEmbedded` or `AgentNativeFrame` |
|
|
24
23
|
| Connect a headless or remote agent to an app | [MCP Server](/docs/mcp-protocol), [External Agents](/docs/external-agents), or [A2A](/docs/a2a-protocol) |
|
|
25
24
|
|
|
26
25
|
WebMCP is not a network MCP server and does not make a closed app globally
|
|
27
26
|
discoverable. A consuming agent needs an open, connected browser page and the
|
|
28
|
-
page must
|
|
27
|
+
page must expose tools to it.
|
|
29
28
|
|
|
30
29
|
The browser API is experimental and may change. See the [Chrome WebMCP
|
|
31
30
|
Imperative API](https://developer.chrome.com/docs/ai/webmcp/imperative-api) and
|
|
32
31
|
the [WebMCP specification](https://webmachinelearning.github.io/webmcp/) for
|
|
33
32
|
the current browser contract.
|
|
34
33
|
|
|
35
|
-
##
|
|
34
|
+
## Automatic backend actions
|
|
35
|
+
|
|
36
|
+
On an authenticated app page, `AppProviders` automatically registers eligible
|
|
37
|
+
backend actions with `document.modelContext`. The tools are derived from the
|
|
38
|
+
same action registry used by the in-app agent and execute through the same
|
|
39
|
+
validated, authenticated server dispatcher. This includes agent-only actions
|
|
40
|
+
declared with `http: false`; WebMCP does not turn those actions into public HTTP
|
|
41
|
+
APIs.
|
|
42
|
+
|
|
43
|
+
An action is eligible when `agentTool` is not `false` and `needsApproval` is
|
|
44
|
+
not declared. The latter is a fail-closed boundary: the server action approval
|
|
45
|
+
contract belongs to the agent loop and cannot be delegated to a browser hint.
|
|
46
|
+
Public paths, signed-out pages, and `sessionBypass` surfaces do not register
|
|
47
|
+
backend action tools. Browsers without `document.modelContext` simply keep the
|
|
48
|
+
normal app behavior.
|
|
49
|
+
|
|
50
|
+
No per-app WebMCP list or new `defineAction` metadata is required. Change the
|
|
51
|
+
existing action definition when its exposure should change.
|
|
52
|
+
|
|
53
|
+
## Explicit page-local actions
|
|
36
54
|
|
|
37
55
|
Register only the actions that should be available to a page agent. The
|
|
38
56
|
framework removes the action's local `run` function from the descriptor and
|
|
@@ -181,10 +199,10 @@ remote or headless tool calls, and use A2A when another app's agent should
|
|
|
181
199
|
receive work and reason over it. WebMCP is an opt-in browser bridge, not a
|
|
182
200
|
replacement for either network protocol.
|
|
183
201
|
|
|
184
|
-
The framework
|
|
185
|
-
|
|
186
|
-
retain normal MCP or A2A exposure for
|
|
187
|
-
open browser page.
|
|
202
|
+
The framework automatically projects eligible backend actions and still lets a
|
|
203
|
+
page register explicit client-only capabilities. Keep server-side approval
|
|
204
|
+
actions on the agent loop, and retain normal MCP or A2A exposure for
|
|
205
|
+
capabilities that must work without an open browser page.
|
|
188
206
|
|
|
189
207
|
## Compatibility and safety
|
|
190
208
|
|
|
@@ -195,7 +213,8 @@ open browser page.
|
|
|
195
213
|
- Treat descriptions, schemas, annotations, and results as page-provided
|
|
196
214
|
data. The framework validates and bounds them, but agents should still treat
|
|
197
215
|
`untrustedContentHint` results as untrusted content.
|
|
198
|
-
-
|
|
199
|
-
remain
|
|
216
|
+
- Automatic registrations are aborted when the private app shell unmounts. Use
|
|
217
|
+
`stop()` for explicit page-local registrations so stale tools do not remain
|
|
218
|
+
available.
|
|
200
219
|
- Keep server MCP and A2A endpoints for remote access. WebMCP only reaches the
|
|
201
220
|
tools registered in a connected browser context.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.177.0-nightly-20260828155142",
|
|
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": {
|