@agent-native/core 0.92.10 → 0.92.12
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/core/CHANGELOG.md +29 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +214 -17
- package/corpus/core/src/agent/run-manager.ts +72 -14
- package/corpus/core/src/agent/run-store.ts +152 -27
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat-adapter.ts +56 -12
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +75 -9
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +82 -7
- package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +4 -7
- package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
- package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +82 -7
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +180 -17
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +63 -14
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +129 -22
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +48 -12
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
|
@@ -385,6 +385,8 @@ export function useCollabReconcile({
|
|
|
385
385
|
const editorUnchangedSinceApply =
|
|
386
386
|
lastAppliedSerializedRef.current !== null &&
|
|
387
387
|
currentMarkdown === lastAppliedSerializedRef.current;
|
|
388
|
+
const typingRecently =
|
|
389
|
+
editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
|
|
388
390
|
|
|
389
391
|
// Doc-equivalence skip. Never re-apply content the editor already
|
|
390
392
|
// represents — comparing by DOC EQUIVALENCE, not raw strings/timestamps:
|
|
@@ -407,11 +409,15 @@ export function useCollabReconcile({
|
|
|
407
409
|
// `<p>` → `<p>` → `&lt;p&gt;` escalation.
|
|
408
410
|
if (
|
|
409
411
|
currentMarkdown === normalizedValue ||
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
412
|
+
(typingRecently &&
|
|
413
|
+
// A stale echo of our own (possibly partial) save while the user is
|
|
414
|
+
// actively typing would clobber the fresh tail. Outside active
|
|
415
|
+
// typing, the same bytes can be a deliberate newer external revert
|
|
416
|
+
// (history restore / provider conflict choice) and must be allowed
|
|
417
|
+
// through even if mount-time normalization emitted them.
|
|
418
|
+
(value === lastEmittedRef.current ||
|
|
419
|
+
recentEmittedRef.current.includes(value) ||
|
|
420
|
+
recentEmittedRef.current.includes(normalizedValue))) ||
|
|
415
421
|
(editorUnchangedSinceApply &&
|
|
416
422
|
(value === lastAppliedValueRef.current ||
|
|
417
423
|
normalizedValue === lastAppliedSerializedRef.current))
|
|
@@ -442,8 +448,6 @@ export function useCollabReconcile({
|
|
|
442
448
|
// for non-idempotent input, fight every keystroke. Newer external content
|
|
443
449
|
// retries so it still lands once they pause; older-or-equal content is a
|
|
444
450
|
// stale poll and is dropped outright while focused.
|
|
445
|
-
const typingRecently =
|
|
446
|
-
editor.isFocused && Date.now() - lastTypedAtRef.current < 1500;
|
|
447
451
|
if (typingRecently) {
|
|
448
452
|
if (externalNewer) {
|
|
449
453
|
retry = setTimeout(() => apply(deferred), 700);
|
|
@@ -549,6 +553,13 @@ export function useCollabReconcile({
|
|
|
549
553
|
|
|
550
554
|
const shouldIgnoreUpdate = (transaction: Transaction): boolean => {
|
|
551
555
|
if (!editable || isSettingContentRef.current) return true;
|
|
556
|
+
// Collaboration can emit schema/default-paragraph transactions while the
|
|
557
|
+
// persisted Y.Doc is still loading and before the authoritative value has
|
|
558
|
+
// seeded/reconciled. Never let those transient pre-seed updates reach an
|
|
559
|
+
// app's local mirror or autosave path (Content serializes an empty paragraph
|
|
560
|
+
// as `<empty-block/>`, which is non-empty text and bypasses the generic
|
|
561
|
+
// registerEmitted empty-string guard).
|
|
562
|
+
if (collab && !seededRef.current) return true;
|
|
552
563
|
if (transaction.getMeta(RICH_MARKDOWN_PROGRAMMATIC_TRANSACTION)) {
|
|
553
564
|
return true;
|
|
554
565
|
}
|
|
@@ -149,23 +149,34 @@ export async function loadAwarenessRows(
|
|
|
149
149
|
now: number = Date.now(),
|
|
150
150
|
): Promise<AwarenessEntry[]> {
|
|
151
151
|
try {
|
|
152
|
-
await
|
|
153
|
-
const client = getDbExec();
|
|
154
|
-
const { rows } = await client.execute({
|
|
155
|
-
sql: `SELECT client_id, state, last_seen FROM _collab_awareness
|
|
156
|
-
WHERE doc_id = ? AND last_seen >= ?`,
|
|
157
|
-
args: [docId, now - ROW_TTL_MS],
|
|
158
|
-
});
|
|
159
|
-
return rows.map((row: any) => ({
|
|
160
|
-
clientId: Number(row.client_id),
|
|
161
|
-
state: String(row.state),
|
|
162
|
-
lastSeen: Number(row.last_seen),
|
|
163
|
-
}));
|
|
152
|
+
return await loadAwarenessRowsStrict(docId, now);
|
|
164
153
|
} catch {
|
|
165
154
|
return [];
|
|
166
155
|
}
|
|
167
156
|
}
|
|
168
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Strict awareness read for safety-critical callers that must distinguish
|
|
160
|
+
* "no active participants" from "presence storage could not be read."
|
|
161
|
+
*/
|
|
162
|
+
export async function loadAwarenessRowsStrict(
|
|
163
|
+
docId: string,
|
|
164
|
+
now: number = Date.now(),
|
|
165
|
+
): Promise<AwarenessEntry[]> {
|
|
166
|
+
await ensureTable();
|
|
167
|
+
const client = getDbExec();
|
|
168
|
+
const { rows } = await client.execute({
|
|
169
|
+
sql: `SELECT client_id, state, last_seen FROM _collab_awareness
|
|
170
|
+
WHERE doc_id = ? AND last_seen >= ?`,
|
|
171
|
+
args: [docId, now - ROW_TTL_MS],
|
|
172
|
+
});
|
|
173
|
+
return rows.map((row: any) => ({
|
|
174
|
+
clientId: Number(row.client_id),
|
|
175
|
+
state: String(row.state),
|
|
176
|
+
lastSeen: Number(row.last_seen),
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
|
|
169
180
|
async function maybePurge(docId: string, now: number): Promise<void> {
|
|
170
181
|
const last = _lastPurges.get(docId) ?? 0;
|
|
171
182
|
if (now - last < PURGE_INTERVAL_MS) return;
|
|
@@ -579,7 +579,10 @@ class CollabDocConnection {
|
|
|
579
579
|
this.awareness.setLocalStateField("visible", !isDocumentHidden());
|
|
580
580
|
}
|
|
581
581
|
|
|
582
|
-
private handleAwarenessChange = (
|
|
582
|
+
private handleAwarenessChange = (
|
|
583
|
+
_changes?: { added: number[]; updated: number[]; removed: number[] },
|
|
584
|
+
origin?: unknown,
|
|
585
|
+
): void => {
|
|
583
586
|
const users: CollabUser[] = [];
|
|
584
587
|
let hasAgent = false;
|
|
585
588
|
this.awareness.getStates().forEach((state, clientId) => {
|
|
@@ -616,12 +619,20 @@ class CollabDocConnection {
|
|
|
616
619
|
this.setSnapshot({ activeUsers, agentPresent: hasAgent });
|
|
617
620
|
}
|
|
618
621
|
|
|
619
|
-
// Fast awareness push: whenever awareness changes (e.g.
|
|
620
|
-
// setPresence() calls), schedule a throttled POST so peers
|
|
621
|
-
// at ~150ms instead of waiting for the next poll cycle.
|
|
622
|
+
// Fast awareness push: whenever OUR OWN awareness state changes (e.g.
|
|
623
|
+
// cursor moves, setPresence() calls), schedule a throttled POST so peers
|
|
624
|
+
// receive updates at ~150ms instead of waiting for the next poll cycle.
|
|
625
|
+
// Gated on origin === "local": this listener also fires for remote
|
|
626
|
+
// awareness changes (poll/SSE call `awareness.emit("change", [changes,
|
|
627
|
+
// "remote"])` after reconciling incoming states — see
|
|
628
|
+
// applyAwarenessEvent/poll above). Without the origin check, every
|
|
629
|
+
// peer's cursor move would also re-trigger THIS client to re-broadcast
|
|
630
|
+
// its own unchanged state, turning one person moving their mouse into
|
|
631
|
+
// O(n) redundant POSTs from every other connected client (an awareness
|
|
632
|
+
// storm that gets worse as more people join the doc). Only active once a
|
|
622
633
|
// local user identity has been published (matches the previous per-hook
|
|
623
634
|
// gating on `user`). The poll cycle remains the authoritative baseline.
|
|
624
|
-
if (this.lastSetUser && !this.disposed) {
|
|
635
|
+
if (this.lastSetUser && origin === "local" && !this.disposed) {
|
|
625
636
|
scheduleAwarenessPush(
|
|
626
637
|
this.baseUrl,
|
|
627
638
|
this.docId,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
RECENT_EDIT_TTL_MS,
|
|
22
22
|
type AttributedRecentEdit,
|
|
23
23
|
type RecentEdit,
|
|
24
|
+
type RecentEditDescriptor,
|
|
24
25
|
} from "@agent-native/toolkit/collab-ui";
|
|
25
26
|
import { useEffect, useRef, useState } from "react";
|
|
26
27
|
|
|
@@ -34,16 +35,78 @@ export {
|
|
|
34
35
|
type RecentEditDescriptor,
|
|
35
36
|
} from "@agent-native/toolkit/collab-ui";
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Hard cap on any single string carried in a recentEdits entry (quote,
|
|
40
|
+
* selector, path segment, label). Callers are expected to pass short
|
|
41
|
+
* excerpts already (existing call sites trim to 80–120 chars for a "what
|
|
42
|
+
* changed" snippet), but this is the ring's own size ceiling — a caller that
|
|
43
|
+
* forgets to trim (e.g. passing a whole paragraph/document as `quote`) must
|
|
44
|
+
* not blow up the awareness payload every connected client receives on the
|
|
45
|
+
* fast-push path, nor the `_collab_awareness` SQL row it gets mirrored into.
|
|
46
|
+
*/
|
|
47
|
+
const RECENT_EDIT_STRING_MAX = 500;
|
|
48
|
+
|
|
49
|
+
function truncateString(value: string): string {
|
|
50
|
+
return value.length > RECENT_EDIT_STRING_MAX
|
|
51
|
+
? value.slice(0, RECENT_EDIT_STRING_MAX)
|
|
52
|
+
: value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function truncateDescriptor(
|
|
56
|
+
descriptor: RecentEditDescriptor,
|
|
57
|
+
): RecentEditDescriptor {
|
|
58
|
+
// The union's last member (`{ kind: string; [key: string]: unknown }`) is an
|
|
59
|
+
// open-ended catch-all whose `kind` is a plain `string`, so a `switch` on
|
|
60
|
+
// `descriptor.kind` can't discriminate it away from the literal-kind
|
|
61
|
+
// members for the compiler — every property still type-checks as
|
|
62
|
+
// `unknown`. Read/write through an untyped view instead and lean on
|
|
63
|
+
// runtime checks; the return value is cast back to the real type below.
|
|
64
|
+
const d = descriptor as Record<string, unknown> & { kind: string };
|
|
65
|
+
if (d.kind === "text" && typeof d.quote === "string") {
|
|
66
|
+
return { ...d, quote: truncateString(d.quote) } as RecentEditDescriptor;
|
|
67
|
+
}
|
|
68
|
+
if (d.kind === "selector" && typeof d.selector === "string") {
|
|
69
|
+
return {
|
|
70
|
+
...d,
|
|
71
|
+
selector: truncateString(d.selector),
|
|
72
|
+
} as RecentEditDescriptor;
|
|
73
|
+
}
|
|
74
|
+
if (d.kind === "paths" && Array.isArray(d.paths)) {
|
|
75
|
+
return {
|
|
76
|
+
...d,
|
|
77
|
+
paths: d.paths.map((p) =>
|
|
78
|
+
typeof p === "string" ? truncateString(p) : p,
|
|
79
|
+
),
|
|
80
|
+
} as RecentEditDescriptor;
|
|
81
|
+
}
|
|
82
|
+
if (d.kind === "doc") {
|
|
83
|
+
return descriptor;
|
|
84
|
+
}
|
|
85
|
+
// Open-ended shape — trim any string-valued fields defensively without
|
|
86
|
+
// knowing their names.
|
|
87
|
+
const trimmed: Record<string, unknown> = {};
|
|
88
|
+
for (const [key, value] of Object.entries(d)) {
|
|
89
|
+
trimmed[key] = typeof value === "string" ? truncateString(value) : value;
|
|
90
|
+
}
|
|
91
|
+
return trimmed as RecentEditDescriptor;
|
|
92
|
+
}
|
|
93
|
+
|
|
37
94
|
/**
|
|
38
95
|
* Append an edit to a recentEdits ring, keeping the newest
|
|
39
|
-
* {@link RECENT_EDITS_MAX} entries. Pure — returns a new array.
|
|
96
|
+
* {@link RECENT_EDITS_MAX} entries. Pure — returns a new array. Descriptor
|
|
97
|
+
* strings and the label are truncated to {@link RECENT_EDIT_STRING_MAX}
|
|
98
|
+
* characters as a defensive cap on the awareness payload size.
|
|
40
99
|
*/
|
|
41
100
|
export function appendRecentEdit(
|
|
42
101
|
existing: RecentEdit[] | undefined,
|
|
43
102
|
edit: RecentEdit,
|
|
44
103
|
): RecentEdit[] {
|
|
45
104
|
const ring = Array.isArray(existing) ? existing.slice() : [];
|
|
46
|
-
ring.push(
|
|
105
|
+
ring.push({
|
|
106
|
+
...edit,
|
|
107
|
+
descriptor: truncateDescriptor(edit.descriptor),
|
|
108
|
+
label: edit.label ? truncateString(edit.label) : edit.label,
|
|
109
|
+
});
|
|
47
110
|
if (ring.length > RECENT_EDITS_MAX) {
|
|
48
111
|
ring.splice(0, ring.length - RECENT_EDITS_MAX);
|
|
49
112
|
}
|
|
@@ -8561,10 +8561,34 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8561
8561
|
// covers the initial dispatch (a connected client is polling the claim),
|
|
8562
8562
|
// but a server-chained CONTINUATION handoff has no foreground watching
|
|
8563
8563
|
// it: if the dispatch is lost after the successor row was inserted, the
|
|
8564
|
-
// row would sit at dispatch_mode='background' forever and the
|
|
8565
|
-
// silently.
|
|
8566
|
-
//
|
|
8567
|
-
//
|
|
8564
|
+
// row would otherwise sit at dispatch_mode='background' forever and the
|
|
8565
|
+
// turn hangs silently. `chainServerDrivenContinuation` (production-agent.ts)
|
|
8566
|
+
// leaves exactly such a row behind — status='running', dispatch_mode=
|
|
8567
|
+
// 'background', `dispatch_payload` intact — when it exhausts its own
|
|
8568
|
+
// dispatch retry budget, instead of erroring it immediately. This sweep
|
|
8569
|
+
// gives that row a real chance to RECOVER: within
|
|
8570
|
+
// `UNCLAIMED_BACKGROUND_RUN_REDISPATCH_BOUND_MS` of its original
|
|
8571
|
+
// `started_at` it redispatches the handoff (same `_process-run`
|
|
8572
|
+
// processor, same persisted payload); past that bound it falls back to
|
|
8573
|
+
// the existing loud reap (`background_worker_never_started`) so a
|
|
8574
|
+
// genuinely dead handoff still fails loud, never silently. A
|
|
8575
|
+
// redispatch is always safe to attempt — even a duplicate or
|
|
8576
|
+
// late-arriving one — because the worker's `claimBackgroundRun` atomic
|
|
8577
|
+
// CAS (status='running' AND dispatch_mode='background' -> 'background-
|
|
8578
|
+
// processing') is the sole gate on actual execution; a row that was
|
|
8579
|
+
// already claimed or already reaped by a concurrent path just loses the
|
|
8580
|
+
// CAS and no-ops. Cheap: one indexed-ish query per 2-min window.
|
|
8581
|
+
//
|
|
8582
|
+
// THREE-SITE INVARIANT (keep in lockstep): this sweep only ever sees the
|
|
8583
|
+
// deferred successor because the ~1s client poll in
|
|
8584
|
+
// `getActiveRunForThreadAsync` (run-manager.ts) skips its own
|
|
8585
|
+
// `reapUnclaimedBackgroundRun` while the row is within the redispatch
|
|
8586
|
+
// bound (`shouldRedispatchUnclaimedBackgroundRun`). If a future change
|
|
8587
|
+
// makes that client poll reap deferred successors at the 25s grace
|
|
8588
|
+
// again, this sweep will almost never win the race for connected clients.
|
|
8589
|
+
// Do not edit one site without the others (producer:
|
|
8590
|
+
// chainServerDrivenContinuation in production-agent.ts; guard:
|
|
8591
|
+
// run-manager.ts; recovery actor: here).
|
|
8568
8592
|
(() => {
|
|
8569
8593
|
let lastSweep = 0;
|
|
8570
8594
|
const SWEEP_INTERVAL_MS = 2 * 60 * 1000;
|
|
@@ -8577,22 +8601,85 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8577
8601
|
|
|
8578
8602
|
(async () => {
|
|
8579
8603
|
const {
|
|
8580
|
-
|
|
8604
|
+
listUnclaimedBackgroundRunRows,
|
|
8581
8605
|
reapUnclaimedBackgroundRun,
|
|
8606
|
+
updateRunHeartbeat,
|
|
8607
|
+
shouldRedispatchUnclaimedBackgroundRun,
|
|
8582
8608
|
} = await import("../agent/run-store.js");
|
|
8583
|
-
|
|
8609
|
+
const { resolveAgentChatProcessRunDispatchPath } =
|
|
8610
|
+
await import("../agent/durable-background.js");
|
|
8611
|
+
const { fireInternalDispatch } =
|
|
8612
|
+
await import("./self-dispatch.js");
|
|
8613
|
+
let rows: { id: string; startedAt: number }[];
|
|
8584
8614
|
try {
|
|
8585
|
-
|
|
8615
|
+
rows = await listUnclaimedBackgroundRunRows();
|
|
8586
8616
|
} catch {
|
|
8587
8617
|
return; // Table may not exist yet on first boot
|
|
8588
8618
|
}
|
|
8589
|
-
for (const
|
|
8619
|
+
for (const row of rows) {
|
|
8590
8620
|
try {
|
|
8591
|
-
|
|
8621
|
+
if (shouldRedispatchUnclaimedBackgroundRun(row)) {
|
|
8622
|
+
// Bump liveness BEFORE attempting the redispatch so the
|
|
8623
|
+
// row doesn't look freshly-stale again the instant this
|
|
8624
|
+
// tick returns — best-effort, the CAS is what actually
|
|
8625
|
+
// matters for correctness, not this timing.
|
|
8626
|
+
await updateRunHeartbeat(row.id).catch(() => {});
|
|
8627
|
+
try {
|
|
8628
|
+
// DELIBERATE: this marker omits `continuationCount`.
|
|
8629
|
+
// `chainServerDrivenContinuation` (production-agent.ts)
|
|
8630
|
+
// reads `backgroundRunMarker.continuationCount` to
|
|
8631
|
+
// compute `backgroundContinuationCount`, defaulting to 0
|
|
8632
|
+
// when absent — so a chunk recovered here always starts
|
|
8633
|
+
// a fresh nested-dispatch segment at depth 0, regardless
|
|
8634
|
+
// of how deep the chain was before this sweep picked it
|
|
8635
|
+
// up. This is what makes the sweep a genuine CHAIN
|
|
8636
|
+
// BREAK, not just a retry: this redispatch fires from an
|
|
8637
|
+
// unrelated, timer-driven invocation rather than from
|
|
8638
|
+
// inside the prior chain's own live execution, so
|
|
8639
|
+
// starting its nested-depth count over at 0 here is
|
|
8640
|
+
// correct — see `MAX_NESTED_SELF_DISPATCH_DEPTH` in
|
|
8641
|
+
// production-agent.ts for why nested depth is bounded
|
|
8642
|
+
// and how this reset keeps a long turn progressing past
|
|
8643
|
+
// Netlify's undocumented self-invocation loop-protection
|
|
8644
|
+
// limit instead of dying at it. Do not "fix" this by
|
|
8645
|
+
// adding `continuationCount` back without re-reading
|
|
8646
|
+
// that constant's doc comment.
|
|
8647
|
+
await fireInternalDispatch({
|
|
8648
|
+
path: resolveAgentChatProcessRunDispatchPath(),
|
|
8649
|
+
taskId: row.id,
|
|
8650
|
+
body: {
|
|
8651
|
+
internalContinuation: true,
|
|
8652
|
+
[AGENT_CHAT_BACKGROUND_RUN_FIELD]: {
|
|
8653
|
+
runId: row.id,
|
|
8654
|
+
payloadRef: true,
|
|
8655
|
+
},
|
|
8656
|
+
},
|
|
8657
|
+
awaitResponse: true,
|
|
8658
|
+
responseTimeoutMs: 15_000,
|
|
8659
|
+
});
|
|
8660
|
+
console.error(
|
|
8661
|
+
"[agent-chat] redispatched unclaimed background run (handoff recovery):",
|
|
8662
|
+
row.id,
|
|
8663
|
+
);
|
|
8664
|
+
} catch (redispatchErr) {
|
|
8665
|
+
console.error(
|
|
8666
|
+
"[agent-chat] unclaimed background run redispatch attempt failed (retrying until the redispatch bound, then reaping):",
|
|
8667
|
+
row.id,
|
|
8668
|
+
redispatchErr instanceof Error
|
|
8669
|
+
? redispatchErr.message
|
|
8670
|
+
: redispatchErr,
|
|
8671
|
+
);
|
|
8672
|
+
}
|
|
8673
|
+
continue;
|
|
8674
|
+
}
|
|
8675
|
+
// Redispatch bound exceeded — this handoff is not
|
|
8676
|
+
// recovering. Fall back to the pre-existing loud reap so
|
|
8677
|
+
// the turn fails loud instead of retrying forever.
|
|
8678
|
+
const reaped = await reapUnclaimedBackgroundRun(row.id);
|
|
8592
8679
|
if (reaped) {
|
|
8593
8680
|
console.error(
|
|
8594
|
-
"[agent-chat] swept unclaimed background run (handoff lost):",
|
|
8595
|
-
|
|
8681
|
+
"[agent-chat] swept unclaimed background run (handoff lost, redispatch bound exceeded):",
|
|
8682
|
+
row.id,
|
|
8596
8683
|
);
|
|
8597
8684
|
}
|
|
8598
8685
|
} catch {
|
|
@@ -16,6 +16,21 @@ export interface BuilderDesignSystemCodeFileInput {
|
|
|
16
16
|
filename: string;
|
|
17
17
|
content: string;
|
|
18
18
|
mimeType?: string;
|
|
19
|
+
/**
|
|
20
|
+
* How `content` is encoded. Defaults to `"utf8"` (existing behavior,
|
|
21
|
+
* unchanged for every current text-file caller). Pass `"base64"` for
|
|
22
|
+
* binary files -- most importantly `.fig` (a zip/kiwi binary container,
|
|
23
|
+
* never valid UTF-8 text). Without this, a `.fig` upload silently
|
|
24
|
+
* corrupts: `mimeTypeForBuilderDesignSystemFilename` already special-cases
|
|
25
|
+
* `.fig` as `application/octet-stream`, but the actual byte pipeline ran
|
|
26
|
+
* every file through `TextEncoder().encode()` regardless, which mangles
|
|
27
|
+
* any byte >= 0x80 in a binary-as-string payload (or, if the caller
|
|
28
|
+
* base64-encoded first with no decode step here, stores the literal
|
|
29
|
+
* base64 text instead of the decoded binary). Callers sending `.fig`/PDF/
|
|
30
|
+
* other binary bytes must base64-encode `content` and set this to
|
|
31
|
+
* `"base64"`.
|
|
32
|
+
*/
|
|
33
|
+
encoding?: "utf8" | "base64";
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
export interface BuildBuilderDesignSystemIndexFilesOptions {
|
|
@@ -184,9 +199,20 @@ export function buildBuilderDesignSystemIndexFiles({
|
|
|
184
199
|
const files: BuilderDesignSystemIndexFile[] = [];
|
|
185
200
|
let totalBytes = 0;
|
|
186
201
|
|
|
187
|
-
function
|
|
202
|
+
function pushFile(
|
|
203
|
+
filename: string,
|
|
204
|
+
content: string,
|
|
205
|
+
mimeType?: string,
|
|
206
|
+
encoding?: "utf8" | "base64",
|
|
207
|
+
) {
|
|
188
208
|
const normalizedName = filename.replace(/^\/+/, "") || "code.txt";
|
|
189
|
-
|
|
209
|
+
// `.fig`/PDF/other binary payloads must round-trip through base64, not
|
|
210
|
+
// UTF-8 -- TextEncoder().encode() on a binary-as-string payload mangles
|
|
211
|
+
// any byte >= 0x80. See BuilderDesignSystemCodeFileInput.encoding.
|
|
212
|
+
const data =
|
|
213
|
+
encoding === "base64"
|
|
214
|
+
? new Uint8Array(Buffer.from(content, "base64"))
|
|
215
|
+
: encoder.encode(content);
|
|
190
216
|
if (data.byteLength === 0) return;
|
|
191
217
|
if (totalBytes + data.byteLength > maxTotalCodeBytes) return;
|
|
192
218
|
totalBytes += data.byteLength;
|
|
@@ -201,7 +227,7 @@ export function buildBuilderDesignSystemIndexFiles({
|
|
|
201
227
|
}
|
|
202
228
|
|
|
203
229
|
if (designMd?.trim()) {
|
|
204
|
-
|
|
230
|
+
pushFile(
|
|
205
231
|
designMdFilename?.trim() || "design.md",
|
|
206
232
|
designMd,
|
|
207
233
|
"text/markdown",
|
|
@@ -209,7 +235,7 @@ export function buildBuilderDesignSystemIndexFiles({
|
|
|
209
235
|
}
|
|
210
236
|
|
|
211
237
|
for (const file of (codeFiles ?? []).slice(0, maxCodeFiles)) {
|
|
212
|
-
|
|
238
|
+
pushFile(file.filename, file.content, file.mimeType, file.encoding);
|
|
213
239
|
}
|
|
214
240
|
|
|
215
241
|
return files;
|
|
@@ -126,7 +126,7 @@ Transcription takes an audio file and returns text + segments. That's not a prom
|
|
|
126
126
|
**Provider priority:**
|
|
127
127
|
|
|
128
128
|
1. **Native (highest priority).** The browser's Web Speech API, desktop local Whisper/macOS SFSpeech when available, and desktop Web Speech fallback on non-mac run during recording and save results via `save-browser-transcript`. This gives an instant transcript with no API key when the local/browser recognizer is available. When `request-transcript` runs afterward, it preserves the ready native transcript and only falls back to a cloud provider if native text is missing.
|
|
129
|
-
2. **Cloud fallback — Builder.io managed (Gemini).** When a Builder account is connected (`BUILDER_PRIVATE_KEY` or per-user OAuth, no separate API key needed), `request-transcript` calls `transcribeWithBuilder()`, which routes audio to Builder.io's managed Gemini transcription. Returns text plus timestamped segments.
|
|
129
|
+
2. **Cloud fallback — Builder.io managed (Gemini).** When a Builder account is connected (`BUILDER_PRIVATE_KEY` or per-user OAuth, no separate API key needed), `request-transcript` calls `transcribeWithBuilder()`, which routes audio to Builder.io's managed Gemini transcription. Returns text plus timestamped segments. Retry a failed transcript with `force: true`; pass `regenerate: true` to replace an existing ready transcript from the stored recording media. Regeneration keeps the prior ready transcript available if the new provider attempt fails.
|
|
130
130
|
3. **Cloud fallback — Groq Whisper.** `GROQ_API_KEY` → `https://api.groq.com/openai/v1/audio/transcriptions`, model `whisper-large-v3-turbo`. Fast (~$0.04/hour of audio) Whisper-compatible speech-to-text used when Builder is unavailable.
|
|
131
131
|
|
|
132
132
|
Clips never routes recording/meeting audio to OpenAI for transcription. (Groq's endpoint is OpenAI-_compatible_ in request shape only — the audio goes to Groq, not OpenAI.)
|
|
@@ -14,6 +14,12 @@ description: >-
|
|
|
14
14
|
|
|
15
15
|
Recording sharing uses the framework `sharing` system — not a custom share table. Recordings are registered via `registerShareableResource({ type: "recording", ... })` in `server/db/index.ts`. The `share-resource`, `unshare-resource`, `list-resource-shares`, and `set-resource-visibility` actions are auto-mounted and handle per-user grants, per-org grants, and the three visibility levels (`private` / `org` / `public`).
|
|
16
16
|
|
|
17
|
+
Unlike the framework-wide private default, normal Clips recordings and uploaded
|
|
18
|
+
videos default to **public** so their copied share links work immediately.
|
|
19
|
+
Embedded bug-report recordings are the exception and default to organization
|
|
20
|
+
visibility. Callers can still explicitly create a private or organization-only
|
|
21
|
+
recording, and owners/admins can change visibility from the Share dialog.
|
|
22
|
+
|
|
17
23
|
Clips **adds two things** on top of the framework system:
|
|
18
24
|
|
|
19
25
|
1. **Password** — an optional bcrypt'd string on the `recordings` row. When set, all non-owner viewers must enter it to play the recording.
|
|
@@ -62,7 +68,7 @@ import { ShareDialog } from "@agent-native/core/client";
|
|
|
62
68
|
</>
|
|
63
69
|
}
|
|
64
70
|
embedTabContent={<EmbedSnippetAndOptions recordingId={recording.id} />}
|
|
65
|
-
|
|
71
|
+
/>;
|
|
66
72
|
```
|
|
67
73
|
|
|
68
74
|
- `shareUrl` / `embedUrl` — the copy-link and embed URLs the framework renders in its tabs.
|
|
@@ -163,11 +169,11 @@ Required Slack app setup:
|
|
|
163
169
|
Recordings can expose URLs meant for external agents without handing over raw
|
|
164
170
|
video bytes:
|
|
165
171
|
|
|
166
|
-
| Endpoint
|
|
167
|
-
|
|
|
168
|
-
| `/api/agent-context.json?id=<recordingId>`
|
|
169
|
-
| `/api/agent-transcript.json?id=<recordingId>`
|
|
170
|
-
| `/api/agent-frame.jpg?id=<recordingId>&atMs=<ms>` | JPEG frame extracted from the video at the requested original-video timestamp
|
|
172
|
+
| Endpoint | Meaning |
|
|
173
|
+
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
174
|
+
| `/api/agent-context.json?id=<recordingId>` | Clip metadata, transcript summary, recommended frames, and API discovery links |
|
|
175
|
+
| `/api/agent-transcript.json?id=<recordingId>` | Timestamped transcript segments with `startMs`, `endMs`, `timestamp`, `range`, `text`, and optional `source` |
|
|
176
|
+
| `/api/agent-frame.jpg?id=<recordingId>&atMs=<ms>` | JPEG frame extracted from the video at the requested original-video timestamp |
|
|
171
177
|
|
|
172
178
|
These endpoints follow the same access model as `/api/public-recording`, plus a
|
|
173
179
|
temporary agent-link path:
|
|
@@ -34,6 +34,10 @@ Detailed media, meeting, dictation, editing, and sharing rules live in
|
|
|
34
34
|
download the original from Loom and use "Upload video".
|
|
35
35
|
- Native transcript first. Cleanup and title generation can run in the
|
|
36
36
|
background; do not hide a usable native transcript behind a failed cleanup.
|
|
37
|
+
- Use `request-transcript --recordingId=<id> --force=true` to retry a failed
|
|
38
|
+
transcript. Pass `--regenerate=true` to replace an existing ready transcript
|
|
39
|
+
from the stored recording media; if regeneration fails, keep the prior ready
|
|
40
|
+
transcript available.
|
|
37
41
|
- Dictation cleanup, Clip title/cleanup, and meeting summaries should pass
|
|
38
42
|
bounded `voiceContext` to the shared cleanup/transcription path when active
|
|
39
43
|
app context, learned vocabulary, user notes, or AGENTS.md preferences are
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "../server/lib/recordings.js";
|
|
25
25
|
import { setResumableSession } from "../server/lib/resumable-session.js";
|
|
26
26
|
import { shouldEnableStreamingUpload } from "../server/lib/streaming-upload-mode.js";
|
|
27
|
+
import { allowsSqlRecordingChunkScratch } from "../server/lib/video-storage.js";
|
|
27
28
|
import { createRecordingSchema } from "./lib/create-recording-schema.js";
|
|
28
29
|
import { DEFAULT_RECORDING_TITLE } from "./lib/title-source.js";
|
|
29
30
|
|
|
@@ -92,6 +93,7 @@ export default defineAction({
|
|
|
92
93
|
shouldEnableStreamingUpload({
|
|
93
94
|
client: args.streamingUploadClient,
|
|
94
95
|
mimeType: args.mimeType,
|
|
96
|
+
bufferedFallbackAvailable: allowsSqlRecordingChunkScratch(),
|
|
95
97
|
}) &&
|
|
96
98
|
uploadProvider?.resumable
|
|
97
99
|
) {
|
|
@@ -62,8 +62,10 @@ export const createRecordingSchema = z.object({
|
|
|
62
62
|
.describe("Height of the recording in pixels (may be 0 until finalized)"),
|
|
63
63
|
visibility: z
|
|
64
64
|
.enum(["private", "org", "public"])
|
|
65
|
-
.
|
|
66
|
-
.describe(
|
|
65
|
+
.default("public")
|
|
66
|
+
.describe(
|
|
67
|
+
"Initial share visibility for the recording (defaults to public)",
|
|
68
|
+
),
|
|
67
69
|
mimeType: z
|
|
68
70
|
.string()
|
|
69
71
|
.optional()
|
|
@@ -74,7 +76,7 @@ export const createRecordingSchema = z.object({
|
|
|
74
76
|
.boolean()
|
|
75
77
|
.optional()
|
|
76
78
|
.describe(
|
|
77
|
-
"Request the resumable streaming upload path.
|
|
79
|
+
"Request the resumable streaming upload path. Hosted deployments use it automatically because SQL chunk buffering is unavailable; local deployments can opt in with CLIPS_ENABLE_STREAMING_UPLOAD.",
|
|
78
80
|
),
|
|
79
81
|
streamingUploadClient: z
|
|
80
82
|
.enum(["desktop-native"])
|