@agent-native/core 0.161.22 → 0.162.0
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/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +18 -2
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/calendar/server/handlers/google-auth.ts +1 -8
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +88 -35
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +36 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -3
- package/corpus/templates/clips/desktop/src/app.tsx +30 -40
- package/corpus/templates/clips/desktop/src/components/MicOffConfirmation.tsx +47 -0
- package/corpus/templates/clips/desktop/src/styles.css +110 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +114 -7
- package/corpus/templates/clips/shared/recording-capture.ts +18 -13
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -5
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +0 -3
- package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +0 -4
- package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +4 -1
- package/corpus/templates/content/app/hooks/use-notion.ts +17 -15
- package/corpus/templates/crm/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +13 -7
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +18 -21
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +2 -5
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +4 -12
- package/corpus/templates/design/app/pages/design-editor/types.ts +38 -1
- package/corpus/templates/design/changelog/2026-08-18-advanced-design-editor-panels-hidden-by-default.md +6 -0
- package/corpus/templates/design/playwright.config.ts +19 -2
- package/corpus/templates/dispatch/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +18 -3
- package/corpus/templates/mail/server/handlers/google-auth.ts +1 -8
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +4 -0
- package/corpus/templates/slides/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/slides/actions/_await-fit-check.ts +30 -5
- package/corpus/templates/slides/actions/add-slide.ts +6 -2
- package/corpus/templates/slides/actions/create-deck.ts +2 -1
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +34 -1
- package/corpus/templates/slides/app/routes/p.$id.tsx +3 -1
- package/corpus/templates/slides/changelog/2026-08-18-generated-slides-preserve-speaker-notes.md +6 -0
- package/corpus/templates/slides/server/handlers/assets.ts +23 -12
- package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +15 -6
- package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +15 -6
- package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +34 -19
- package/corpus/templates/slides/server/handlers/share.ts +1 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/slides/server/routes/api/deck-agent-context.json.get.ts +1 -1
- package/corpus/templates/slides/server/routes/api/exports/[filename].get.ts +9 -3
- package/corpus/templates/slides/server/routes/api/exports/google-slides.post.ts +12 -5
- package/corpus/templates/slides/server/routes/api/exports/html.post.ts +9 -7
- package/corpus/templates/slides/server/routes/api/exports/pptx.post.ts +9 -7
- package/corpus/templates/slides/shared/api.ts +7 -1
- package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/agent/engine/builder-engine.js +15 -8
- package/dist/agent/engine/translate-anthropic.js +27 -2
- package/dist/agent/production-agent.d.ts +17 -2
- package/dist/agent/production-agent.js +48 -17
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +6 -4
- package/dist/agent/tool-approval-store.d.ts +10 -1
- package/dist/agent/tool-approval-store.js +19 -2
- package/dist/agent/types.d.ts +9 -0
- package/dist/client/AssistantChat.js +14 -6
- package/dist/client/EnvironmentBadge.d.ts +15 -0
- package/dist/client/EnvironmentBadge.js +79 -0
- package/dist/client/MultiTabAssistantChat.js +73 -17
- package/dist/client/agent-chat-adapter.js +11 -1
- package/dist/client/api-path.js +14 -1
- package/dist/client/app-providers.js +2 -1
- package/dist/client/chat/tool-call-display.d.ts +12 -2
- package/dist/client/chat/tool-call-display.js +17 -7
- package/dist/client/chat/widgets/WorkspaceFileWidget.js +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts +6 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.js +19 -0
- package/dist/client/onboarding/FirstRunOnboarding.js +46 -32
- package/dist/client/onboarding/use-onboarding.d.ts +8 -1
- package/dist/client/onboarding/use-onboarding.js +22 -7
- package/dist/client/session-replay.js +58 -15
- package/dist/client/settings/SettingsTabsPage.js +19 -6
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.js +4 -1
- package/dist/client/ui/index.d.ts +1 -0
- package/dist/client/ui/index.js +1 -0
- package/dist/client/use-action.js +26 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.js +53 -0
- package/dist/extensions/fetch-tool.js +8 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/embed-app.js +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/index.d.ts +10 -0
- package/dist/provider-api/index.js +45 -9
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +10 -3
- package/dist/server/poll.js +7 -3
- package/dist/server/sentry.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/vite/client.d.ts +5 -0
- package/dist/vite/client.js +64 -2
- package/dist/workspace-files/actions.d.ts +1 -8
- package/dist/workspace-files/actions.js +5 -21
- package/dist/workspace-files/index.d.ts +1 -1
- package/dist/workspace-files/index.js +1 -1
- package/dist/workspace-files/store.d.ts +18 -0
- package/dist/workspace-files/store.js +24 -3
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FeatureNotConfiguredError,
|
|
3
|
-
getSession,
|
|
4
3
|
indexBuilderDesignSystem,
|
|
5
4
|
} from "@agent-native/core/server";
|
|
6
5
|
import { defineEventHandler, readBody, setResponseStatus } from "h3";
|
|
7
6
|
|
|
8
7
|
import { upsertBuilderProxyDesignSystem } from "../lib/builder-design-system-proxy.js";
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
resolveSlidesRequestAuth,
|
|
10
|
+
withSlidesRequestContext,
|
|
11
|
+
} from "./request-auth-context.js";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Finalizes Builder DSI indexing from upload tokens produced by the
|
|
@@ -14,8 +16,14 @@ import { withSlidesRequestContext } from "./request-auth-context.js";
|
|
|
14
16
|
* storage; this endpoint only forwards the opaque tokens.
|
|
15
17
|
*/
|
|
16
18
|
export const indexDesignSystemSources = defineEventHandler(async (event) => {
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
19
|
+
const auth = await resolveSlidesRequestAuth(event);
|
|
20
|
+
if (!auth.ok) {
|
|
21
|
+
setResponseStatus(event, auth.statusCode);
|
|
22
|
+
return { error: auth.error };
|
|
23
|
+
}
|
|
24
|
+
const session = auth.context;
|
|
25
|
+
const sessionEmail = session.email;
|
|
26
|
+
if (!sessionEmail) {
|
|
19
27
|
setResponseStatus(event, 401);
|
|
20
28
|
return { error: "Unauthorized" };
|
|
21
29
|
}
|
|
@@ -45,21 +53,28 @@ export const indexDesignSystemSources = defineEventHandler(async (event) => {
|
|
|
45
53
|
}));
|
|
46
54
|
|
|
47
55
|
try {
|
|
48
|
-
return await withSlidesRequestContext(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
result
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
return await withSlidesRequestContext(
|
|
57
|
+
event,
|
|
58
|
+
async ({ email, orgId }) => {
|
|
59
|
+
const result = await indexBuilderDesignSystem({
|
|
60
|
+
sources,
|
|
61
|
+
projectName,
|
|
62
|
+
});
|
|
63
|
+
const proxy = await upsertBuilderProxyDesignSystem({
|
|
64
|
+
result,
|
|
65
|
+
ownerEmail: email ?? sessionEmail,
|
|
66
|
+
orgId: orgId ?? null,
|
|
67
|
+
projectName,
|
|
68
|
+
sourceKind: "figma",
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
...proxy,
|
|
73
|
+
uploadedFileCount: uploadTokens.length,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
session,
|
|
77
|
+
);
|
|
63
78
|
} catch (err) {
|
|
64
79
|
if (err instanceof FeatureNotConfiguredError) {
|
|
65
80
|
setResponseStatus(event, 412);
|
|
@@ -116,7 +116,7 @@ async function createShareLink(event: any, deckId: string) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const slides = storedDeck.slides.map((slide: unknown, index: number) =>
|
|
119
|
-
toSharedDeckSlide(slide, index),
|
|
119
|
+
toSharedDeckSlide(slide, index, { includeNotes: false }),
|
|
120
120
|
);
|
|
121
121
|
|
|
122
122
|
await db.insert(schema.deckShareLinks).values({
|
|
@@ -57,7 +57,7 @@ export default createAgentChatPlugin({
|
|
|
57
57
|
return authContext.orgId === undefined ? null : authContext.orgId;
|
|
58
58
|
},
|
|
59
59
|
prepareRequest: prepareSlidesChatAttachments,
|
|
60
|
-
systemPrompt: `You are an AI deck assistant. You create, edit, import, export, style, share, and navigate decks through actions and shared application state. For a newly created presentation, use create-deck with slides: [] only when you are creating the deck yourself, then add-slide sequentially with full rendered HTML. The legacy generate-slides-ai action returns Markdown drafts and is not part of the persisted presentation workflow.
|
|
60
|
+
systemPrompt: `You are an AI deck assistant. You create, edit, import, export, style, share, and navigate decks through actions and shared application state. For a newly created presentation, use create-deck with slides: [] only when you are creating the deck yourself, then add-slide sequentially with full rendered HTML. The legacy generate-slides-ai action returns Markdown drafts and is not part of the persisted presentation workflow. When speaker notes are requested, keep presenter-only text in each slide's notes field rather than the slide HTML, and preserve notes during source-preserving edits.
|
|
61
61
|
|
|
62
62
|
When the user asks to improve, beautify, restyle, or make an uploaded/existing deck on-brand, treat it as an in-place source-preserving edit unless the user explicitly asks to rewrite the story or change slide count. First call view-screen when the active deck is unclear, then get-deck with compact=true for deck orientation. If you need slide markup, call get-deck with compact=false explicitly and only when the edit requires the full HTML. If get-deck.sourceImport exists, preserve its slide count, order, IDs, factual copy, notes, images, charts, tables, diagrams, freeform objects, and source aspect ratio. For a deck-wide restyle, use one patch-deck call with requireAllSourceSlides=true and one patch-slide operation with fields.content for every source slide ID; the action rejects partial coverage. Do not split a full-deck restyle into arbitrary batches or use one-by-one update-slide calls - reserve update-slide for targeted one-slide edits. After the patch succeeds, verify with get-deck using compact=true so the verification does not retransmit every slide's HTML. Do not claim a partial or initial pass. Do not use add-slide, delete, reorder, or replace source imagery with generic cards for this workflow. If sourceImport.fidelity is partial or imagesSkipped is nonzero, stop and report the exact fidelity warning instead of claiming a reliable improvement.
|
|
63
63
|
For a targeted one-slide edit, prefer get-deck with the stable slideId from view-screen or compact get-deck. It returns only that slide's full HTML by default, so do not load the full deck when one slide is enough. For code-style edits, request compact=false and format=true, then call update-slide with that slide's contentHash as baseContentHash and an ordered edits list. Use exact replace, insert-before/after, replace-between, or regex-replace edits; include expectedMatches for ambiguous markers. The action applies the whole list atomically under the deck lock, so a failed required match writes nothing. Set format=true on update-slide when readable line breaks should be persisted. Use fullContent only for an intentional full rewrite. Pass compact=true when you only need a lightweight check of that slide.
|
|
@@ -77,7 +77,7 @@ export default defineEventHandler(async (event) => {
|
|
|
77
77
|
const data = parseDeckData(deck.data);
|
|
78
78
|
const slides = Array.isArray(data.slides)
|
|
79
79
|
? data.slides.map((slide: unknown, index: number) =>
|
|
80
|
-
toSharedDeckSlide(slide, index),
|
|
80
|
+
toSharedDeckSlide(slide, index, { includeNotes: false }),
|
|
81
81
|
)
|
|
82
82
|
: [];
|
|
83
83
|
|
|
@@ -2,9 +2,10 @@ import { createReadStream } from "fs";
|
|
|
2
2
|
import { stat } from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { streamFile } from "@agent-native/core/server";
|
|
6
6
|
import { defineEventHandler, getRouterParam, setResponseStatus } from "h3";
|
|
7
7
|
|
|
8
|
+
import { resolveSlidesRequestAuth } from "../../../handlers/request-auth-context.js";
|
|
8
9
|
import { tenantExportDir } from "../../../lib/tenant-files.js";
|
|
9
10
|
|
|
10
11
|
const CONTENT_TYPES: Record<string, string> = {
|
|
@@ -15,8 +16,13 @@ const CONTENT_TYPES: Record<string, string> = {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export default defineEventHandler(async (event) => {
|
|
18
|
-
const
|
|
19
|
-
if (!
|
|
19
|
+
const auth = await resolveSlidesRequestAuth(event);
|
|
20
|
+
if (!auth.ok) {
|
|
21
|
+
setResponseStatus(event, auth.statusCode);
|
|
22
|
+
return { error: auth.error };
|
|
23
|
+
}
|
|
24
|
+
const session = auth.context;
|
|
25
|
+
if (!session.email) {
|
|
20
26
|
setResponseStatus(event, 401);
|
|
21
27
|
return { error: "Unauthorized" };
|
|
22
28
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runWithRequestContext } from "@agent-native/core/server";
|
|
2
2
|
import {
|
|
3
3
|
defineEventHandler,
|
|
4
4
|
readMultipartFormData,
|
|
5
5
|
setResponseStatus,
|
|
6
6
|
} from "h3";
|
|
7
7
|
|
|
8
|
+
import { resolveSlidesRequestAuth } from "../../../handlers/request-auth-context.js";
|
|
8
9
|
import { getGoogleDocsAccessToken } from "../../../lib/google-docs-oauth.js";
|
|
9
10
|
|
|
10
11
|
const PPTX_CONTENT_TYPE =
|
|
@@ -28,8 +29,14 @@ const UPLOAD_URL =
|
|
|
28
29
|
* See "Export Behavior" in `templates/slides/AGENTS.md` for the routing rule.
|
|
29
30
|
*/
|
|
30
31
|
export default defineEventHandler(async (event) => {
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
32
|
+
const auth = await resolveSlidesRequestAuth(event);
|
|
33
|
+
if (!auth.ok) {
|
|
34
|
+
setResponseStatus(event, auth.statusCode);
|
|
35
|
+
return { error: auth.error };
|
|
36
|
+
}
|
|
37
|
+
const session = auth.context;
|
|
38
|
+
const sessionEmail = session.email;
|
|
39
|
+
if (!sessionEmail) {
|
|
33
40
|
setResponseStatus(event, 401);
|
|
34
41
|
return { error: "Unauthorized" };
|
|
35
42
|
}
|
|
@@ -50,8 +57,8 @@ export default defineEventHandler(async (event) => {
|
|
|
50
57
|
// be org-scoped vault secrets, and resolving them without the org reports the
|
|
51
58
|
// integration as unconfigured.
|
|
52
59
|
const account = await runWithRequestContext(
|
|
53
|
-
{ userEmail:
|
|
54
|
-
() => getGoogleDocsAccessToken(
|
|
60
|
+
{ userEmail: sessionEmail, orgId: session.orgId },
|
|
61
|
+
() => getGoogleDocsAccessToken(sessionEmail),
|
|
55
62
|
);
|
|
56
63
|
if (!account) {
|
|
57
64
|
setResponseStatus(event, 409);
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
getSession,
|
|
5
|
-
readBody,
|
|
6
|
-
runWithRequestContext,
|
|
7
|
-
} from "@agent-native/core/server";
|
|
3
|
+
import { readBody, runWithRequestContext } from "@agent-native/core/server";
|
|
8
4
|
import { defineEventHandler, setResponseStatus } from "h3";
|
|
9
5
|
|
|
10
6
|
import exportHtmlAction from "../../../../actions/export-html.js";
|
|
7
|
+
import { resolveSlidesRequestAuth } from "../../../handlers/request-auth-context.js";
|
|
11
8
|
|
|
12
9
|
export default defineEventHandler(async (event) => {
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
10
|
+
const auth = await resolveSlidesRequestAuth(event);
|
|
11
|
+
if (!auth.ok) {
|
|
12
|
+
setResponseStatus(event, auth.statusCode);
|
|
13
|
+
return { error: auth.error };
|
|
14
|
+
}
|
|
15
|
+
const session = auth.context;
|
|
16
|
+
if (!session.email) {
|
|
15
17
|
setResponseStatus(event, 401);
|
|
16
18
|
return { error: "Unauthorized" };
|
|
17
19
|
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
getSession,
|
|
5
|
-
readBody,
|
|
6
|
-
runWithRequestContext,
|
|
7
|
-
} from "@agent-native/core/server";
|
|
3
|
+
import { readBody, runWithRequestContext } from "@agent-native/core/server";
|
|
8
4
|
import { defineEventHandler, setResponseStatus } from "h3";
|
|
9
5
|
|
|
10
6
|
import exportPptxAction from "../../../../actions/export-pptx.js";
|
|
7
|
+
import { resolveSlidesRequestAuth } from "../../../handlers/request-auth-context.js";
|
|
11
8
|
|
|
12
9
|
const PPTX_CONTENT_TYPE =
|
|
13
10
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
14
11
|
|
|
15
12
|
export default defineEventHandler(async (event) => {
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
13
|
+
const auth = await resolveSlidesRequestAuth(event);
|
|
14
|
+
if (!auth.ok) {
|
|
15
|
+
setResponseStatus(event, auth.statusCode);
|
|
16
|
+
return { error: auth.error };
|
|
17
|
+
}
|
|
18
|
+
const session = auth.context;
|
|
19
|
+
if (!session.email) {
|
|
18
20
|
setResponseStatus(event, 401);
|
|
19
21
|
return { error: "Unauthorized" };
|
|
20
22
|
}
|
|
@@ -126,6 +126,11 @@ export interface SharedDeckSlide {
|
|
|
126
126
|
splitByParagraph?: boolean;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
export interface SharedDeckSlideOptions {
|
|
130
|
+
/** Include presenter-only notes for an authenticated internal read. */
|
|
131
|
+
includeNotes?: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
129
134
|
const SHARED_SLIDE_TRANSITIONS = new Set<SharedSlideTransition>([
|
|
130
135
|
"instant",
|
|
131
136
|
"none",
|
|
@@ -216,12 +221,13 @@ function normalizeSlideAnimation(
|
|
|
216
221
|
export function toSharedDeckSlide(
|
|
217
222
|
value: unknown,
|
|
218
223
|
index: number,
|
|
224
|
+
options: SharedDeckSlideOptions = {},
|
|
219
225
|
): SharedDeckSlide {
|
|
220
226
|
const slide = isRecord(value) ? value : {};
|
|
221
227
|
const shared: SharedDeckSlide = {
|
|
222
228
|
id: normalizeString(slide.id, `slide-${index + 1}`),
|
|
223
229
|
content: normalizeString(slide.content, ""),
|
|
224
|
-
notes: "",
|
|
230
|
+
notes: options.includeNotes ? normalizeString(slide.notes, "") : "",
|
|
225
231
|
layout: normalizeString(slide.layout, "content"),
|
|
226
232
|
};
|
|
227
233
|
|
|
@@ -50,7 +50,7 @@ The agent modifies data in SQL, but the UI runs in the browser. SSE bridges same
|
|
|
50
50
|
|
|
51
51
|
For list/sidebar queries, use the same pattern — pass the counter into the queryKey of every list query you want to keep fresh.
|
|
52
52
|
|
|
53
|
-
4. **Fallback** polling calls `/_agent-native/poll?since=N`. It
|
|
53
|
+
4. **Fallback** polling calls `/_agent-native/poll?since=N`. It polls every 2 seconds only while an agent run is in flight; otherwise it idles at 60 seconds whether or not SSE is connected (`SSE_FALLBACK_INTERVAL_MS` and `IDLE_POLL_INTERVAL_MS`, both 60 s). A hidden tab polls no faster than every 10 seconds (`HIDDEN_POLL_INTERVAL_MS`). Polling is the universal serverless fallback: new framework writes are read from the durable `sync_events` log, while the older DB timestamp scan remains as a slower safety net for direct SQL writes and older processes.
|
|
54
54
|
|
|
55
55
|
5. When the agent writes to the database, the version increments, SSE/polling detects it, and React Query refetches the affected queries.
|
|
56
56
|
|
|
@@ -138,10 +138,17 @@ class BuilderEngine {
|
|
|
138
138
|
}, creditsLane);
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
|
+
// The Builder gateway has an "auto" fallback mode, but Agent Native owns
|
|
142
|
+
// model selection. Always send a concrete model so the gateway cannot
|
|
143
|
+
// select an organization-level override or another fallback model.
|
|
144
|
+
const requestedModel = opts.model.trim();
|
|
145
|
+
const model = requestedModel.length === 0 || requestedModel === "auto"
|
|
146
|
+
? BUILDER_DEFAULT_MODEL
|
|
147
|
+
: requestedModel;
|
|
141
148
|
const messages = engineMessagesToBuilderGatewayAnthropic(opts.messages);
|
|
142
149
|
const tools = engineToolsToAnthropic(opts.tools);
|
|
143
150
|
const thinkingBudget = opts.providerOptions?.anthropic?.thinking?.budgetTokens;
|
|
144
|
-
const reasoningEffort = normalizeReasoningEffortForModel(
|
|
151
|
+
const reasoningEffort = normalizeReasoningEffortForModel(model, opts.reasoningEffort ??
|
|
145
152
|
(typeof thinkingBudget === "number"
|
|
146
153
|
? mapReasoningEffort(thinkingBudget)
|
|
147
154
|
: undefined));
|
|
@@ -200,13 +207,13 @@ class BuilderEngine {
|
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
|
-
const gptToolsRequireExplicitNoReasoning = cachedTools.length > 0 && isGPTReasoningModel(
|
|
210
|
+
const gptToolsRequireExplicitNoReasoning = cachedTools.length > 0 && isGPTReasoningModel(model);
|
|
204
211
|
const body = {
|
|
205
|
-
model
|
|
212
|
+
model,
|
|
206
213
|
messages: cachedMessages,
|
|
207
214
|
...(systemValue !== undefined ? { system: systemValue } : {}),
|
|
208
215
|
...(cachedTools.length > 0 ? { tools: cachedTools } : {}),
|
|
209
|
-
max_tokens: resolveMaxOutputTokensForEngine(this.name, opts.maxOutputTokens,
|
|
216
|
+
max_tokens: resolveMaxOutputTokensForEngine(this.name, opts.maxOutputTokens, model),
|
|
210
217
|
...(typeof opts.temperature === "number"
|
|
211
218
|
? { temperature: opts.temperature }
|
|
212
219
|
: {}),
|
|
@@ -232,7 +239,7 @@ class BuilderEngine {
|
|
|
232
239
|
// gateway outage are the same capture.
|
|
233
240
|
const payload = JSON.stringify(body);
|
|
234
241
|
const requestShape = {
|
|
235
|
-
model
|
|
242
|
+
model,
|
|
236
243
|
payloadBytes: new TextEncoder().encode(payload).length,
|
|
237
244
|
toolCount: cachedTools.length,
|
|
238
245
|
messageCount: cachedMessages.length,
|
|
@@ -242,7 +249,7 @@ class BuilderEngine {
|
|
|
242
249
|
gatewayUrl.searchParams.set("apiKey", spaceId);
|
|
243
250
|
const orgLabel = creds.orgName || "unknown-org";
|
|
244
251
|
const tStart = Date.now();
|
|
245
|
-
console.log(`[builder-engine] → POST ${gatewayUrl.origin}${gatewayUrl.pathname} model=${
|
|
252
|
+
console.log(`[builder-engine] → POST ${gatewayUrl.origin}${gatewayUrl.pathname} model=${model} tools=${tools.length} org=${orgLabel}`);
|
|
246
253
|
const gatewayTimeoutMs = getBuilderGatewayTimeoutMs();
|
|
247
254
|
const gatewayAbort = createGatewayAbortSignal(opts.abortSignal, gatewayTimeoutMs);
|
|
248
255
|
try {
|
|
@@ -269,7 +276,7 @@ class BuilderEngine {
|
|
|
269
276
|
if (timedOut || isBuilderGatewayNetworkError(err)) {
|
|
270
277
|
captureBuilderGatewayTransportError(err, {
|
|
271
278
|
phase: "request",
|
|
272
|
-
model
|
|
279
|
+
model,
|
|
273
280
|
gatewayUrl,
|
|
274
281
|
timeoutMs: gatewayAbort.effectiveTimeoutMs(),
|
|
275
282
|
timedOut,
|
|
@@ -325,7 +332,7 @@ class BuilderEngine {
|
|
|
325
332
|
}, creditsLane, requestShape);
|
|
326
333
|
return;
|
|
327
334
|
}
|
|
328
|
-
yield* parseJsonlStream(reader,
|
|
335
|
+
yield* parseJsonlStream(reader, model, {
|
|
329
336
|
creditsLane,
|
|
330
337
|
abortSignal: gatewayAbort.signal,
|
|
331
338
|
didGatewayTimeout: gatewayAbort.didTimeout,
|
|
@@ -261,17 +261,42 @@ export function backfillEngineMessagesToolResults(messages) {
|
|
|
261
261
|
// ---------------------------------------------------------------------------
|
|
262
262
|
// EngineMessage → Anthropic.MessageParam
|
|
263
263
|
// ---------------------------------------------------------------------------
|
|
264
|
+
/**
|
|
265
|
+
* A thinking block replays only with the signature Anthropic issued for it, or
|
|
266
|
+
* (redacted) with its encrypted payload. An empty signature is not a fallback:
|
|
267
|
+
* the native API rejects the whole request, so a turn that streamed fine dies
|
|
268
|
+
* with a provider error pointing nowhere near the cause. Drop the unsendable
|
|
269
|
+
* block and say so instead of coercing it into a guaranteed 400.
|
|
270
|
+
*
|
|
271
|
+
* Not applied to the Builder gateway: its tolerance for an unsigned thinking
|
|
272
|
+
* block is unverified, so that path keeps its existing behavior rather than
|
|
273
|
+
* trading a working request for an untested one.
|
|
274
|
+
*/
|
|
275
|
+
function replayableAnthropicPart(part) {
|
|
276
|
+
if (part.type !== "thinking")
|
|
277
|
+
return true;
|
|
278
|
+
if (part.redactedData || part.signature)
|
|
279
|
+
return true;
|
|
280
|
+
console.warn("[anthropic-engine] dropping a thinking block with no signature; it cannot be replayed");
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
264
283
|
export function engineMessageToAnthropic(msg, opts) {
|
|
265
284
|
const builderGateway = opts?.builderGateway === true;
|
|
285
|
+
const content = builderGateway
|
|
286
|
+
? msg.content
|
|
287
|
+
: msg.content.filter(replayableAnthropicPart);
|
|
266
288
|
return {
|
|
267
289
|
role: msg.role,
|
|
268
|
-
content:
|
|
290
|
+
content: content.map((p) => enginePartToAnthropic(p, builderGateway)),
|
|
269
291
|
};
|
|
270
292
|
}
|
|
271
293
|
/** Messages for the Anthropic HTTP API (strict schema — no extra tool_result fields). */
|
|
272
294
|
export function engineMessagesToAnthropic(messages) {
|
|
273
295
|
const normalized = backfillEngineMessagesToolResults(messages);
|
|
274
|
-
return normalized.
|
|
296
|
+
return normalized.flatMap((m) => {
|
|
297
|
+
const translated = engineMessageToAnthropic(m);
|
|
298
|
+
return translated.content.length > 0 ? [translated] : [];
|
|
299
|
+
});
|
|
275
300
|
}
|
|
276
301
|
/**
|
|
277
302
|
* Messages for the Builder LLM gateway (Gemini-backed). Same Anthropic-shaped
|
|
@@ -700,7 +700,7 @@ export interface ExecuteAgentToolCallOptions {
|
|
|
700
700
|
threadId?: string;
|
|
701
701
|
turnId?: string;
|
|
702
702
|
approvedToolCalls?: string[];
|
|
703
|
-
onApprovalRequired?: (binding: AgentApprovalBinding) => Promise<void>;
|
|
703
|
+
onApprovalRequired?: (binding: AgentApprovalBinding) => Promise<string | void>;
|
|
704
704
|
consumeApproval?: (binding: AgentApprovalBinding) => Promise<boolean>;
|
|
705
705
|
send?: (event: AgentChatEvent) => void;
|
|
706
706
|
}
|
|
@@ -877,7 +877,7 @@ export declare function runAgentLoop(opts: {
|
|
|
877
877
|
* so client-supplied history is never the authorization source; direct loop
|
|
878
878
|
* callers may omit them when they own the surrounding approval boundary.
|
|
879
879
|
*/
|
|
880
|
-
onApprovalRequired?: (binding: AgentApprovalBinding) => Promise<void>;
|
|
880
|
+
onApprovalRequired?: (binding: AgentApprovalBinding) => Promise<string | void>;
|
|
881
881
|
consumeApproval?: (binding: AgentApprovalBinding) => Promise<boolean>;
|
|
882
882
|
/**
|
|
883
883
|
* In-loop processor seam (see `processors.ts`). Each processor can observe
|
|
@@ -1351,5 +1351,20 @@ export declare function resolveAgentRequestReasoningEffort({ model, requestEffor
|
|
|
1351
1351
|
requestEffort?: unknown;
|
|
1352
1352
|
configuredEffort?: ReasoningEffort;
|
|
1353
1353
|
}): ReasoningEffort | undefined;
|
|
1354
|
+
export type AgentModelSelectionSource = "request" | "configured" | "stored" | "default";
|
|
1355
|
+
/**
|
|
1356
|
+
* Resolve the model before the engine boundary. `auto` is a UI sentinel, not
|
|
1357
|
+
* a model to forward to the gateway, so it gives way to an AN org/user
|
|
1358
|
+
* default, then the configured global engine default.
|
|
1359
|
+
*/
|
|
1360
|
+
export declare function resolveAgentModelSelection(options: {
|
|
1361
|
+
requestModel?: string | null;
|
|
1362
|
+
configuredModel?: string | null;
|
|
1363
|
+
storedModel?: string | null;
|
|
1364
|
+
defaultModel: string;
|
|
1365
|
+
}): {
|
|
1366
|
+
model: string;
|
|
1367
|
+
source: AgentModelSelectionSource;
|
|
1368
|
+
};
|
|
1354
1369
|
export declare function createProductionAgentHandler(options: ProductionAgentOptions): H3EventHandler;
|
|
1355
1370
|
export { getActiveRunForThread, getActiveRunForThreadAsync, getRun, abortRun, abortRunDurably, abortTurnDurably, subscribeToRun, };
|
|
@@ -4331,7 +4331,14 @@ export async function runAgentLoop(opts) {
|
|
|
4331
4331
|
mustApprove = true;
|
|
4332
4332
|
}
|
|
4333
4333
|
if (mustApprove) {
|
|
4334
|
-
|
|
4334
|
+
// `askId` identifies THIS gate hit, distinct from any earlier ask
|
|
4335
|
+
// for the same approvalKey/toolCallId. A failed resume (expired
|
|
4336
|
+
// grant, turn-id mismatch) re-enters this branch and mints a new
|
|
4337
|
+
// askId, which is how the client tells "an already-approved card
|
|
4338
|
+
// re-rendered" apart from "a fresh ask after the grant didn't land"
|
|
4339
|
+
// — without it, a stale client-side "approved" mark permanently
|
|
4340
|
+
// hides Approve/Deny with no way to retry.
|
|
4341
|
+
const askId = (await opts.onApprovalRequired?.(approvalBinding)) || undefined;
|
|
4335
4342
|
send({
|
|
4336
4343
|
type: "tool_start",
|
|
4337
4344
|
id: toolCall.id,
|
|
@@ -4343,6 +4350,7 @@ export async function runAgentLoop(opts) {
|
|
|
4343
4350
|
tool: toolCall.name,
|
|
4344
4351
|
input: toolCall.input,
|
|
4345
4352
|
approvalKey,
|
|
4353
|
+
...(askId ? { askId } : {}),
|
|
4346
4354
|
...(toolCall.id ? { toolCallId: toolCall.id } : {}),
|
|
4347
4355
|
});
|
|
4348
4356
|
// Audit the blocked attempt: the action did NOT run, but "the agent
|
|
@@ -6275,6 +6283,27 @@ function progressStepFromAgentChatEvent(event) {
|
|
|
6275
6283
|
export function resolveAgentRequestReasoningEffort({ model, requestEffort, configuredEffort, }) {
|
|
6276
6284
|
return normalizeReasoningEffortForRequest(model, isReasoningEffort(requestEffort) ? requestEffort : configuredEffort);
|
|
6277
6285
|
}
|
|
6286
|
+
function isConcreteModelSelection(model) {
|
|
6287
|
+
const normalized = typeof model === "string" ? model.trim() : "";
|
|
6288
|
+
return normalized.length > 0 && normalized !== "auto";
|
|
6289
|
+
}
|
|
6290
|
+
/**
|
|
6291
|
+
* Resolve the model before the engine boundary. `auto` is a UI sentinel, not
|
|
6292
|
+
* a model to forward to the gateway, so it gives way to an AN org/user
|
|
6293
|
+
* default, then the configured global engine default.
|
|
6294
|
+
*/
|
|
6295
|
+
export function resolveAgentModelSelection(options) {
|
|
6296
|
+
if (isConcreteModelSelection(options.requestModel)) {
|
|
6297
|
+
return { model: options.requestModel, source: "request" };
|
|
6298
|
+
}
|
|
6299
|
+
if (isConcreteModelSelection(options.configuredModel)) {
|
|
6300
|
+
return { model: options.configuredModel, source: "configured" };
|
|
6301
|
+
}
|
|
6302
|
+
if (isConcreteModelSelection(options.storedModel)) {
|
|
6303
|
+
return { model: options.storedModel, source: "stored" };
|
|
6304
|
+
}
|
|
6305
|
+
return { model: options.defaultModel, source: "default" };
|
|
6306
|
+
}
|
|
6278
6307
|
export function createProductionAgentHandler(options) {
|
|
6279
6308
|
if (options.resolveActionSurface) {
|
|
6280
6309
|
assertRequestActionSurfaceIsolation();
|
|
@@ -6644,21 +6673,23 @@ export function createProductionAgentHandler(options) {
|
|
|
6644
6673
|
// DIAGNOSTIC-ONLY: bracket stored-model resolution (getStoredModelForEngine
|
|
6645
6674
|
// settings read).
|
|
6646
6675
|
workerStep("model_start");
|
|
6647
|
-
const
|
|
6676
|
+
const requestModelIsExplicit = isConcreteModelSelection(requestModel);
|
|
6677
|
+
const configuredModelIsExplicit = isConcreteModelSelection(configuredModel);
|
|
6678
|
+
const storedModel = !requestModelIsExplicit && !configuredModelIsExplicit
|
|
6648
6679
|
? await getStoredModelForEngine(engine, { appId: options.appId })
|
|
6649
6680
|
: undefined;
|
|
6650
|
-
const
|
|
6681
|
+
const modelSelection = resolveAgentModelSelection({
|
|
6682
|
+
requestModel,
|
|
6683
|
+
configuredModel,
|
|
6684
|
+
storedModel,
|
|
6685
|
+
defaultModel: engine.defaultModel,
|
|
6686
|
+
});
|
|
6687
|
+
const modelCandidate = modelSelection.model;
|
|
6651
6688
|
// DIAGNOSTIC-ONLY: stored-model resolution finished.
|
|
6652
6689
|
workerStep("model_done");
|
|
6653
6690
|
const model = normalizeModelForEngine(engine, modelCandidate);
|
|
6654
6691
|
let effectiveModel = model;
|
|
6655
|
-
let modelSelectionSource =
|
|
6656
|
-
? "request"
|
|
6657
|
-
: configuredModel != null
|
|
6658
|
-
? "configured"
|
|
6659
|
-
: storedModel != null
|
|
6660
|
-
? "stored"
|
|
6661
|
-
: "default";
|
|
6692
|
+
let modelSelectionSource = modelSelection.source;
|
|
6662
6693
|
let experimentAssignments = [];
|
|
6663
6694
|
// Database-backed experiments remain available to app operators.
|
|
6664
6695
|
try {
|
|
@@ -7171,7 +7202,7 @@ export function createProductionAgentHandler(options) {
|
|
|
7171
7202
|
};
|
|
7172
7203
|
const approvalHooks = {
|
|
7173
7204
|
onApprovalRequired: async (binding) => {
|
|
7174
|
-
|
|
7205
|
+
return createAgentToolApproval(approvalStoreBinding(binding));
|
|
7175
7206
|
},
|
|
7176
7207
|
consumeApproval: async (binding) => {
|
|
7177
7208
|
if (!ownerEmail)
|
|
@@ -7211,12 +7242,12 @@ export function createProductionAgentHandler(options) {
|
|
|
7211
7242
|
const { threadDataToEngineMessages } = await import("./thread-data-builder.js");
|
|
7212
7243
|
const priorThreadData = (await getThread(effectiveThreadId))
|
|
7213
7244
|
?.threadData;
|
|
7214
|
-
//
|
|
7215
|
-
//
|
|
7216
|
-
//
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7245
|
+
// Replay what earlier chunks DID, not just what they said: the default
|
|
7246
|
+
// text-only rebuild drops every tool call and result, so the next chunk
|
|
7247
|
+
// re-runs work already committed and cannot see its output.
|
|
7248
|
+
const resumed = threadDataToEngineMessages(priorThreadData, {
|
|
7249
|
+
includeToolCalls: true,
|
|
7250
|
+
});
|
|
7220
7251
|
if (resumed.length > 0) {
|
|
7221
7252
|
const actionPreparationTool = typeof backgroundRunMarker?.actionPreparationTool === "string" &&
|
|
7222
7253
|
backgroundRunMarker.actionPreparationTool.trim()
|
|
@@ -144,7 +144,10 @@ export function buildAssistantMessage(events, runId, options = {}) {
|
|
|
144
144
|
const matchingIndex = findApprovalToolCallIndex(content, event.tool ?? "unknown", event.toolCallId);
|
|
145
145
|
const part = content[matchingIndex];
|
|
146
146
|
if (part?.type === "tool-call") {
|
|
147
|
-
part.approval = {
|
|
147
|
+
part.approval = {
|
|
148
|
+
approvalKey: event.approvalKey,
|
|
149
|
+
...(event.askId ? { askId: event.askId } : {}),
|
|
150
|
+
};
|
|
148
151
|
}
|
|
149
152
|
continue;
|
|
150
153
|
}
|
|
@@ -744,14 +747,13 @@ function assistantReplayContent(message, text) {
|
|
|
744
747
|
? part.args
|
|
745
748
|
: {};
|
|
746
749
|
assistant.push({ type: "tool-call", id, name, input });
|
|
747
|
-
|
|
748
|
-
continue;
|
|
750
|
+
const result = part.result === undefined ? INTERRUPTED_TOOL_RESULT : part.result;
|
|
749
751
|
results.push({
|
|
750
752
|
type: "tool-result",
|
|
751
753
|
toolCallId: id,
|
|
752
754
|
toolName: name,
|
|
753
755
|
toolInput: stringifyToolUseInputForGateway(input),
|
|
754
|
-
content: replayedToolResultContent(
|
|
756
|
+
content: replayedToolResultContent(result),
|
|
755
757
|
...(part.isError === true ? { isError: true } : {}),
|
|
756
758
|
});
|
|
757
759
|
}
|
|
@@ -21,7 +21,16 @@ export declare function resolveAgentToolApprovalTurnId(binding: {
|
|
|
21
21
|
requestedTurnId?: string | null;
|
|
22
22
|
approvalKeys: readonly string[];
|
|
23
23
|
}): Promise<string | null>;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Creates the durable pending-approval row and returns its id as the "ask"
|
|
26
|
+
* identity for this specific gate hit. Every call is a fresh row (a fresh
|
|
27
|
+
* id), including a second gate hit for the same `approvalKey` after a prior
|
|
28
|
+
* grant failed to be consumed — so the id doubles as the signal the client
|
|
29
|
+
* needs to tell "this approval_required is the one I already approved,
|
|
30
|
+
* re-rendered" apart from "this is a NEW ask for the same tool call". See
|
|
31
|
+
* `ApprovalAffordance` in client/chat/tool-call-display.tsx.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createAgentToolApproval(binding: AgentToolApprovalBinding): Promise<string>;
|
|
25
34
|
/**
|
|
26
35
|
* Atomically consume the server-created approval for one logical tool call.
|
|
27
36
|
* The model's call id is transport metadata and can change when Dispatch
|