@agent-native/core 0.147.0 → 0.148.1
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/frontend-design/SKILL.md +115 -68
- package/corpus/templates/analytics/DEVELOPING.md +50 -3
- package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
- package/corpus/templates/analytics/app/i18n/index.ts +14 -29
- package/corpus/templates/analytics/app/i18n-data.ts +10 -10
- package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
- package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +197 -18
- package/corpus/templates/analytics/server/db/schema.ts +23 -0
- package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
- package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
- package/corpus/templates/analytics/server/plugins/db.ts +42 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/assets/app/i18n/index.ts +14 -29
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/brain/app/i18n/index.ts +14 -29
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/chat/app/i18n/index.ts +14 -29
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
- package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
- package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
- package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
- package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
- package/corpus/templates/clips/desktop/src/app.tsx +1 -1
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/content/app/i18n/index.ts +14 -29
- package/corpus/templates/crm/app/i18n/index.ts +14 -29
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/design/actions/import-code.ts +2 -43
- package/corpus/templates/design/app/i18n/index.ts +14 -29
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/dispatch/app/i18n-data.ts +1 -1
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/factory/AGENTS.md +40 -49
- package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
- package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
- package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
- package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
- package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
- package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
- package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
- package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
- package/corpus/templates/factory/app/routes/factory.tsx +95 -42
- package/corpus/templates/factory/server/db/schema.ts +37 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
- package/corpus/templates/factory/server/triage/audit.ts +79 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/forms/app/i18n/index.ts +14 -29
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/macros/app/i18n/index.ts +14 -29
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/mail/app/i18n/index.ts +14 -29
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
- package/corpus/templates/plan/app/i18n/index.ts +14 -29
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/slides/actions/import-code.ts +2 -43
- package/corpus/templates/slides/app/i18n/index.ts +14 -29
- package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
- package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
- package/corpus/templates/tasks/app/i18n/index.ts +14 -29
- package/dist/agent/engine/ai-sdk-engine.js +2 -1
- package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
- package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
- package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
- package/dist/agent/engine/provider-endpoint-validation.js +26 -0
- package/dist/agent/engine/provider-env-vars.d.ts +1 -1
- package/dist/agent/engine/provider-env-vars.js +1 -1
- package/dist/agent/engine/registry.d.ts +8 -6
- package/dist/agent/engine/registry.js +37 -24
- package/dist/agent/tool-search.d.ts +1 -0
- package/dist/agent/tool-search.js +17 -1
- package/dist/automations/service.js +4 -10
- package/dist/brand-kit/brand-signals.d.ts +13 -0
- package/dist/brand-kit/brand-signals.js +15 -0
- package/dist/brand-kit/index.d.ts +2 -2
- package/dist/brand-kit/index.js +1 -1
- package/dist/cli/plan-blocks.d.ts +3 -18
- package/dist/cli/plan-blocks.js +2 -95
- package/dist/cli/plan-publish-store.d.ts +2 -61
- package/dist/cli/plan-publish-store.js +2 -127
- package/dist/cli/skills-content/connection.d.ts +1 -1
- package/dist/cli/skills-content/connection.js +1 -52
- package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-recap-skill.js +1 -553
- package/dist/client/agent-engine-key.d.ts +14 -4
- package/dist/client/agent-engine-key.js +51 -5
- package/dist/client/agent-provider-catalog.d.ts +20 -0
- package/dist/client/agent-provider-catalog.js +100 -0
- package/dist/client/blocks/index.d.ts +1 -0
- package/dist/client/blocks/index.js +1 -0
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/sanitize-html.js +66 -2
- package/dist/client/blocks/library/wireframe-kit.js +69 -0
- package/dist/client/chat/run-recovery.js +4 -48
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +4 -4
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/chat-first.js +4 -3
- package/dist/client/i18n.d.ts +14 -0
- package/dist/client/i18n.js +18 -0
- package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
- package/dist/client/settings/ProviderSetupForm.js +145 -0
- package/dist/client/settings/SettingsPanel.js +52 -49
- package/dist/client/settings/index.d.ts +2 -0
- package/dist/client/settings/index.js +2 -0
- package/dist/coding-tools/run-code.d.ts +5 -0
- package/dist/coding-tools/run-code.js +196 -36
- package/dist/coding-tools/tool-orchestration.d.ts +10 -0
- package/dist/coding-tools/tool-orchestration.js +107 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/jobs/frontmatter.d.ts +9 -0
- package/dist/jobs/frontmatter.js +16 -0
- package/dist/jobs/scheduler.js +8 -8
- package/dist/jobs/tools.d.ts +1 -1
- package/dist/jobs/tools.js +2 -7
- package/dist/localization/default-messages.d.ts +20 -0
- package/dist/localization/default-messages.js +25 -5
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/onboarding/app-profile.js +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
- package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
- package/dist/server/agent-chat/run-code-tools.js +3 -1
- package/dist/server/agent-chat-plugin.js +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.js +43 -12
- package/dist/server/agents-bundle.js +2 -1
- package/dist/server/auth.js +21 -5
- package/dist/server/credential-provider.js +1 -0
- package/dist/server/design-token-utils.d.ts +18 -0
- package/dist/server/design-token-utils.js +31 -0
- package/dist/server/onboarding-html.js +22 -2
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/styles/blocks.css +132 -0
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/chat/app/i18n/index.ts +14 -29
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/default/app/i18n/index.ts +14 -29
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/triggers/dispatcher.js +1 -8
- package/docs/content/actions.mdx +4 -1
- package/docs/content/agent-surfaces.mdx +8 -6
- package/docs/content/automations.mdx +1 -1
- package/docs/content/database.mdx +6 -1
- package/docs/content/docs-components.mdx +22 -14
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/environment-variables.mdx +39 -38
- package/docs/content/getting-started-actions.mdx +5 -4
- package/docs/content/getting-started-pages.mdx +7 -6
- package/docs/content/getting-started.mdx +4 -3
- package/docs/content/notifications.mdx +6 -6
- package/docs/content/progress.mdx +5 -5
- package/docs/content/routing.mdx +3 -1
- package/docs/content/server-database.mdx +13 -4
- package/docs/content/server-overview.mdx +9 -9
- package/docs/content/server-plugins.mdx +10 -4
- package/docs/content/server-routes.mdx +7 -2
- package/docs/content/tracking.mdx +4 -4
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/chat/app/i18n/index.ts +14 -29
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/default/app/i18n/index.ts +14 -29
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
name: frontend-design
|
|
3
3
|
description: >-
|
|
4
4
|
Sets the visual direction for a new or redesigned surface, with production
|
|
5
|
-
quality that avoids generic AI aesthetics. Use when building
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
quality that avoids generic AI aesthetics. Use when building, redesigning, or
|
|
6
|
+
cleaning up any user-facing UI, including screenshot-driven feedback, copy or
|
|
7
|
+
density reduction, settings, control placement, or a design pass ("make this
|
|
8
|
+
look good"). Do not load it only for purely mechanical wiring or formatting.
|
|
9
9
|
scope: dev
|
|
10
10
|
license: Complete terms in LICENSE.txt
|
|
11
11
|
source: https://github.com/anthropics/skills/blob/main/skills/frontend-design/SKILL.md
|
|
12
12
|
local-changes: >-
|
|
13
|
-
Description
|
|
14
|
-
|
|
13
|
+
Description deliberately widened on 2026-08-09 because the previous narrow
|
|
14
|
+
trigger excluded routine UI edits, including screenshot-driven cleanup, copy
|
|
15
|
+
and density changes, settings, and control placement. An upstream sync must
|
|
16
|
+
not re-narrow it. Verification remains deliberately rescoped; an upstream
|
|
17
|
+
sync must not restore screenshot-everything or broad browser-automation steps.
|
|
15
18
|
metadata:
|
|
16
19
|
internal: true
|
|
17
20
|
---
|
|
@@ -26,10 +29,12 @@ The user may ask for a component, page, full app, dashboard, marketing surface,
|
|
|
26
29
|
|
|
27
30
|
Before coding, decide:
|
|
28
31
|
|
|
29
|
-
- **Purpose**: What workflow does this surface make easier? What
|
|
32
|
+
- **Purpose**: What workflow does this surface make easier? What does the user
|
|
33
|
+
need to understand, decide, or do next?
|
|
30
34
|
- **Audience**: Who will use it repeatedly, and what should feel fast, calm, playful, premium, editorial, technical, or utilitarian?
|
|
31
35
|
- **Tone**: Choose a concrete aesthetic direction: refined minimal, dense operations console, editorial, playful, industrial, warm handmade, high-contrast data tool, etc.
|
|
32
|
-
- **Information hierarchy**: What must be visible
|
|
36
|
+
- **Information hierarchy**: What must be visible to orient and act, and what
|
|
37
|
+
can wait until context or intent makes it relevant?
|
|
33
38
|
- **Differentiation**: What makes this feel designed for this exact domain?
|
|
34
39
|
|
|
35
40
|
Then implement working code that is cohesive, accessible, responsive, and polished in small details: typography, spacing, copy, motion, empty states, loading states, focus states, and error states.
|
|
@@ -50,46 +55,80 @@ before selecting its accent family. Shared behavior and semantic token names
|
|
|
50
55
|
should stay consistent; palette, density, composition, type contrast, and
|
|
51
56
|
shape language should not be identical by default.
|
|
52
57
|
|
|
53
|
-
## Minimalism And Progressive Disclosure
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- **
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- **
|
|
92
|
-
|
|
58
|
+
## High-Signal Minimalism And Progressive Disclosure
|
|
59
|
+
|
|
60
|
+
Minimalism means fewer competing kinds of UI, not less useful information. Keep
|
|
61
|
+
surfaces dense and scannable: reduce redundant prose, decorative containers,
|
|
62
|
+
borders, badges, accent colors, and persistent controls before removing content
|
|
63
|
+
that helps the user orient, compare, decide, or recover. The reference class is
|
|
64
|
+
calm productivity software such as ChatGPT/Codex, Linear, and Vercel, not empty
|
|
65
|
+
whitespace.
|
|
66
|
+
|
|
67
|
+
### Product UX Philosophy
|
|
68
|
+
|
|
69
|
+
Apply these principles as binding decision rules:
|
|
70
|
+
|
|
71
|
+
- **Purpose over presence**: Every visible element must support orientation,
|
|
72
|
+
state comprehension, a decision, an action, or recovery. Before adding one,
|
|
73
|
+
name which; if it supports none or duplicates another element, remove or
|
|
74
|
+
defer it.
|
|
75
|
+
- **Clarity over cleverness**: Use plain language, familiar patterns, explicit
|
|
76
|
+
hierarchy, and visible state. Do not make decoration or invented terminology
|
|
77
|
+
carry meaning. Preserve the labels, hit targets, focus states, and recovery
|
|
78
|
+
paths required for accessible use.
|
|
79
|
+
- **Simple first, powerful on demand**: Keep the current task and state in the
|
|
80
|
+
default presentation. Put advanced, rare, diagnostic, and secondary work
|
|
81
|
+
behind contextual disclosure, menus, or Settings. Do not turn a discoverability
|
|
82
|
+
concern into permanent chrome, and do not hide information needed for the
|
|
83
|
+
current decision.
|
|
84
|
+
- **Hierarchy over chrome**: Use typography, spacing, alignment, and restrained
|
|
85
|
+
emphasis to organize information. Treat prose, controls, borders, cards,
|
|
86
|
+
badges, and accent colors as a competition budget: reduce competing kinds of
|
|
87
|
+
elements before reducing useful information. When a surface has one clear
|
|
88
|
+
next decision, give it dominant emphasis and move secondary actions out of
|
|
89
|
+
the default path. Do not wrap every section in a card or add borders for
|
|
90
|
+
grouping alone; use spacing, alignment, typography, or dividers first. A
|
|
91
|
+
container must communicate grouping, state, or interaction.
|
|
92
|
+
- **Coherence over novelty**: Reuse the product's primitives, interaction
|
|
93
|
+
patterns, and language. When given a screenshot or reference product, match
|
|
94
|
+
its underlying hierarchy, density, and behavior; do not copy decoration or
|
|
95
|
+
introduce a second visual language without a product reason.
|
|
96
|
+
- **Quiet continuity**: Feedback must be immediate, contextual, and honest.
|
|
97
|
+
Preserve the user's place and layout, make loading, empty, and error states
|
|
98
|
+
understandable, and use motion only to explain a change.
|
|
99
|
+
- **Respect repeated use**: Optimize for the hundredth use, not the first
|
|
100
|
+
impression. If a control repeats an explanation, occupies persistent space
|
|
101
|
+
for an occasional task, or turns routine work into ceremony, remove it or
|
|
102
|
+
move it to the contextual surface that owns it.
|
|
103
|
+
|
|
104
|
+
### Progressive Disclosure And Information Timing
|
|
105
|
+
|
|
106
|
+
Expose information and controls according to relevance, frequency, and
|
|
107
|
+
consequence. Before coding, inventory the content and actions and assign them to
|
|
108
|
+
the smallest useful visibility layer:
|
|
109
|
+
|
|
110
|
+
1. **Orient and act**: location, current state, and context needed for the next
|
|
111
|
+
meaningful choice.
|
|
112
|
+
2. **Support active work**: details and controls for the selected item or
|
|
113
|
+
current workflow.
|
|
114
|
+
3. **Available on demand**: advanced settings, diagnostics, credentials,
|
|
115
|
+
metadata, destructive actions, documentation, and rare tools.
|
|
116
|
+
|
|
117
|
+
Use a disclosure pattern that matches the task. Keep independent concerns easy
|
|
118
|
+
to scan inside an expanded area instead of dumping every form, explanation,
|
|
119
|
+
and secondary action into the first reveal. A collapsed row must retain its
|
|
120
|
+
label and essential state; remove explanatory subtext unless it changes the
|
|
121
|
+
next decision. For configuration with a known current state, show that state
|
|
122
|
+
first and expose editing contextually instead of defaulting to a full form.
|
|
123
|
+
|
|
124
|
+
Before shipping, verify collapsed, expanded, loading, empty, error, and
|
|
125
|
+
narrow-width states. The default state fails review when its first viewport
|
|
126
|
+
contains multiple unrelated forms, repeated explanatory copy, documentation
|
|
127
|
+
links, or controls for unrelated tasks. It also fails when useful information
|
|
128
|
+
has been replaced with empty whitespace or extra clicks solely to look minimal.
|
|
129
|
+
The default state passes when users can identify their location, current state,
|
|
130
|
+
current task, and relevant next choice without reading documentation, while
|
|
131
|
+
the useful information remains dense and scannable.
|
|
93
132
|
|
|
94
133
|
## Aesthetic Guidelines
|
|
95
134
|
|
|
@@ -166,26 +205,28 @@ passed this requirement yet.
|
|
|
166
205
|
|
|
167
206
|
### Product Surface Review
|
|
168
207
|
|
|
169
|
-
Before shipping a new app or a substantial redesign, review the
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
208
|
+
Before shipping a new app or a substantial redesign, review the surface as an
|
|
209
|
+
operator would use it repeatedly:
|
|
210
|
+
|
|
211
|
+
- Can users identify where they are, what state they are in, and what
|
|
212
|
+
meaningful choices or next steps are available?
|
|
213
|
+
- Remove competing or redundant elements, then place optional inputs, provider
|
|
214
|
+
choices, diagnostics, long explanations, and secondary actions where they
|
|
215
|
+
remain discoverable without crowding the current task.
|
|
216
|
+
- Match the composition to the workflow. Use a focused page or step flow when
|
|
217
|
+
the work is sequential; use an overview when comparing or monitoring several
|
|
218
|
+
things is genuinely the job. Preserve the user's progress in either case.
|
|
179
219
|
- Remove generic hero copy, feature tours, repeated helper text, nested cards,
|
|
180
220
|
status-chip soup, and decorative AI treatment before adding more styling.
|
|
181
|
-
- For review flows,
|
|
182
|
-
|
|
183
|
-
|
|
221
|
+
- For review flows, choose stacking, side-by-side comparison, or another
|
|
222
|
+
composition based on content length, scanability, and the user's comparison
|
|
223
|
+
task.
|
|
184
224
|
- Compare the result with sibling apps. Shared toolkit behavior should feel
|
|
185
225
|
consistent, but a repeated palette, hero composition, type pairing, and
|
|
186
226
|
radius language without a product reason is visual drift, not consistency.
|
|
187
|
-
- Check the result at the target desktop width and a
|
|
188
|
-
|
|
227
|
+
- Check the result with realistic content at the target desktop width and a
|
|
228
|
+
narrow width. If it feels like documentation instead of a tool, reduce noise
|
|
229
|
+
or clarify the hierarchy.
|
|
189
230
|
|
|
190
231
|
## shadcn/ui Design Rules
|
|
191
232
|
|
|
@@ -203,18 +244,24 @@ as an operator would use it repeatedly:
|
|
|
203
244
|
not show generic "Loading..." text for content loads; reserve `Spinner` for
|
|
204
245
|
brief mutations, uploads, and progress actions. Use the app's existing
|
|
205
246
|
loading primitive when it is a genuine design-system adapter. Empty states
|
|
206
|
-
should
|
|
247
|
+
should communicate the state and offer the appropriate next step or small set
|
|
248
|
+
of choices.
|
|
207
249
|
|
|
208
250
|
## Anti-Patterns
|
|
209
251
|
|
|
210
|
-
Avoid
|
|
252
|
+
Avoid patterns that add visual noise, obscure state, or compete with the user's
|
|
253
|
+
task without a clear product reason:
|
|
211
254
|
|
|
212
255
|
- Generic AI aesthetics: purple gradients, glassy cards everywhere, vague sparkle language, decorative blobs, and context-free hero sections.
|
|
213
256
|
- Custom reimplementations of shadcn primitives.
|
|
214
257
|
- Raw color overrides on shared components when semantic tokens or variants would work.
|
|
215
|
-
- New always-visible controls for rare actions
|
|
216
|
-
|
|
217
|
-
-
|
|
258
|
+
- New always-visible controls for rare actions when a contextual surface would
|
|
259
|
+
preserve discoverability and reduce noise.
|
|
260
|
+
- Full-width banners, persistent helper rows, decorative cards, or explanatory
|
|
261
|
+
chrome for status that could be communicated more clearly with less weight.
|
|
262
|
+
- Hiding essential meaning or operability behind progressive disclosure. Defer
|
|
263
|
+
complexity when it is contextual or infrequent, but preserve labels, focus,
|
|
264
|
+
hit targets, accessibility, and recovery paths.
|
|
218
265
|
- UI cards nested inside other cards.
|
|
219
266
|
- Text or icons that resize or shift fixed-format UI on hover/loading.
|
|
220
267
|
|
|
@@ -1,84 +1 @@
|
|
|
1
|
-
|
|
2
|
-
useEffect,
|
|
3
|
-
useSyncExternalStore,
|
|
4
|
-
type ReactNode,
|
|
5
|
-
type FC,
|
|
6
|
-
} from "react";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* External store for the page's header title + actions. We use an external
|
|
10
|
-
* store (not React context) so that pages can inject ReactNode without
|
|
11
|
-
* subscribing to the header state themselves — subscribing would trigger a
|
|
12
|
-
* re-render on every update, which in turn creates new JSX, which updates
|
|
13
|
-
* the store again, which re-renders… an infinite loop.
|
|
14
|
-
*
|
|
15
|
-
* Only <Header /> reads the store via useSyncExternalStore; pages only write.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
type Listener = () => void;
|
|
19
|
-
|
|
20
|
-
let currentTitle: ReactNode = null;
|
|
21
|
-
let currentActions: ReactNode = null;
|
|
22
|
-
const listeners = new Set<Listener>();
|
|
23
|
-
|
|
24
|
-
function notify() {
|
|
25
|
-
for (const l of listeners) l();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function subscribe(l: Listener): () => void {
|
|
29
|
-
listeners.add(l);
|
|
30
|
-
return () => {
|
|
31
|
-
listeners.delete(l);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Consumed only by <Header /> — returns the current title. */
|
|
36
|
-
export function useHeaderTitle(): ReactNode {
|
|
37
|
-
return useSyncExternalStore(
|
|
38
|
-
subscribe,
|
|
39
|
-
() => currentTitle,
|
|
40
|
-
() => currentTitle,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Consumed only by <Header /> — returns the current actions slot. */
|
|
45
|
-
export function useHeaderActions(): ReactNode {
|
|
46
|
-
return useSyncExternalStore(
|
|
47
|
-
subscribe,
|
|
48
|
-
() => currentActions,
|
|
49
|
-
() => currentActions,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Provider is now a no-op wrapper for backwards compatibility — the state
|
|
55
|
-
* lives in the module-level store above. Kept as a component so callers of
|
|
56
|
-
* <HeaderActionsProvider> don't need to change.
|
|
57
|
-
*/
|
|
58
|
-
export const HeaderActionsProvider: FC<{ children: ReactNode }> = ({
|
|
59
|
-
children,
|
|
60
|
-
}) => <>{children}</>;
|
|
61
|
-
|
|
62
|
-
/** Mount a custom title into the app header. Cleans up on unmount. */
|
|
63
|
-
export function useSetPageTitle(node: ReactNode) {
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
currentTitle = node;
|
|
66
|
-
notify();
|
|
67
|
-
return () => {
|
|
68
|
-
currentTitle = null;
|
|
69
|
-
notify();
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Mount ReactNode into the header's actions slot. Cleans up on unmount. */
|
|
75
|
-
export function useSetHeaderActions(node: ReactNode) {
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
currentActions = node;
|
|
78
|
-
notify();
|
|
79
|
-
return () => {
|
|
80
|
-
currentActions = null;
|
|
81
|
-
notify();
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
}
|
|
1
|
+
export * from "@agent-native/toolkit/app-shell";
|
|
@@ -1,21 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const MOBILE_BREAKPOINT = 768;
|
|
4
|
-
|
|
5
|
-
export function useIsMobile() {
|
|
6
|
-
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
-
undefined,
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
React.useEffect(() => {
|
|
11
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
-
const onChange = () => {
|
|
13
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
-
};
|
|
15
|
-
mql.addEventListener("change", onChange);
|
|
16
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
-
return () => mql.removeEventListener("change", onChange);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return !!isMobile;
|
|
21
|
-
}
|
|
1
|
+
export * from "@agent-native/toolkit/hooks";
|
|
@@ -1,34 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAgentNativeI18nCatalog } from "@agent-native/core/client/i18n";
|
|
2
2
|
|
|
3
3
|
import enUS from "./en-US";
|
|
4
4
|
|
|
5
|
-
export const i18nCatalog = {
|
|
6
|
-
sourceLocale: "en-US",
|
|
5
|
+
export const i18nCatalog = createAgentNativeI18nCatalog({
|
|
7
6
|
messages: enUS,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return (await import("./de-DE")).default;
|
|
20
|
-
case "ja-JP":
|
|
21
|
-
return (await import("./ja-JP")).default;
|
|
22
|
-
case "ko-KR":
|
|
23
|
-
return (await import("./ko-KR")).default;
|
|
24
|
-
case "pt-BR":
|
|
25
|
-
return (await import("./pt-BR")).default;
|
|
26
|
-
case "hi-IN":
|
|
27
|
-
return (await import("./hi-IN")).default;
|
|
28
|
-
case "ar-SA":
|
|
29
|
-
return (await import("./ar-SA")).default;
|
|
30
|
-
default:
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
7
|
+
localeLoaders: {
|
|
8
|
+
"zh-CN": () => import("./zh-CN"),
|
|
9
|
+
"zh-TW": () => import("./zh-TW"),
|
|
10
|
+
"es-ES": () => import("./es-ES"),
|
|
11
|
+
"fr-FR": () => import("./fr-FR"),
|
|
12
|
+
"de-DE": () => import("./de-DE"),
|
|
13
|
+
"ja-JP": () => import("./ja-JP"),
|
|
14
|
+
"ko-KR": () => import("./ko-KR"),
|
|
15
|
+
"pt-BR": () => import("./pt-BR"),
|
|
16
|
+
"hi-IN": () => import("./hi-IN"),
|
|
17
|
+
"ar-SA": () => import("./ar-SA"),
|
|
33
18
|
},
|
|
34
|
-
}
|
|
19
|
+
});
|
|
@@ -173,7 +173,8 @@ class AISDKEngine {
|
|
|
173
173
|
this.defaultModel = config.model ?? PROVIDER_DEFAULT_MODELS[provider];
|
|
174
174
|
this.supportedModels = PROVIDER_SUPPORTED_MODELS[provider];
|
|
175
175
|
this.preserveCustomModels =
|
|
176
|
-
provider === "
|
|
176
|
+
provider === "ollama" ||
|
|
177
|
+
(provider === "openai" && Boolean(config.baseUrl));
|
|
177
178
|
this.capabilities = PROVIDER_CAPABILITIES[provider];
|
|
178
179
|
this.apiKey =
|
|
179
180
|
config.apiKey ??
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare const OPENAI_BASE_URL_ENV_VAR = "OPENAI_BASE_URL";
|
|
2
|
+
export declare const OLLAMA_BASE_URL_ENV_VAR = "OLLAMA_BASE_URL";
|
|
3
|
+
export declare function normalizeProviderBaseUrl(value: string): string;
|
|
2
4
|
export declare function normalizeOpenAiBaseUrl(value: string): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const OPENAI_BASE_URL_ENV_VAR = "OPENAI_BASE_URL";
|
|
2
|
-
export
|
|
2
|
+
export const OLLAMA_BASE_URL_ENV_VAR = "OLLAMA_BASE_URL";
|
|
3
|
+
export function normalizeProviderBaseUrl(value) {
|
|
3
4
|
const trimmed = value.trim();
|
|
4
5
|
if (!trimmed) {
|
|
5
6
|
throw new Error("Endpoint URL is required.");
|
|
@@ -20,3 +21,6 @@ export function normalizeOpenAiBaseUrl(value) {
|
|
|
20
21
|
url.hash = "";
|
|
21
22
|
return url.toString().replace(/\/+$/, "");
|
|
22
23
|
}
|
|
24
|
+
export function normalizeOpenAiBaseUrl(value) {
|
|
25
|
+
return normalizeProviderBaseUrl(value);
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a provider endpoint before a server-side model request can use it.
|
|
3
|
+
* `allowPrivate` is reserved for operator-owned deployment configuration; it
|
|
4
|
+
* must never be enabled for a user- or agent-supplied URL.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateProviderBaseUrl(value: string, options?: {
|
|
7
|
+
allowPrivate?: boolean;
|
|
8
|
+
allowLocalOllama?: boolean;
|
|
9
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isBlockedExtensionUrlWithDns } from "../../extensions/url-safety.js";
|
|
2
|
+
import { normalizeProviderBaseUrl } from "./openai-compatible-endpoint.js";
|
|
3
|
+
function isLoopbackOllamaEndpoint(value) {
|
|
4
|
+
const hostname = new URL(value).hostname.toLowerCase();
|
|
5
|
+
return (hostname === "localhost" ||
|
|
6
|
+
hostname === "127.0.0.1" ||
|
|
7
|
+
hostname === "::1" ||
|
|
8
|
+
hostname === "[::1]");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate a provider endpoint before a server-side model request can use it.
|
|
12
|
+
* `allowPrivate` is reserved for operator-owned deployment configuration; it
|
|
13
|
+
* must never be enabled for a user- or agent-supplied URL.
|
|
14
|
+
*/
|
|
15
|
+
export async function validateProviderBaseUrl(value, options = {}) {
|
|
16
|
+
const normalized = normalizeProviderBaseUrl(value);
|
|
17
|
+
const allowLocalOllama = options.allowLocalOllama === true &&
|
|
18
|
+
process.env.NODE_ENV === "development" &&
|
|
19
|
+
isLoopbackOllamaEndpoint(normalized);
|
|
20
|
+
if (!options.allowPrivate &&
|
|
21
|
+
!allowLocalOllama &&
|
|
22
|
+
(await isBlockedExtensionUrlWithDns(normalized))) {
|
|
23
|
+
throw new Error("Endpoint URL resolves to a private/internal address — SSRF not allowed.");
|
|
24
|
+
}
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
@@ -14,7 +14,7 @@ export interface ProviderEnvMeta {
|
|
|
14
14
|
/** Input placeholder hint shown in the LLM picker's key field */
|
|
15
15
|
placeholder: string;
|
|
16
16
|
}
|
|
17
|
-
export { OPENAI_BASE_URL_ENV_VAR } from "./openai-compatible-endpoint.js";
|
|
17
|
+
export { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
|
|
18
18
|
export declare const PROVIDER_ENV_META: Record<string, ProviderEnvMeta>;
|
|
19
19
|
export declare const PROVIDER_TO_ENV: Record<string, string>;
|
|
20
20
|
export declare const PROVIDER_ENV_VARS: readonly string[];
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (AssistantChat composer, settings env-var list, settings key form) pick
|
|
8
8
|
* it up automatically.
|
|
9
9
|
*/
|
|
10
|
-
export { OPENAI_BASE_URL_ENV_VAR } from "./openai-compatible-endpoint.js";
|
|
10
|
+
export { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
|
|
11
11
|
export const PROVIDER_ENV_META = {
|
|
12
12
|
anthropic: {
|
|
13
13
|
envVar: "ANTHROPIC_API_KEY",
|
|
@@ -75,14 +75,16 @@ export declare function resolveDelegatedRunModel(engine: ModelResolvableEngine,
|
|
|
75
75
|
* verbatim instead of normalized against the built-in catalog.
|
|
76
76
|
*
|
|
77
77
|
* `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
|
|
78
|
-
* that flag is only set on an AI SDK engine INSTANCE when the
|
|
79
|
-
* is pointed at an OpenAI-compatible gateway (a custom
|
|
80
|
-
* Cloud or LiteLLM), whose model IDs are not in the
|
|
78
|
+
* that flag is only set on an AI SDK engine INSTANCE when the provider is
|
|
79
|
+
* Ollama, or when OpenAI is pointed at an OpenAI-compatible gateway (a custom
|
|
80
|
+
* base URL — e.g. Ollama Cloud or LiteLLM), whose model IDs are not in the
|
|
81
|
+
* built-in catalogs.
|
|
81
82
|
* The static registry entry the settings actions pass to
|
|
82
83
|
* `normalizeModelForEngine` cannot carry that runtime flag, so this async
|
|
83
|
-
* helper reproduces the same decision
|
|
84
|
-
*
|
|
85
|
-
* returns false so an unknown/invalid model
|
|
84
|
+
* helper reproduces the same decision from the request's stored/deploy config.
|
|
85
|
+
* Ollama always returns true because its local model inventory is user-defined;
|
|
86
|
+
* first-party OpenAI (no gateway) returns false so an unknown/invalid model
|
|
87
|
+
* still normalizes to a supported one.
|
|
86
88
|
*/
|
|
87
89
|
export declare function resolveEnginePreservesCustomModels(entry: Pick<AgentEngineEntry, "name">): Promise<boolean>;
|
|
88
90
|
/**
|
|
@@ -11,7 +11,8 @@ import { createRequire } from "node:module";
|
|
|
11
11
|
import { assertCredentialStoreReadable, canUseDeployCredentialFallbackForRequest, getBuilderCredentialAuthFailure, getProviderCredentialAuthFailure, readDeployCredentialEnv, resolveBuilderCredentialsDetailed, resolveSecret, } from "../../server/credential-provider.js";
|
|
12
12
|
import { getSetting } from "../../settings/store.js";
|
|
13
13
|
import { getAgentAppModelDefaultForCurrentRequest } from "../app-model-defaults.js";
|
|
14
|
-
import {
|
|
14
|
+
import { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
|
|
15
|
+
import { validateProviderBaseUrl } from "./provider-endpoint-validation.js";
|
|
15
16
|
const require = createRequire(import.meta.url);
|
|
16
17
|
const _registry = new Map();
|
|
17
18
|
const _packageAvailabilityCache = new Map();
|
|
@@ -234,20 +235,24 @@ export function resolveDelegatedRunModel(engine, options) {
|
|
|
234
235
|
* verbatim instead of normalized against the built-in catalog.
|
|
235
236
|
*
|
|
236
237
|
* `normalizeModelForEngine` honors a live engine's `preserveCustomModels`, but
|
|
237
|
-
* that flag is only set on an AI SDK engine INSTANCE when the
|
|
238
|
-
* is pointed at an OpenAI-compatible gateway (a custom
|
|
239
|
-
* Cloud or LiteLLM), whose model IDs are not in the
|
|
238
|
+
* that flag is only set on an AI SDK engine INSTANCE when the provider is
|
|
239
|
+
* Ollama, or when OpenAI is pointed at an OpenAI-compatible gateway (a custom
|
|
240
|
+
* base URL — e.g. Ollama Cloud or LiteLLM), whose model IDs are not in the
|
|
241
|
+
* built-in catalogs.
|
|
240
242
|
* The static registry entry the settings actions pass to
|
|
241
243
|
* `normalizeModelForEngine` cannot carry that runtime flag, so this async
|
|
242
|
-
* helper reproduces the same decision
|
|
243
|
-
*
|
|
244
|
-
* returns false so an unknown/invalid model
|
|
244
|
+
* helper reproduces the same decision from the request's stored/deploy config.
|
|
245
|
+
* Ollama always returns true because its local model inventory is user-defined;
|
|
246
|
+
* first-party OpenAI (no gateway) returns false so an unknown/invalid model
|
|
247
|
+
* still normalizes to a supported one.
|
|
245
248
|
*/
|
|
246
249
|
export async function resolveEnginePreservesCustomModels(entry) {
|
|
250
|
+
if (entry.name === "ai-sdk:ollama")
|
|
251
|
+
return true;
|
|
247
252
|
if (entry.name !== "ai-sdk:openai")
|
|
248
253
|
return false;
|
|
249
254
|
try {
|
|
250
|
-
return Boolean(await
|
|
255
|
+
return Boolean(await resolveProviderBaseUrl(OPENAI_BASE_URL_ENV_VAR));
|
|
251
256
|
}
|
|
252
257
|
catch {
|
|
253
258
|
return false;
|
|
@@ -493,19 +498,22 @@ function engineCreateConfig(entry, apiKey, extra) {
|
|
|
493
498
|
...(extra ?? {}),
|
|
494
499
|
};
|
|
495
500
|
}
|
|
496
|
-
async function
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
501
|
+
async function resolveProviderBaseUrl(envVar) {
|
|
502
|
+
const raw = await resolveSecret(envVar);
|
|
503
|
+
if (!raw && canUseDeployCredentialFallbackForRequest(envVar)) {
|
|
504
|
+
const deployValue = readDeployCredentialEnv(envVar);
|
|
505
|
+
if (!deployValue)
|
|
506
|
+
return undefined;
|
|
507
|
+
return validateProviderBaseUrl(deployValue, {
|
|
508
|
+
allowPrivate: true,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return raw
|
|
512
|
+
? validateProviderBaseUrl(raw, {
|
|
513
|
+
allowLocalOllama: envVar === OLLAMA_BASE_URL_ENV_VAR &&
|
|
514
|
+
process.env.NODE_ENV === "development",
|
|
515
|
+
})
|
|
516
|
+
: undefined;
|
|
509
517
|
}
|
|
510
518
|
/**
|
|
511
519
|
* A Builder connection we could not read is not a missing connection. Throwing
|
|
@@ -609,12 +617,17 @@ async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCr
|
|
|
609
617
|
: undefined;
|
|
610
618
|
}
|
|
611
619
|
}
|
|
612
|
-
if (entry.name === "ai-sdk:openai") {
|
|
620
|
+
if (entry.name === "ai-sdk:openai" || entry.name === "ai-sdk:ollama") {
|
|
613
621
|
if (typeof safeExtra.baseURL === "string" && safeExtra.baseUrl == null) {
|
|
614
|
-
safeExtra.baseUrl =
|
|
622
|
+
safeExtra.baseUrl = await validateProviderBaseUrl(safeExtra.baseURL, {
|
|
623
|
+
allowLocalOllama: entry.name === "ai-sdk:ollama" &&
|
|
624
|
+
process.env.NODE_ENV === "development",
|
|
625
|
+
});
|
|
615
626
|
}
|
|
616
627
|
if (safeExtra.baseUrl == null) {
|
|
617
|
-
const baseUrl = await
|
|
628
|
+
const baseUrl = await resolveProviderBaseUrl(entry.name === "ai-sdk:ollama"
|
|
629
|
+
? OLLAMA_BASE_URL_ENV_VAR
|
|
630
|
+
: OPENAI_BASE_URL_ENV_VAR);
|
|
618
631
|
if (baseUrl)
|
|
619
632
|
safeExtra.baseUrl = baseUrl;
|
|
620
633
|
}
|