@agent-native/core 0.70.0 → 0.70.2
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/README.md +15 -15
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +67 -0
- package/corpus/core/docs/design/durable-agent-runs.md +217 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +89 -0
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
- package/corpus/core/src/cli/skills.ts +2 -2
- package/corpus/core/src/cli/sync-builder-starter-manifest.ts +275 -0
- package/corpus/core/src/client/AgentPanel.tsx +5 -3
- package/corpus/core/src/client/AssistantChat.tsx +8 -0
- package/corpus/core/src/client/client-surface.ts +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
- package/corpus/core/src/client/feedback-context.ts +4 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/sse-event-processor.ts +7 -1
- package/corpus/core/src/mcp/build-server.ts +57 -1
- package/corpus/core/src/mcp/embed-app.ts +86 -2
- package/corpus/core/src/mcp/oauth-route.ts +109 -13
- package/corpus/core/src/server/core-routes-plugin.ts +78 -0
- package/corpus/core/src/server/onboarding-html.ts +35 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
- package/corpus/templates/analytics/AGENTS.md +8 -1
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +97 -2
- package/corpus/templates/analytics/actions/update-dashboard.ts +36 -7
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -4
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
- package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
- package/corpus/templates/assets/actions/generate-image.ts +24 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +17 -1
- package/corpus/templates/assets/app/routes/_index.tsx +94 -14
- package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
- package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
- package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
- package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
- package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
- package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
- package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
- package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
- package/corpus/templates/clips/desktop/src/styles.css +70 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +73 -0
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +34 -5
- package/corpus/templates/content/AGENTS.md +20 -3
- package/corpus/templates/content/actions/_database-utils.ts +15 -0
- package/corpus/templates/content/actions/_property-utils.ts +371 -4
- package/corpus/templates/content/actions/configure-document-property.ts +35 -1
- package/corpus/templates/content/actions/create-content-database.ts +5 -1
- package/corpus/templates/content/actions/delete-document-property.ts +52 -2
- package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
- package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
- package/corpus/templates/content/actions/set-document-property.ts +39 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
- package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
- package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
- package/corpus/templates/content/server/db/schema.ts +30 -0
- package/corpus/templates/content/server/plugins/db.ts +87 -0
- package/corpus/templates/content/shared/api.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +109 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
- package/corpus/templates/forms/actions/export-responses.ts +6 -0
- package/corpus/templates/forms/actions/list-responses.ts +2 -0
- package/corpus/templates/forms/actions/response-insights.ts +2 -0
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
- package/corpus/templates/forms/server/db/schema.ts +7 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
- package/corpus/templates/forms/server/lib/integrations.ts +76 -2
- package/corpus/templates/forms/server/plugins/db.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +12 -0
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
- package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
- package/dist/action.js +87 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +13 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +44 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/cli/skills.js +2 -2
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.d.ts +32 -0
- package/dist/cli/sync-builder-starter-manifest.d.ts.map +1 -0
- package/dist/cli/sync-builder-starter-manifest.js +179 -0
- package/dist/cli/sync-builder-starter-manifest.js.map +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/client-surface.d.ts +17 -0
- package/dist/client/client-surface.d.ts.map +1 -0
- package/dist/client/client-surface.js +24 -0
- package/dist/client/client-surface.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +26 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/feedback-context.d.ts +3 -0
- package/dist/client/feedback-context.d.ts.map +1 -1
- package/dist/client/feedback-context.js +2 -0
- package/dist/client/feedback-context.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +21 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +43 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +86 -2
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +95 -12
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +63 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +35 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/docs/design/durable-agent-runs.md +217 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/templates/analytics/app/pages/About.tsx +0 -108
- package/corpus/templates/analytics/app/routes/about.tsx +0 -9
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { _postProcessStandalone } from "./create.js";
|
|
6
|
+
|
|
7
|
+
export const STARTER_APP_NAME = "builder-agent-native-starter";
|
|
8
|
+
export const CHAT_TEMPLATE = "chat";
|
|
9
|
+
|
|
10
|
+
type PackageJson = Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
export function findAgentNativeRoot(startDir = process.cwd()): string {
|
|
13
|
+
let dir = path.resolve(startDir);
|
|
14
|
+
for (let i = 0; i < 12; i++) {
|
|
15
|
+
const chatPackageJson = path.join(
|
|
16
|
+
dir,
|
|
17
|
+
"templates",
|
|
18
|
+
CHAT_TEMPLATE,
|
|
19
|
+
"package.json",
|
|
20
|
+
);
|
|
21
|
+
if (fs.existsSync(chatPackageJson)) {
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(dir);
|
|
25
|
+
if (parent === dir) break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Could not find agent-native repo root (expected templates/chat/package.json).",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function generateStandaloneChatManifest(repoRoot?: string): {
|
|
34
|
+
packageJson: PackageJson;
|
|
35
|
+
pnpmWorkspaceYaml: string | null;
|
|
36
|
+
} {
|
|
37
|
+
const root = repoRoot ?? findAgentNativeRoot();
|
|
38
|
+
const chatTemplateDir = path.join(root, "templates", CHAT_TEMPLATE);
|
|
39
|
+
const tempDir = fs.mkdtempSync(
|
|
40
|
+
path.join(os.tmpdir(), "an-builder-starter-sync-"),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
fs.cpSync(chatTemplateDir, tempDir, { recursive: true });
|
|
45
|
+
_postProcessStandalone(STARTER_APP_NAME, tempDir, CHAT_TEMPLATE);
|
|
46
|
+
|
|
47
|
+
const packageJson = JSON.parse(
|
|
48
|
+
fs.readFileSync(path.join(tempDir, "package.json"), "utf-8"),
|
|
49
|
+
) as PackageJson;
|
|
50
|
+
|
|
51
|
+
const workspacePath = path.join(tempDir, "pnpm-workspace.yaml");
|
|
52
|
+
const pnpmWorkspaceYaml = fs.existsSync(workspacePath)
|
|
53
|
+
? fs.readFileSync(workspacePath, "utf-8")
|
|
54
|
+
: null;
|
|
55
|
+
|
|
56
|
+
return { packageJson, pnpmWorkspaceYaml };
|
|
57
|
+
} finally {
|
|
58
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function mergeStarterManifest(
|
|
63
|
+
starterPackageJson: PackageJson,
|
|
64
|
+
canonicalPackageJson: PackageJson,
|
|
65
|
+
): PackageJson {
|
|
66
|
+
const merged = structuredClone(canonicalPackageJson) as PackageJson;
|
|
67
|
+
|
|
68
|
+
merged.name = starterPackageJson.name ?? STARTER_APP_NAME;
|
|
69
|
+
if (typeof starterPackageJson.displayName === "string") {
|
|
70
|
+
merged.displayName = starterPackageJson.displayName;
|
|
71
|
+
}
|
|
72
|
+
if (typeof starterPackageJson.description === "string") {
|
|
73
|
+
merged.description = starterPackageJson.description;
|
|
74
|
+
}
|
|
75
|
+
if (starterPackageJson.private !== undefined) {
|
|
76
|
+
merged.private = starterPackageJson.private;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const starterDeps =
|
|
80
|
+
(starterPackageJson.dependencies as Record<string, string> | undefined) ??
|
|
81
|
+
{};
|
|
82
|
+
const canonicalDeps =
|
|
83
|
+
(canonicalPackageJson.dependencies as Record<string, string> | undefined) ??
|
|
84
|
+
{};
|
|
85
|
+
merged.dependencies = {
|
|
86
|
+
...canonicalDeps,
|
|
87
|
+
...(starterDeps["@agent-native/core"]
|
|
88
|
+
? { "@agent-native/core": starterDeps["@agent-native/core"] }
|
|
89
|
+
: {}),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return merged;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function workspaceFileSyncChanged(
|
|
96
|
+
existingWorkspace: string | null,
|
|
97
|
+
canonicalWorkspace: string | null,
|
|
98
|
+
): boolean {
|
|
99
|
+
if (canonicalWorkspace === null) {
|
|
100
|
+
return existingWorkspace !== null;
|
|
101
|
+
}
|
|
102
|
+
return existingWorkspace !== canonicalWorkspace;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function applyWorkspaceFileSync(
|
|
106
|
+
targetPath: string,
|
|
107
|
+
canonicalWorkspace: string | null,
|
|
108
|
+
): void {
|
|
109
|
+
if (canonicalWorkspace === null) {
|
|
110
|
+
if (fs.existsSync(targetPath)) {
|
|
111
|
+
fs.unlinkSync(targetPath);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
fs.writeFileSync(targetPath, canonicalWorkspace);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function stableJson(value: unknown): string {
|
|
119
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type SyncStarterManifestResult = {
|
|
123
|
+
changed: boolean;
|
|
124
|
+
packageJson: PackageJson;
|
|
125
|
+
pnpmWorkspaceYaml: string | null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export function syncStarterManifestFiles(options: {
|
|
129
|
+
starterPackageJsonPath: string;
|
|
130
|
+
starterPnpmWorkspacePath?: string;
|
|
131
|
+
repoRoot?: string;
|
|
132
|
+
write?: boolean;
|
|
133
|
+
}): SyncStarterManifestResult {
|
|
134
|
+
const { packageJson: canonical, pnpmWorkspaceYaml } =
|
|
135
|
+
generateStandaloneChatManifest(options.repoRoot);
|
|
136
|
+
|
|
137
|
+
const starterPackageJson = JSON.parse(
|
|
138
|
+
fs.readFileSync(options.starterPackageJsonPath, "utf-8"),
|
|
139
|
+
) as PackageJson;
|
|
140
|
+
const mergedPackageJson = mergeStarterManifest(starterPackageJson, canonical);
|
|
141
|
+
|
|
142
|
+
let workspaceChanged = false;
|
|
143
|
+
const existingWorkspace =
|
|
144
|
+
options.starterPnpmWorkspacePath &&
|
|
145
|
+
fs.existsSync(options.starterPnpmWorkspacePath)
|
|
146
|
+
? fs.readFileSync(options.starterPnpmWorkspacePath, "utf-8")
|
|
147
|
+
: null;
|
|
148
|
+
|
|
149
|
+
workspaceChanged = workspaceFileSyncChanged(
|
|
150
|
+
existingWorkspace,
|
|
151
|
+
pnpmWorkspaceYaml,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const packageChanged =
|
|
155
|
+
stableJson(starterPackageJson) !== stableJson(mergedPackageJson);
|
|
156
|
+
const changed = packageChanged || workspaceChanged;
|
|
157
|
+
|
|
158
|
+
if (options.write && changed) {
|
|
159
|
+
if (packageChanged) {
|
|
160
|
+
fs.writeFileSync(
|
|
161
|
+
options.starterPackageJsonPath,
|
|
162
|
+
stableJson(mergedPackageJson),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (workspaceChanged && options.starterPnpmWorkspacePath) {
|
|
166
|
+
applyWorkspaceFileSync(
|
|
167
|
+
options.starterPnpmWorkspacePath,
|
|
168
|
+
pnpmWorkspaceYaml,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
changed,
|
|
175
|
+
packageJson: mergedPackageJson,
|
|
176
|
+
pnpmWorkspaceYaml,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function parseSyncStarterManifestArgs(argv: string[]): {
|
|
181
|
+
command: "merge" | "generate";
|
|
182
|
+
starterPackageJsonPath?: string;
|
|
183
|
+
starterPnpmWorkspacePath?: string;
|
|
184
|
+
write: boolean;
|
|
185
|
+
repoRoot?: string;
|
|
186
|
+
} {
|
|
187
|
+
const [commandRaw, ...rest] = argv;
|
|
188
|
+
const command = commandRaw === "generate" ? "generate" : "merge";
|
|
189
|
+
let starterPackageJsonPath: string | undefined;
|
|
190
|
+
let starterPnpmWorkspacePath: string | undefined;
|
|
191
|
+
let write = false;
|
|
192
|
+
let repoRoot: string | undefined;
|
|
193
|
+
|
|
194
|
+
for (let i = 0; i < rest.length; i++) {
|
|
195
|
+
const arg = rest[i];
|
|
196
|
+
if (arg === "--write") {
|
|
197
|
+
write = true;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (arg === "--starter-package-json") {
|
|
201
|
+
starterPackageJsonPath = rest[++i];
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (arg === "--starter-pnpm-workspace") {
|
|
205
|
+
starterPnpmWorkspacePath = rest[++i];
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (arg === "--repo-root") {
|
|
209
|
+
repoRoot = rest[++i];
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (command === "merge" && !starterPackageJsonPath) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
"merge requires --starter-package-json <path> [--starter-pnpm-workspace <path>] [--write] [--repo-root <path>]",
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
command,
|
|
223
|
+
starterPackageJsonPath,
|
|
224
|
+
starterPnpmWorkspacePath,
|
|
225
|
+
write,
|
|
226
|
+
repoRoot,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function runSyncStarterManifestCli(argv: string[]): number {
|
|
231
|
+
const args = parseSyncStarterManifestArgs(argv);
|
|
232
|
+
|
|
233
|
+
if (args.command === "generate") {
|
|
234
|
+
const { packageJson, pnpmWorkspaceYaml } = generateStandaloneChatManifest(
|
|
235
|
+
args.repoRoot,
|
|
236
|
+
);
|
|
237
|
+
process.stdout.write(stableJson(packageJson));
|
|
238
|
+
if (pnpmWorkspaceYaml) {
|
|
239
|
+
process.stdout.write("\n--- pnpm-workspace.yaml ---\n");
|
|
240
|
+
process.stdout.write(pnpmWorkspaceYaml);
|
|
241
|
+
}
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const result = syncStarterManifestFiles({
|
|
246
|
+
starterPackageJsonPath: args.starterPackageJsonPath!,
|
|
247
|
+
starterPnpmWorkspacePath: args.starterPnpmWorkspacePath,
|
|
248
|
+
repoRoot: args.repoRoot,
|
|
249
|
+
write: args.write,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (result.changed) {
|
|
253
|
+
console.log(
|
|
254
|
+
args.write
|
|
255
|
+
? "Updated builder-agent-native-starter manifest from templates/chat."
|
|
256
|
+
: "builder-agent-native-starter manifest is out of date with templates/chat.",
|
|
257
|
+
);
|
|
258
|
+
return args.write ? 0 : 1;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.log(
|
|
262
|
+
"builder-agent-native-starter manifest already matches templates/chat.",
|
|
263
|
+
);
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const isDirectRun =
|
|
268
|
+
process.argv[1] &&
|
|
269
|
+
path.resolve(process.argv[1]) ===
|
|
270
|
+
path.resolve(fileURLToPath(import.meta.url));
|
|
271
|
+
|
|
272
|
+
if (isDirectRun) {
|
|
273
|
+
const exitCode = runSyncStarterManifestCli(process.argv.slice(2));
|
|
274
|
+
process.exit(exitCode);
|
|
275
|
+
}
|
|
@@ -147,9 +147,11 @@ const SetupButton = lazy(() =>
|
|
|
147
147
|
})),
|
|
148
148
|
);
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
|
|
150
|
+
// The setup/onboarding checklist that used to appear above chat is disabled
|
|
151
|
+
// for every app — setup (AI engine, image/video gen, asset storage, email,
|
|
152
|
+
// GitHub, etc.) is surfaced in better places (the settings panel and the
|
|
153
|
+
// per-feature setup affordances). Keep this off; do not re-enable globally.
|
|
154
|
+
const SHOW_ONBOARDING = false;
|
|
153
155
|
|
|
154
156
|
const CLI_STORAGE_KEY = "agent-native-cli-command";
|
|
155
157
|
const CLI_DEFAULT = "claude";
|
|
@@ -86,6 +86,7 @@ import { agentNativePath } from "./api-path.js";
|
|
|
86
86
|
import {
|
|
87
87
|
TiptapComposer,
|
|
88
88
|
type ComposerSubmitIntent,
|
|
89
|
+
type ComposerImageModelMenu,
|
|
89
90
|
type TiptapComposerHandle,
|
|
90
91
|
} from "./composer/TiptapComposer.js";
|
|
91
92
|
import { AgentComposerFrame } from "./composer/AgentComposerFrame.js";
|
|
@@ -712,6 +713,11 @@ export interface AssistantChatProps {
|
|
|
712
713
|
onModelChange?: (model: string, engine: string) => void;
|
|
713
714
|
/** Callback when user picks a reasoning effort from the picker */
|
|
714
715
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
716
|
+
/**
|
|
717
|
+
* Optional secondary model menu (e.g. an image-generation model) shown inside
|
|
718
|
+
* the composer's model picker. Opt-in; chat-only apps omit it.
|
|
719
|
+
*/
|
|
720
|
+
imageModelMenu?: ComposerImageModelMenu;
|
|
715
721
|
/** Callback when user clicks "Fork Chat" in the message actions menu */
|
|
716
722
|
onForkChat?: () => void | boolean | Promise<void | boolean>;
|
|
717
723
|
/** Override Builder/provider connect routing for embedded hosts. */
|
|
@@ -977,6 +983,7 @@ const AssistantChatInner = forwardRef<
|
|
|
977
983
|
availableModels,
|
|
978
984
|
onModelChange,
|
|
979
985
|
onEffortChange,
|
|
986
|
+
imageModelMenu,
|
|
980
987
|
onForkChat,
|
|
981
988
|
onConnectProvider,
|
|
982
989
|
plusMenuMode = "full",
|
|
@@ -3392,6 +3399,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3392
3399
|
availableModels={availableModels}
|
|
3393
3400
|
onModelChange={onModelChange}
|
|
3394
3401
|
onEffortChange={onEffortChange}
|
|
3402
|
+
imageModelMenu={imageModelMenu}
|
|
3395
3403
|
onConnectProvider={onConnectProvider}
|
|
3396
3404
|
toolbarSlot={composerToolbarSlot}
|
|
3397
3405
|
contextItems={composerContextItems}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime shell the web UI is rendering inside. Used as pass-through
|
|
3
|
+
* feedback metadata so form owners can tell whether a submission came from the
|
|
4
|
+
* Agent Native desktop app (Electron), a Tauri desktop shell (e.g. Clips), or a
|
|
5
|
+
* plain browser — without surfacing it as a visible form field.
|
|
6
|
+
*/
|
|
7
|
+
export type ClientSurface = "web" | "electron" | "tauri";
|
|
8
|
+
|
|
9
|
+
interface SurfaceGlobals {
|
|
10
|
+
// Tauri v2 always injects this into the webview; `__TAURI__` only exists when
|
|
11
|
+
// the app opts into `withGlobalTauri`, so check both.
|
|
12
|
+
__TAURI_INTERNALS__?: unknown;
|
|
13
|
+
__TAURI__?: unknown;
|
|
14
|
+
// Exposed by the Agent Native desktop (Electron) preload bridges.
|
|
15
|
+
agentNativeDesktop?: unknown;
|
|
16
|
+
electronAPI?: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Best-effort, side-effect-free detection of the current client surface.
|
|
21
|
+
* Returns "web" during SSR and in any plain browser.
|
|
22
|
+
*
|
|
23
|
+
* Electron detection keys off the `AgentNativeDesktop` User-Agent token that the
|
|
24
|
+
* desktop shell appends app-wide (see `app.userAgentFallback`), plus the preload
|
|
25
|
+
* globals, so it stays specific to our app rather than every Electron webview.
|
|
26
|
+
*/
|
|
27
|
+
export function getClientSurface(): ClientSurface {
|
|
28
|
+
if (typeof window === "undefined") return "web";
|
|
29
|
+
const w = window as unknown as SurfaceGlobals;
|
|
30
|
+
if (w.__TAURI_INTERNALS__ || w.__TAURI__) return "tauri";
|
|
31
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent || "" : "";
|
|
32
|
+
if (
|
|
33
|
+
/AgentNativeDesktop/i.test(ua) ||
|
|
34
|
+
/\bElectron\b/i.test(ua) ||
|
|
35
|
+
w.agentNativeDesktop ||
|
|
36
|
+
w.electronAPI
|
|
37
|
+
) {
|
|
38
|
+
return "electron";
|
|
39
|
+
}
|
|
40
|
+
return "web";
|
|
41
|
+
}
|
|
@@ -417,6 +417,11 @@ export interface TiptapComposerProps {
|
|
|
417
417
|
* Used by the Electron desktop app to route through the native IPC handler.
|
|
418
418
|
*/
|
|
419
419
|
onConnectProvider?: () => void;
|
|
420
|
+
/**
|
|
421
|
+
* Optional secondary model menu (e.g. an image-generation model) rendered as
|
|
422
|
+
* an extra section inside the model picker. Opt-in; omit for chat-only apps.
|
|
423
|
+
*/
|
|
424
|
+
imageModelMenu?: ComposerImageModelMenu;
|
|
420
425
|
/** Stable scope for persisted drafts, usually the active thread or tab id. */
|
|
421
426
|
draftScope?: string;
|
|
422
427
|
/** Keyed context nuggets staged for the next submitted prompt. */
|
|
@@ -669,6 +674,24 @@ function latestModelsOnly(models: string[]): string[] {
|
|
|
669
674
|
});
|
|
670
675
|
}
|
|
671
676
|
|
|
677
|
+
/**
|
|
678
|
+
* Optional secondary model menu for apps that drive a separate generation model
|
|
679
|
+
* alongside the chat LLM (e.g. the Assets app's image-generation model). When
|
|
680
|
+
* provided, the model picker renders an extra collapsible section so the user
|
|
681
|
+
* can see and pick both "what reasons about my request" (the chat model) and
|
|
682
|
+
* "what produces the output" (this model). Opt-in — omit it and nothing changes.
|
|
683
|
+
*/
|
|
684
|
+
export interface ComposerImageModelMenu {
|
|
685
|
+
/** Currently-selected model id for this secondary menu. */
|
|
686
|
+
value: string;
|
|
687
|
+
/** Selectable options (stable id + human label). */
|
|
688
|
+
options: Array<{ value: string; label: string }>;
|
|
689
|
+
/** Invoked when the user picks a different option. */
|
|
690
|
+
onChange: (value: string) => void;
|
|
691
|
+
/** Section header. Defaults to "Image model". */
|
|
692
|
+
label?: string;
|
|
693
|
+
}
|
|
694
|
+
|
|
672
695
|
function ModelSelector({
|
|
673
696
|
model,
|
|
674
697
|
effort = "auto",
|
|
@@ -677,6 +700,7 @@ function ModelSelector({
|
|
|
677
700
|
onEffortChange,
|
|
678
701
|
providerConnectStatusEnabled = true,
|
|
679
702
|
onConnectProvider,
|
|
703
|
+
imageModel,
|
|
680
704
|
}: {
|
|
681
705
|
model: string;
|
|
682
706
|
effort?: ReasoningEffort;
|
|
@@ -690,6 +714,7 @@ function ModelSelector({
|
|
|
690
714
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
691
715
|
providerConnectStatusEnabled?: boolean;
|
|
692
716
|
onConnectProvider?: () => void;
|
|
717
|
+
imageModel?: ComposerImageModelMenu;
|
|
693
718
|
}) {
|
|
694
719
|
const [open, setOpen] = useState(false);
|
|
695
720
|
const autoModelGroup = engines.find((group) => group.models.includes("auto"));
|
|
@@ -744,6 +769,24 @@ function ModelSelector({
|
|
|
744
769
|
});
|
|
745
770
|
}, []);
|
|
746
771
|
|
|
772
|
+
// The reasoning effort list is collapsed by default — it's a secondary
|
|
773
|
+
// control most users don't touch, so it stays tucked behind a header that
|
|
774
|
+
// reveals the current effort at a glance. Reset to collapsed on each open.
|
|
775
|
+
const [reasoningExpanded, setReasoningExpanded] = useState(false);
|
|
776
|
+
useEffect(() => {
|
|
777
|
+
if (open) setReasoningExpanded(false);
|
|
778
|
+
}, [open]);
|
|
779
|
+
|
|
780
|
+
// The optional image-model section follows the same collapsed-by-default
|
|
781
|
+
// pattern; the current selection shows next to the header.
|
|
782
|
+
const [imageExpanded, setImageExpanded] = useState(false);
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
if (open) setImageExpanded(false);
|
|
785
|
+
}, [open]);
|
|
786
|
+
const imageModelLabel =
|
|
787
|
+
imageModel?.options.find((option) => option.value === imageModel.value)
|
|
788
|
+
?.label ?? imageModel?.value;
|
|
789
|
+
|
|
747
790
|
// When Builder.io isn't connected, surface a one-click connect path —
|
|
748
791
|
// it unlocks every model family (Claude, OpenAI, Gemini) without the
|
|
749
792
|
// user having to paste individual API keys.
|
|
@@ -835,6 +878,52 @@ function ModelSelector({
|
|
|
835
878
|
<div className="my-1 border-t border-border" />
|
|
836
879
|
</>
|
|
837
880
|
)}
|
|
881
|
+
{imageModel && imageModel.options.length > 0 && (
|
|
882
|
+
<>
|
|
883
|
+
<div className="flex items-center hover:bg-accent/30">
|
|
884
|
+
<button
|
|
885
|
+
type="button"
|
|
886
|
+
aria-expanded={imageExpanded}
|
|
887
|
+
onClick={() => setImageExpanded((prev) => !prev)}
|
|
888
|
+
className="flex flex-1 min-w-0 items-center gap-1.5 px-2 py-1.5 cursor-pointer text-left"
|
|
889
|
+
>
|
|
890
|
+
{imageExpanded ? (
|
|
891
|
+
<IconChevronDown className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
892
|
+
) : (
|
|
893
|
+
<IconChevronRight className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
894
|
+
)}
|
|
895
|
+
<span className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide shrink-0">
|
|
896
|
+
{imageModel.label ?? "Image model"}
|
|
897
|
+
</span>
|
|
898
|
+
{!imageExpanded && imageModelLabel && (
|
|
899
|
+
<span className="text-[11px] text-muted-foreground/80 truncate">
|
|
900
|
+
{imageModelLabel}
|
|
901
|
+
</span>
|
|
902
|
+
)}
|
|
903
|
+
</button>
|
|
904
|
+
</div>
|
|
905
|
+
{imageExpanded &&
|
|
906
|
+
imageModel.options.map((option) => (
|
|
907
|
+
<button
|
|
908
|
+
key={option.value}
|
|
909
|
+
type="button"
|
|
910
|
+
onClick={() => {
|
|
911
|
+
imageModel.onChange(option.value);
|
|
912
|
+
setImageExpanded(false);
|
|
913
|
+
}}
|
|
914
|
+
className="flex w-full items-center gap-3 pl-7 pr-3 py-1.5 text-left hover:bg-accent/50"
|
|
915
|
+
>
|
|
916
|
+
<span className="flex-1 min-w-0 text-[13px] text-foreground truncate">
|
|
917
|
+
{option.label}
|
|
918
|
+
</span>
|
|
919
|
+
{option.value === imageModel.value && (
|
|
920
|
+
<IconCheck className="h-3.5 w-3.5 shrink-0 text-blue-500" />
|
|
921
|
+
)}
|
|
922
|
+
</button>
|
|
923
|
+
))}
|
|
924
|
+
<div className="my-1 border-t border-border" />
|
|
925
|
+
</>
|
|
926
|
+
)}
|
|
838
927
|
{autoModelGroup && (
|
|
839
928
|
<button
|
|
840
929
|
type="button"
|
|
@@ -930,24 +1019,44 @@ function ModelSelector({
|
|
|
930
1019
|
{effortOptions.length > 0 && (
|
|
931
1020
|
<>
|
|
932
1021
|
<div className="my-1 border-t border-border" />
|
|
933
|
-
<div className="
|
|
934
|
-
Reasoning
|
|
935
|
-
</div>
|
|
936
|
-
{effortOptions.map((option) => (
|
|
1022
|
+
<div className="flex items-center hover:bg-accent/30">
|
|
937
1023
|
<button
|
|
938
|
-
key={option}
|
|
939
1024
|
type="button"
|
|
940
|
-
|
|
941
|
-
|
|
1025
|
+
aria-expanded={reasoningExpanded}
|
|
1026
|
+
onClick={() => setReasoningExpanded((prev) => !prev)}
|
|
1027
|
+
className="flex flex-1 min-w-0 items-center gap-1.5 px-2 py-1.5 cursor-pointer text-left"
|
|
942
1028
|
>
|
|
943
|
-
|
|
944
|
-
|
|
1029
|
+
{reasoningExpanded ? (
|
|
1030
|
+
<IconChevronDown className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
1031
|
+
) : (
|
|
1032
|
+
<IconChevronRight className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
1033
|
+
)}
|
|
1034
|
+
<span className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide shrink-0">
|
|
1035
|
+
Reasoning
|
|
945
1036
|
</span>
|
|
946
|
-
{
|
|
947
|
-
<
|
|
1037
|
+
{!reasoningExpanded && (
|
|
1038
|
+
<span className="text-[11px] text-muted-foreground/80 truncate">
|
|
1039
|
+
{reasoningEffortLabel(effort)}
|
|
1040
|
+
</span>
|
|
948
1041
|
)}
|
|
949
1042
|
</button>
|
|
950
|
-
|
|
1043
|
+
</div>
|
|
1044
|
+
{reasoningExpanded &&
|
|
1045
|
+
effortOptions.map((option) => (
|
|
1046
|
+
<button
|
|
1047
|
+
key={option}
|
|
1048
|
+
type="button"
|
|
1049
|
+
onClick={() => onEffortChange?.(option)}
|
|
1050
|
+
className="flex w-full items-center gap-3 pl-7 pr-3 py-1.5 text-left hover:bg-accent/50"
|
|
1051
|
+
>
|
|
1052
|
+
<span className="flex-1 min-w-0 text-[13px] text-foreground truncate">
|
|
1053
|
+
{reasoningEffortLabel(option)}
|
|
1054
|
+
</span>
|
|
1055
|
+
{option === effort && (
|
|
1056
|
+
<IconCheck className="h-3.5 w-3.5 shrink-0 text-blue-500" />
|
|
1057
|
+
)}
|
|
1058
|
+
</button>
|
|
1059
|
+
))}
|
|
951
1060
|
</>
|
|
952
1061
|
)}
|
|
953
1062
|
</PopoverContent>
|
|
@@ -994,6 +1103,7 @@ export function TiptapComposer({
|
|
|
994
1103
|
onEffortChange,
|
|
995
1104
|
providerConnectStatusEnabled,
|
|
996
1105
|
onConnectProvider,
|
|
1106
|
+
imageModelMenu,
|
|
997
1107
|
draftScope,
|
|
998
1108
|
contextItems = [],
|
|
999
1109
|
onRemoveContextItem,
|
|
@@ -2091,6 +2201,7 @@ export function TiptapComposer({
|
|
|
2091
2201
|
onEffortChange={onEffortChange}
|
|
2092
2202
|
providerConnectStatusEnabled={providerConnectStatusEnabled}
|
|
2093
2203
|
onConnectProvider={onConnectProvider}
|
|
2204
|
+
imageModel={imageModelMenu}
|
|
2094
2205
|
/>
|
|
2095
2206
|
)}
|
|
2096
2207
|
{execMode && onExecModeChange && (
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { getActiveRun } from "./active-run-state.js";
|
|
2
|
+
import { getClientSurface, type ClientSurface } from "./client-surface.js";
|
|
2
3
|
import { scrubUrl } from "./url-scrub.js";
|
|
3
4
|
|
|
4
5
|
export interface FeedbackClientContext {
|
|
5
6
|
chatSessionIds: string[];
|
|
6
7
|
activeRunId?: string;
|
|
7
8
|
pageUrl?: string;
|
|
9
|
+
/** Runtime shell the feedback was sent from: web, electron, or tauri. */
|
|
10
|
+
clientSurface?: ClientSurface;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export interface FeedbackClientContextOptions {
|
|
@@ -81,6 +84,7 @@ export function getFeedbackClientContext(
|
|
|
81
84
|
if (activeRun?.runId) context.activeRunId = activeRun.runId;
|
|
82
85
|
if (typeof window !== "undefined") {
|
|
83
86
|
context.pageUrl = scrubUrl(window.location.href);
|
|
87
|
+
context.clientSurface = getClientSurface();
|
|
84
88
|
}
|
|
85
89
|
return context;
|
|
86
90
|
}
|
|
@@ -609,6 +609,7 @@ export {
|
|
|
609
609
|
type StarfieldBackgroundProps,
|
|
610
610
|
} from "./StarfieldBackground.js";
|
|
611
611
|
export { FeedbackButton, type FeedbackButtonProps } from "./FeedbackButton.js";
|
|
612
|
+
export { getClientSurface, type ClientSurface } from "./client-surface.js";
|
|
612
613
|
export {
|
|
613
614
|
DevDatabaseLink,
|
|
614
615
|
type DevDatabaseLinkProps,
|
|
@@ -233,7 +233,13 @@ function isAutoRecoverableError(ev: SSEEvent, errMsg: string): boolean {
|
|
|
233
233
|
// same wall. This used to send the chat into a 32-continuation runaway
|
|
234
234
|
// (each turn cleared+regenerated visible content) for users hitting a
|
|
235
235
|
// misbehaving Builder route. Surface the error instead.
|
|
236
|
-
code === "builder_gateway_error"
|
|
236
|
+
code === "builder_gateway_error" ||
|
|
237
|
+
// The hosted run exhausted its in-invocation continuation budget without
|
|
238
|
+
// finishing (run-loop-with-resume.ts). It's flagged `recoverable: true` so
|
|
239
|
+
// the recovery banner reads "stopped before finishing", but it must NOT
|
|
240
|
+
// auto-continue: another POST would hit the same ~40s wall and churn. The
|
|
241
|
+
// user retries deliberately (ideally as a single bulk action).
|
|
242
|
+
code === "run_budget_exhausted"
|
|
237
243
|
) {
|
|
238
244
|
return false;
|
|
239
245
|
}
|