@agent-native/core 0.112.0 → 0.113.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +4 -2
- package/corpus/core/CHANGELOG.md +22 -0
- package/corpus/core/docs/AGENTS.md +7 -0
- package/corpus/core/docs/content/integrations.mdx +63 -2
- package/corpus/core/docs/design/provider-integration-authority.md +117 -0
- package/corpus/core/package.json +6 -1
- package/corpus/core/scripts/materialize-source-corpus.mjs +9 -2
- package/corpus/core/src/cli/atomic-json-file.ts +324 -0
- package/corpus/core/src/cli/claude-code-participant.ts +515 -0
- package/corpus/core/src/cli/code-agent-executor.ts +18 -10
- package/corpus/core/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
- package/corpus/core/src/cli/code-agent-runs.ts +55 -62
- package/corpus/core/src/cli/codex-cli-participant.ts +496 -0
- package/corpus/core/src/cli/multi-frontier-runs.ts +1246 -135
- package/corpus/core/src/client/chat/ChatHistoryList.tsx +7 -404
- package/corpus/core/src/integrations/index.ts +10 -0
- package/corpus/core/src/integrations/slack-oauth.ts +1 -0
- package/corpus/core/src/integrations/webhook-delivery.ts +55 -0
- package/corpus/core/src/notifications/channels.ts +4 -10
- package/corpus/core/src/provider-api/actions/custom-provider-registration.ts +197 -0
- package/corpus/core/src/provider-api/actions/delete-staged-dataset.ts +3 -42
- package/corpus/core/src/provider-api/actions/list-staged-datasets.ts +3 -45
- package/corpus/core/src/provider-api/actions/provider-api-audit.ts +88 -0
- package/corpus/core/src/provider-api/actions/provider-api.ts +460 -0
- package/corpus/core/src/provider-api/actions/query-staged-dataset.ts +3 -140
- package/corpus/core/src/provider-api/actions/staged-datasets.ts +359 -0
- package/corpus/core/src/provider-api/gong.ts +108 -0
- package/corpus/core/src/scripts/docs/source-search.ts +2 -1
- package/corpus/core/src/server/google-oauth-credentials.ts +80 -3
- package/corpus/core/src/server/index.ts +6 -0
- package/corpus/core/src/styles/chat-history-list.css +1 -313
- package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/corpus/core/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/default/AGENTS.md +25 -18
- package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/headless/AGENTS.md +7 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/analytics/actions/delete-staged-dataset.ts +3 -23
- package/corpus/templates/analytics/actions/list-staged-datasets.ts +3 -27
- package/corpus/templates/analytics/actions/provider-api-catalog.ts +16 -19
- package/corpus/templates/analytics/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/analytics/actions/provider-api-request.ts +162 -185
- package/corpus/templates/analytics/actions/query-staged-dataset.ts +3 -47
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +52 -161
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +63 -160
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/brain/actions/_schemas.ts +13 -7
- package/corpus/templates/brain/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/brain/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/brain/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/brain/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/brain/actions/provider-api-request.ts +3 -37
- package/corpus/templates/brain/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/brain/actions/update-source.ts +5 -1
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +48 -154
- package/corpus/templates/brain/app/lib/brain.ts +55 -0
- package/corpus/templates/brain/app/lib/ingest-handoff.ts +35 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-can-now-join-configured-public-channels-befo.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-continue-across-the-configured-number-of.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-start-with-a-safe-four-week-window-and-r.md +6 -0
- package/corpus/templates/brain/server/lib/connectors.ts +53 -2
- package/corpus/templates/brain/server/lib/slack-channel-membership.ts +44 -0
- package/corpus/templates/brain/shared/slack-source-config.ts +60 -0
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/calendar/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/calendar/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/calendar/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/calendar/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/calendar/actions/provider-api-request.ts +3 -37
- package/corpus/templates/calendar/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/calendar/server/handlers/gong.ts +6 -61
- package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -8
- package/corpus/templates/calendar/server/lib/google-calendar.ts +7 -45
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/chat/AGENTS.md +5 -2
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/clips/AGENTS.md +11 -0
- package/corpus/templates/clips/actions/export-to-brain.ts +505 -171
- package/corpus/templates/clips/changelog/2026-07-19-ready-clips-transcripts-can-be-backfilled-to-brain-for-a-bou.md +6 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -1
- package/corpus/templates/clips/server/lib/google-calendar-client.ts +12 -22
- package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +9 -6
- package/corpus/templates/clips/server/lib/slack-oauth.ts +12 -47
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +2 -6
- package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +4 -4
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/content/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/content/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/content/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/content/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/content/actions/provider-api-request.ts +3 -37
- package/corpus/templates/content/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/design/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/design/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/design/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/design/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/design/actions/provider-api-request.ts +140 -164
- package/corpus/templates/design/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/dispatch/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/dispatch/actions/list-staged-datasets.ts +4 -30
- package/corpus/templates/dispatch/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/dispatch/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/dispatch/actions/provider-api-request.ts +138 -163
- package/corpus/templates/dispatch/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/forms/server/lib/integrations.ts +23 -22
- package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/mail/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/mail/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/mail/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/mail/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/mail/actions/provider-api-request.ts +146 -173
- package/corpus/templates/mail/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/mail/server/handlers/gong.ts +6 -61
- package/corpus/templates/mail/server/lib/google-auth.ts +10 -13
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +47 -156
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/slides/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/slides/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/slides/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/slides/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/slides/actions/provider-api-request.ts +145 -172
- package/corpus/templates/slides/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +11 -36
- package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +125 -0
- package/corpus/toolkit/README.md +60 -0
- package/corpus/toolkit/export-snapshot.json +35 -0
- package/corpus/toolkit/migration-manifest.json +4 -0
- package/corpus/toolkit/package.json +281 -0
- package/corpus/toolkit/scripts/finalize-build.mjs +55 -0
- package/corpus/toolkit/src/app-shell/header-actions.tsx +84 -0
- package/corpus/toolkit/src/app-shell/index.ts +1 -0
- package/corpus/toolkit/src/chat-history/ChatHistoryList.tsx +469 -0
- package/corpus/toolkit/src/chat-history/index.ts +7 -0
- package/corpus/toolkit/src/chat-history.css +314 -0
- package/corpus/toolkit/src/collab-ui/AgentPresenceChip.tsx +69 -0
- package/corpus/toolkit/src/collab-ui/LiveCursorOverlay.tsx +241 -0
- package/corpus/toolkit/src/collab-ui/PresenceBar.tsx +346 -0
- package/corpus/toolkit/src/collab-ui/RecentEditHighlights.tsx +225 -0
- package/corpus/toolkit/src/collab-ui/RemoteSelectionRings.tsx +227 -0
- package/corpus/toolkit/src/collab-ui/agent-identity.ts +16 -0
- package/corpus/toolkit/src/collab-ui/index.ts +39 -0
- package/corpus/toolkit/src/collab-ui/lead-client.ts +32 -0
- package/corpus/toolkit/src/collab-ui/types.ts +96 -0
- package/corpus/toolkit/src/composer/AgentComposerFrame.tsx +59 -0
- package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +983 -0
- package/corpus/toolkit/src/composer/MentionPopover.tsx +412 -0
- package/corpus/toolkit/src/composer/PastedTextChip.tsx +150 -0
- package/corpus/toolkit/src/composer/PromptComposer.tsx +718 -0
- package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +856 -0
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +2936 -0
- package/corpus/toolkit/src/composer/VoiceButton.tsx +255 -0
- package/corpus/toolkit/src/composer/asset-picker-url.ts +80 -0
- package/corpus/toolkit/src/composer/attachment-accept.ts +71 -0
- package/corpus/toolkit/src/composer/draft-key.ts +7 -0
- package/corpus/toolkit/src/composer/extensions/FileReference.tsx +55 -0
- package/corpus/toolkit/src/composer/extensions/MentionReference.tsx +93 -0
- package/corpus/toolkit/src/composer/extensions/SkillReference.tsx +56 -0
- package/corpus/toolkit/src/composer/index.ts +79 -0
- package/corpus/toolkit/src/composer/pasted-text.ts +160 -0
- package/corpus/toolkit/src/composer/prompt-attachments.ts +93 -0
- package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +64 -0
- package/corpus/toolkit/src/composer/realtime-voice-transcript.ts +134 -0
- package/corpus/toolkit/src/composer/runtime-adapters.tsx +343 -0
- package/corpus/toolkit/src/composer/types.ts +66 -0
- package/corpus/toolkit/src/composer/use-file-search.ts +64 -0
- package/corpus/toolkit/src/composer/use-mention-search.ts +90 -0
- package/corpus/toolkit/src/composer/use-skills.ts +45 -0
- package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +2117 -0
- package/corpus/toolkit/src/composer/useVoiceDictation.ts +1191 -0
- package/corpus/toolkit/src/context-ui/ContextMeter.tsx +101 -0
- package/corpus/toolkit/src/context-ui/ContextSegmentRow.tsx +184 -0
- package/corpus/toolkit/src/context-ui/ContextTreemap.tsx +125 -0
- package/corpus/toolkit/src/context-ui/ContextXRayPanel.tsx +377 -0
- package/corpus/toolkit/src/context-ui/format.ts +42 -0
- package/corpus/toolkit/src/context-ui/index.ts +15 -0
- package/corpus/toolkit/src/context-ui/types.ts +43 -0
- package/corpus/toolkit/src/design-tweaks/index.ts +1 -0
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +1084 -0
- package/corpus/toolkit/src/editor/BubbleToolbar.tsx +253 -0
- package/corpus/toolkit/src/editor/CodeBlockNode.tsx +254 -0
- package/corpus/toolkit/src/editor/DragHandle.ts +1437 -0
- package/corpus/toolkit/src/editor/ImageExtension.ts +306 -0
- package/corpus/toolkit/src/editor/RegistryBlockContext.tsx +107 -0
- package/corpus/toolkit/src/editor/RegistryBlockNode.tsx +652 -0
- package/corpus/toolkit/src/editor/RichMarkdownEditor.tsx +81 -0
- package/corpus/toolkit/src/editor/RunId.ts +61 -0
- package/corpus/toolkit/src/editor/SharedRichEditor.tsx +321 -0
- package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +310 -0
- package/corpus/toolkit/src/editor/extensions.ts +402 -0
- package/corpus/toolkit/src/editor/gfmDoc.ts +92 -0
- package/corpus/toolkit/src/editor/index.ts +93 -0
- package/corpus/toolkit/src/editor/registrySlashCommands.ts +131 -0
- package/corpus/toolkit/src/editor/surgical-apply.ts +192 -0
- package/corpus/toolkit/src/editor/useCollabReconcile.ts +589 -0
- package/corpus/toolkit/src/editor.css +510 -0
- package/corpus/toolkit/src/hooks/index.ts +2 -0
- package/corpus/toolkit/src/hooks/use-mobile.tsx +21 -0
- package/corpus/toolkit/src/hooks/use-toast.ts +188 -0
- package/corpus/toolkit/src/index.ts +8 -0
- package/corpus/toolkit/src/onboarding/index.ts +1 -0
- package/corpus/toolkit/src/provider.tsx +47 -0
- package/corpus/toolkit/src/sharing/VisibilityBadge.tsx +37 -0
- package/corpus/toolkit/src/sharing/index.ts +4 -0
- package/corpus/toolkit/src/styles.css +21 -0
- package/corpus/toolkit/src/ui/accordion.tsx +56 -0
- package/corpus/toolkit/src/ui/alert-dialog.tsx +141 -0
- package/corpus/toolkit/src/ui/alert.tsx +59 -0
- package/corpus/toolkit/src/ui/aspect-ratio.tsx +5 -0
- package/corpus/toolkit/src/ui/avatar.tsx +48 -0
- package/corpus/toolkit/src/ui/badge.tsx +37 -0
- package/corpus/toolkit/src/ui/breadcrumb.tsx +115 -0
- package/corpus/toolkit/src/ui/button.tsx +78 -0
- package/corpus/toolkit/src/ui/calendar.tsx +214 -0
- package/corpus/toolkit/src/ui/card.tsx +86 -0
- package/corpus/toolkit/src/ui/carousel.tsx +260 -0
- package/corpus/toolkit/src/ui/chart.tsx +375 -0
- package/corpus/toolkit/src/ui/checkbox.tsx +28 -0
- package/corpus/toolkit/src/ui/collapsible.tsx +9 -0
- package/corpus/toolkit/src/ui/command.tsx +174 -0
- package/corpus/toolkit/src/ui/context-menu.tsx +202 -0
- package/corpus/toolkit/src/ui/date-picker.tsx +64 -0
- package/corpus/toolkit/src/ui/dialog.tsx +146 -0
- package/corpus/toolkit/src/ui/drawer.tsx +126 -0
- package/corpus/toolkit/src/ui/dropdown-menu.tsx +218 -0
- package/corpus/toolkit/src/ui/form.tsx +177 -0
- package/corpus/toolkit/src/ui/hover-card.tsx +27 -0
- package/corpus/toolkit/src/ui/index.ts +52 -0
- package/corpus/toolkit/src/ui/input-otp.tsx +69 -0
- package/corpus/toolkit/src/ui/input.tsx +22 -0
- package/corpus/toolkit/src/ui/label.tsx +24 -0
- package/corpus/toolkit/src/ui/logo.tsx +36 -0
- package/corpus/toolkit/src/ui/menubar.tsx +235 -0
- package/corpus/toolkit/src/ui/navigation-menu.tsx +128 -0
- package/corpus/toolkit/src/ui/pagination.tsx +121 -0
- package/corpus/toolkit/src/ui/popover.tsx +44 -0
- package/corpus/toolkit/src/ui/progress.tsx +26 -0
- package/corpus/toolkit/src/ui/radio-group.tsx +42 -0
- package/corpus/toolkit/src/ui/resizable.tsx +43 -0
- package/corpus/toolkit/src/ui/scroll-area.tsx +46 -0
- package/corpus/toolkit/src/ui/select.tsx +158 -0
- package/corpus/toolkit/src/ui/separator.tsx +29 -0
- package/corpus/toolkit/src/ui/sheet.tsx +160 -0
- package/corpus/toolkit/src/ui/sidebar.tsx +777 -0
- package/corpus/toolkit/src/ui/skeleton.tsx +18 -0
- package/corpus/toolkit/src/ui/slider.tsx +26 -0
- package/corpus/toolkit/src/ui/sonner.tsx +58 -0
- package/corpus/toolkit/src/ui/spinner.tsx +17 -0
- package/corpus/toolkit/src/ui/switch.tsx +27 -0
- package/corpus/toolkit/src/ui/table.tsx +117 -0
- package/corpus/toolkit/src/ui/tabs.tsx +53 -0
- package/corpus/toolkit/src/ui/textarea.tsx +23 -0
- package/corpus/toolkit/src/ui/toast.tsx +127 -0
- package/corpus/toolkit/src/ui/toaster.tsx +33 -0
- package/corpus/toolkit/src/ui/toggle-group.tsx +59 -0
- package/corpus/toolkit/src/ui/toggle.tsx +43 -0
- package/corpus/toolkit/src/ui/tooltip.tsx +53 -0
- package/corpus/toolkit/src/ui/use-toast.ts +3 -0
- package/corpus/toolkit/src/utils.ts +8 -0
- package/corpus/toolkit/tsconfig.json +23 -0
- package/dist/cli/atomic-json-file.d.ts +30 -0
- package/dist/cli/atomic-json-file.d.ts.map +1 -0
- package/dist/cli/atomic-json-file.js +240 -0
- package/dist/cli/atomic-json-file.js.map +1 -0
- package/dist/cli/claude-code-participant.d.ts +77 -0
- package/dist/cli/claude-code-participant.d.ts.map +1 -0
- package/dist/cli/claude-code-participant.js +355 -0
- package/dist/cli/claude-code-participant.js.map +1 -0
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +18 -10
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.concurrent-worker.d.ts +2 -0
- package/dist/cli/code-agent-runs.concurrent-worker.d.ts.map +1 -0
- package/dist/cli/code-agent-runs.concurrent-worker.js +32 -0
- package/dist/cli/code-agent-runs.concurrent-worker.js.map +1 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +43 -56
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/codex-cli-participant.d.ts +77 -0
- package/dist/cli/codex-cli-participant.d.ts.map +1 -0
- package/dist/cli/codex-cli-participant.js +354 -0
- package/dist/cli/codex-cli-participant.js.map +1 -0
- package/dist/cli/multi-frontier-runs.d.ts +115 -0
- package/dist/cli/multi-frontier-runs.d.ts.map +1 -1
- package/dist/cli/multi-frontier-runs.js +831 -113
- package/dist/cli/multi-frontier-runs.js.map +1 -1
- package/dist/client/chat/ChatHistoryList.d.ts +1 -87
- package/dist/client/chat/ChatHistoryList.d.ts.map +1 -1
- package/dist/client/chat/ChatHistoryList.js +1 -82
- package/dist/client/chat/ChatHistoryList.js.map +1 -1
- package/dist/integrations/index.d.ts +2 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +2 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/slack-manifest.d.ts +1 -1
- package/dist/integrations/slack-oauth.d.ts +1 -1
- package/dist/integrations/slack-oauth.d.ts.map +1 -1
- package/dist/integrations/slack-oauth.js +1 -0
- package/dist/integrations/slack-oauth.js.map +1 -1
- package/dist/integrations/webhook-delivery.d.ts +23 -0
- package/dist/integrations/webhook-delivery.d.ts.map +1 -0
- package/dist/integrations/webhook-delivery.js +32 -0
- package/dist/integrations/webhook-delivery.js.map +1 -0
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +4 -9
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +136 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts.map +1 -0
- package/dist/provider-api/actions/custom-provider-registration.js +154 -0
- package/dist/provider-api/actions/custom-provider-registration.js.map +1 -0
- package/dist/provider-api/actions/delete-staged-dataset.d.ts +1 -1
- package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/delete-staged-dataset.js +3 -34
- package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -1
- package/dist/provider-api/actions/list-staged-datasets.d.ts +2 -3
- package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -1
- package/dist/provider-api/actions/list-staged-datasets.js +3 -40
- package/dist/provider-api/actions/list-staged-datasets.js.map +1 -1
- package/dist/provider-api/actions/provider-api-audit.d.ts +7 -0
- package/dist/provider-api/actions/provider-api-audit.d.ts.map +1 -0
- package/dist/provider-api/actions/provider-api-audit.js +74 -0
- package/dist/provider-api/actions/provider-api-audit.js.map +1 -0
- package/dist/provider-api/actions/provider-api.d.ts +370 -0
- package/dist/provider-api/actions/provider-api.d.ts.map +1 -0
- package/dist/provider-api/actions/provider-api.js +280 -0
- package/dist/provider-api/actions/provider-api.js.map +1 -0
- package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.js +3 -116
- package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
- package/dist/provider-api/actions/staged-datasets.d.ts +190 -0
- package/dist/provider-api/actions/staged-datasets.d.ts.map +1 -0
- package/dist/provider-api/actions/staged-datasets.js +210 -0
- package/dist/provider-api/actions/staged-datasets.js.map +1 -0
- package/dist/provider-api/gong.d.ts +44 -0
- package/dist/provider-api/gong.d.ts.map +1 -0
- package/dist/provider-api/gong.js +56 -0
- package/dist/provider-api/gong.js.map +1 -0
- package/dist/scripts/docs/source-search.d.ts +2 -1
- package/dist/scripts/docs/source-search.d.ts.map +1 -1
- package/dist/scripts/docs/source-search.js +2 -1
- package/dist/scripts/docs/source-search.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/google-oauth-credentials.d.ts +35 -0
- package/dist/server/google-oauth-credentials.d.ts.map +1 -1
- package/dist/server/google-oauth-credentials.js +48 -2
- package/dist/server/google-oauth-credentials.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/styles/chat-history-list.css +1 -313
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/chat/AGENTS.md +5 -2
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/dist/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/default/AGENTS.md +25 -18
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/headless/AGENTS.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/workspace-core/AGENTS.md +4 -0
- package/dist/templates/workspace-root/AGENTS.md +4 -0
- package/docs/AGENTS.md +7 -0
- package/docs/content/integrations.mdx +63 -2
- package/docs/design/provider-integration-authority.md +117 -0
- package/package.json +7 -2
- package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/chat/AGENTS.md +5 -2
- package/src/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/default/AGENTS.md +25 -18
- package/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/headless/AGENTS.md +7 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
- package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/workspace-core/AGENTS.md +4 -0
- package/src/templates/workspace-root/AGENTS.md +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
3
|
+
import { appendUniqueJsonLineAtomically, withFileLockSync, writeJsonFileAtomically, writeTextFileAtomically, } from "./atomic-json-file.js";
|
|
4
4
|
import { codeAgentStoreRoot } from "./code-agent-runs.js";
|
|
5
5
|
const TERMINAL_PHASES = new Set([
|
|
6
6
|
"completed",
|
|
@@ -8,6 +8,19 @@ const TERMINAL_PHASES = new Set([
|
|
|
8
8
|
"canceled",
|
|
9
9
|
]);
|
|
10
10
|
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,191}$/;
|
|
11
|
+
const SAFE_CONTENT_HASH = /^[a-f0-9]{64}$/;
|
|
12
|
+
const SAFE_FILE_REF = /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._/@+-]+$/;
|
|
13
|
+
const MAX_ARTIFACT_TITLE_BYTES = 280;
|
|
14
|
+
const MAX_ARTIFACT_SUMMARY_BYTES = 8 * 1024;
|
|
15
|
+
const MAX_ARTIFACT_FILE_REFS = 40;
|
|
16
|
+
const MAX_ARTIFACT_TEST_SUMMARIES = 40;
|
|
17
|
+
const MAX_ARTIFACT_TEST_NAME_BYTES = 280;
|
|
18
|
+
const MAX_ARTIFACT_TEST_SUMMARY_BYTES = 2 * 1024;
|
|
19
|
+
export const MAX_MULTI_FRONTIER_ARTIFACTS_PER_RUN = 200;
|
|
20
|
+
export const DEFAULT_MULTI_FRONTIER_PARTICIPANT_EVENT_RETENTION = {
|
|
21
|
+
maxEventCount: 2_000,
|
|
22
|
+
maxBytes: 1_000_000,
|
|
23
|
+
};
|
|
11
24
|
/** All multi-frontier state stays alongside the existing local Code store. */
|
|
12
25
|
export function multiFrontierRunsStoreRoot() {
|
|
13
26
|
return path.join(codeAgentStoreRoot(), "multi-frontier");
|
|
@@ -18,8 +31,14 @@ export function multiFrontierRunsDir() {
|
|
|
18
31
|
export function multiFrontierParticipantEventsDir() {
|
|
19
32
|
return path.join(multiFrontierRunsStoreRoot(), "events");
|
|
20
33
|
}
|
|
34
|
+
export function multiFrontierArtifactsDir() {
|
|
35
|
+
return path.join(multiFrontierRunsStoreRoot(), "artifacts");
|
|
36
|
+
}
|
|
21
37
|
export function createMultiFrontierRun(input) {
|
|
22
38
|
assertSafeId(input.collaborationId, "collaboration id");
|
|
39
|
+
if (input.workspaceId !== undefined) {
|
|
40
|
+
assertSafeId(input.workspaceId, "workspace id");
|
|
41
|
+
}
|
|
23
42
|
assertParticipants(input.participants);
|
|
24
43
|
if (getMultiFrontierRun(input.collaborationId)) {
|
|
25
44
|
throw new Error(`Multi-frontier run already exists: ${input.collaborationId}`);
|
|
@@ -28,6 +47,7 @@ export function createMultiFrontierRun(input) {
|
|
|
28
47
|
const state = {
|
|
29
48
|
schemaVersion: 1,
|
|
30
49
|
collaborationId: input.collaborationId,
|
|
50
|
+
...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),
|
|
31
51
|
phase: input.phase ?? "proposing",
|
|
32
52
|
participants: input.participants.map((participant) => ({
|
|
33
53
|
...participant,
|
|
@@ -41,6 +61,7 @@ export function createMultiFrontierRun(input) {
|
|
|
41
61
|
round: 1,
|
|
42
62
|
proposalIds: [],
|
|
43
63
|
reviewIds: [],
|
|
64
|
+
autoContinueAfterAgreement: input.autoContinueAfterAgreement ?? false,
|
|
44
65
|
createdAt: now,
|
|
45
66
|
updatedAt: now,
|
|
46
67
|
};
|
|
@@ -78,6 +99,7 @@ export function recoverMultiFrontierRun(state, input) {
|
|
|
78
99
|
phase: "paused",
|
|
79
100
|
participants: state.participants.map((participant) => ({
|
|
80
101
|
...participant,
|
|
102
|
+
role: "watchdog",
|
|
81
103
|
permission: "read_only",
|
|
82
104
|
status: participant.status === "running" ? "waiting" : participant.status,
|
|
83
105
|
})),
|
|
@@ -92,13 +114,67 @@ export function recoverMultiFrontierRun(state, input) {
|
|
|
92
114
|
}
|
|
93
115
|
/** Persist the recovery decision made by the main-process coordinator. */
|
|
94
116
|
export function recoverStoredMultiFrontierRun(collaborationId, input) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
117
|
+
return mutateStoredMultiFrontierRun(collaborationId, input.now, (current) => {
|
|
118
|
+
const recovered = recoverMultiFrontierRun(current, input);
|
|
119
|
+
return recovered === current ? current : recovered;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Applies one durable coordinator-owned state transition under the run lock.
|
|
124
|
+
* A state-shaped result retains proposal and review references by default;
|
|
125
|
+
* returning a full stored record is the explicit escape hatch for a
|
|
126
|
+
* coordinator operation that intentionally changes those references.
|
|
127
|
+
*/
|
|
128
|
+
export function transitionStoredMultiFrontierRun(collaborationId, updatedAt, transition) {
|
|
129
|
+
assertSafeId(collaborationId, "collaboration id");
|
|
130
|
+
assertTimestamp(updatedAt, "transition time");
|
|
131
|
+
if (typeof transition !== "function") {
|
|
132
|
+
throw new Error("Invalid multi-frontier coordinator transition.");
|
|
133
|
+
}
|
|
134
|
+
return mutateStoredMultiFrontierRun(collaborationId, updatedAt, (current) => {
|
|
135
|
+
const candidate = transition(cloneStoredMultiFrontierRun(current));
|
|
136
|
+
if (candidate === null)
|
|
137
|
+
return null;
|
|
138
|
+
if (isThenable(candidate)) {
|
|
139
|
+
throw new Error("Multi-frontier coordinator transitions must be synchronous.");
|
|
140
|
+
}
|
|
141
|
+
if (!candidate ||
|
|
142
|
+
typeof candidate !== "object" ||
|
|
143
|
+
Array.isArray(candidate)) {
|
|
144
|
+
throw new Error("Invalid multi-frontier coordinator transition state.");
|
|
145
|
+
}
|
|
146
|
+
const isFullStoredRun = hasStoredRunTimestamps(candidate);
|
|
147
|
+
if (!isFullStoredRun &&
|
|
148
|
+
(Object.hasOwn(candidate, "createdAt") ||
|
|
149
|
+
Object.hasOwn(candidate, "updatedAt"))) {
|
|
150
|
+
throw new Error("Invalid multi-frontier coordinator transition state.");
|
|
151
|
+
}
|
|
152
|
+
if (candidate.schemaVersion !== current.schemaVersion ||
|
|
153
|
+
candidate.collaborationId !== current.collaborationId) {
|
|
154
|
+
throw new Error("Multi-frontier coordinator transitions cannot change run identity.");
|
|
155
|
+
}
|
|
156
|
+
if (isFullStoredRun) {
|
|
157
|
+
if (!isCompleteMultiFrontierStoredRun(candidate)) {
|
|
158
|
+
throw new Error("Invalid multi-frontier coordinator transition state.");
|
|
159
|
+
}
|
|
160
|
+
if (candidate.createdAt !== current.createdAt) {
|
|
161
|
+
throw new Error("Multi-frontier coordinator transitions cannot change creation time.");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const nextState = {
|
|
165
|
+
...candidate,
|
|
166
|
+
...(isFullStoredRun
|
|
167
|
+
? {}
|
|
168
|
+
: {
|
|
169
|
+
proposalIds: [...current.proposalIds],
|
|
170
|
+
reviewIds: [...current.reviewIds],
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
if (!isCompleteMultiFrontierRunState(nextState)) {
|
|
174
|
+
throw new Error("Invalid multi-frontier coordinator transition state.");
|
|
175
|
+
}
|
|
176
|
+
return nextState;
|
|
177
|
+
});
|
|
102
178
|
}
|
|
103
179
|
/**
|
|
104
180
|
* Coordinators must call this before applying a participant event. Write
|
|
@@ -184,20 +260,12 @@ export function activateMultiFrontierDriver(state, participantId) {
|
|
|
184
260
|
/** Persist the first explicit driver activation from the main-process coordinator. */
|
|
185
261
|
export function activateStoredMultiFrontierDriver(collaborationId, participantId, now = new Date().toISOString()) {
|
|
186
262
|
assertTimestamp(now, "activation time");
|
|
187
|
-
|
|
188
|
-
if (!current)
|
|
189
|
-
return null;
|
|
190
|
-
const activated = activateMultiFrontierDriver(current, participantId);
|
|
191
|
-
return activated ? writeNextStoredRun(current, activated, now) : null;
|
|
263
|
+
return mutateStoredMultiFrontierRun(collaborationId, now, (current) => activateMultiFrontierDriver(current, participantId));
|
|
192
264
|
}
|
|
193
265
|
/** Persist an explicit driver reactivation after the human-approved resume. */
|
|
194
266
|
export function reactivateStoredMultiFrontierDriver(collaborationId, participantId, now = new Date().toISOString()) {
|
|
195
267
|
assertTimestamp(now, "reactivation time");
|
|
196
|
-
|
|
197
|
-
if (!current)
|
|
198
|
-
return null;
|
|
199
|
-
const reactivated = reactivateMultiFrontierDriver(current, participantId);
|
|
200
|
-
return reactivated ? writeNextStoredRun(current, reactivated, now) : null;
|
|
268
|
+
return mutateStoredMultiFrontierRun(collaborationId, now, (current) => reactivateMultiFrontierDriver(current, participantId));
|
|
201
269
|
}
|
|
202
270
|
/**
|
|
203
271
|
* Appends an idempotent participant event after the coordinator has fenced it.
|
|
@@ -210,51 +278,141 @@ export function appendMultiFrontierParticipantEvent(input) {
|
|
|
210
278
|
assertSafeId(input.participantId, "participant id");
|
|
211
279
|
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
212
280
|
assertTimestamp(createdAt, "event time");
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
281
|
+
const runPath = multiFrontierRunPath(input.collaborationId);
|
|
282
|
+
return withFileLockSync(runPath, () => {
|
|
283
|
+
const current = readStoredRun(runPath);
|
|
284
|
+
if (!current)
|
|
285
|
+
return { accepted: false, reason: "missing-run", run: null };
|
|
286
|
+
const existing = listMultiFrontierParticipantEvents(input.collaborationId).find((event) => event.id === input.id);
|
|
287
|
+
if (existing) {
|
|
288
|
+
if (!isMatchingParticipantEvent(existing, input)) {
|
|
289
|
+
return { accepted: false, reason: "event-conflict", run: current };
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
accepted: true,
|
|
293
|
+
deduplicated: true,
|
|
294
|
+
event: existing,
|
|
295
|
+
run: current,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
if (!current.participants.some((candidate) => candidate.participantId === input.participantId)) {
|
|
299
|
+
return { accepted: false, reason: "missing-participant", run: current };
|
|
300
|
+
}
|
|
301
|
+
if (!canApplyMultiFrontierParticipantEvent(current, input)) {
|
|
302
|
+
return { accepted: false, reason: "stale-driver", run: current };
|
|
220
303
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
304
|
+
const participant = current.participants.find((candidate) => candidate.participantId === input.participantId);
|
|
305
|
+
if (input.status !== undefined &&
|
|
306
|
+
(participant?.status === "completed" ||
|
|
307
|
+
participant?.status === "failed") &&
|
|
308
|
+
input.status !== participant.status) {
|
|
309
|
+
return { accepted: false, reason: "terminal-participant", run: current };
|
|
310
|
+
}
|
|
311
|
+
const event = {
|
|
312
|
+
schemaVersion: 1,
|
|
313
|
+
id: input.id,
|
|
314
|
+
collaborationId: input.collaborationId,
|
|
315
|
+
participantId: input.participantId,
|
|
316
|
+
permission: input.permission,
|
|
317
|
+
createdAt,
|
|
318
|
+
...(input.generation === undefined
|
|
319
|
+
? {}
|
|
320
|
+
: { generation: input.generation }),
|
|
321
|
+
...(input.status === undefined ? {} : { status: input.status }),
|
|
226
322
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
323
|
+
appendParticipantEvent(event);
|
|
324
|
+
compactParticipantEventJournal(input.collaborationId);
|
|
325
|
+
const status = input.status;
|
|
326
|
+
const participants = status
|
|
327
|
+
? current.participants.map((candidate) => candidate.participantId === input.participantId
|
|
328
|
+
? { ...candidate, status }
|
|
329
|
+
: candidate)
|
|
330
|
+
: current.participants;
|
|
331
|
+
const run = writeNextStoredRun(current, { ...current, participants }, createdAt);
|
|
332
|
+
return { accepted: true, deduplicated: false, event, run };
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Appends a bounded proposal, review, or checkpoint index record. The
|
|
337
|
+
* coordinator owns both the artifact and its run-state reference under one
|
|
338
|
+
* run lock, so a reader never observes a durable reference without its record.
|
|
339
|
+
*/
|
|
340
|
+
export function appendMultiFrontierArtifact(input) {
|
|
341
|
+
assertArtifactInput(input);
|
|
342
|
+
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
343
|
+
assertTimestamp(createdAt, "artifact time");
|
|
344
|
+
const artifact = toPersistedArtifact(input, createdAt);
|
|
345
|
+
const runPath = multiFrontierRunPath(input.collaborationId);
|
|
346
|
+
return withFileLockSync(runPath, () => {
|
|
347
|
+
const current = readStoredRun(runPath);
|
|
348
|
+
if (!current)
|
|
349
|
+
return { accepted: false, reason: "missing-run", run: null };
|
|
350
|
+
if (input.participantId !== undefined &&
|
|
351
|
+
!current.participants.some((participant) => participant.participantId === input.participantId)) {
|
|
352
|
+
return { accepted: false, reason: "missing-participant", run: current };
|
|
353
|
+
}
|
|
354
|
+
if (input.supersedesArtifactId !== undefined &&
|
|
355
|
+
!readStoredArtifact(multiFrontierArtifactPath(input.collaborationId, input.supersedesArtifactId))) {
|
|
356
|
+
return {
|
|
357
|
+
accepted: false,
|
|
358
|
+
reason: "missing-superseded-artifact",
|
|
359
|
+
run: current,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const artifactPath = multiFrontierArtifactPath(input.collaborationId, input.id);
|
|
363
|
+
const existing = readStoredArtifact(artifactPath);
|
|
364
|
+
if (existing) {
|
|
365
|
+
if (!isMatchingArtifact(existing, artifact)) {
|
|
366
|
+
return { accepted: false, reason: "artifact-conflict", run: current };
|
|
367
|
+
}
|
|
368
|
+
const ids = artifactIdsForKind(current, existing.kind);
|
|
369
|
+
const run = ids.includes(existing.id)
|
|
370
|
+
? current
|
|
371
|
+
: writeNextStoredRun(current, withArtifactId(current, existing.kind, existing.id), new Date().toISOString());
|
|
372
|
+
return {
|
|
373
|
+
accepted: true,
|
|
374
|
+
deduplicated: true,
|
|
375
|
+
artifact: existing,
|
|
376
|
+
run,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (fs.existsSync(artifactPath)) {
|
|
380
|
+
return { accepted: false, reason: "artifact-conflict", run: current };
|
|
381
|
+
}
|
|
382
|
+
if (listMultiFrontierArtifacts(input.collaborationId).length >=
|
|
383
|
+
MAX_MULTI_FRONTIER_ARTIFACTS_PER_RUN) {
|
|
384
|
+
return {
|
|
385
|
+
accepted: false,
|
|
386
|
+
reason: "artifact-limit-reached",
|
|
387
|
+
run: current,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
writeJsonFileAtomically(artifactPath, artifact, { mode: 0o600 });
|
|
391
|
+
const ids = artifactIdsForKind(current, artifact.kind);
|
|
392
|
+
const next = ids.includes(artifact.id)
|
|
393
|
+
? current
|
|
394
|
+
: withArtifactId(current, artifact.kind, artifact.id);
|
|
395
|
+
const run = writeNextStoredRun(current, next, createdAt);
|
|
396
|
+
return { accepted: true, deduplicated: false, artifact, run };
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
export function getMultiFrontierArtifact(collaborationId, artifactId) {
|
|
400
|
+
if (!SAFE_ID.test(collaborationId) || !SAFE_ID.test(artifactId))
|
|
401
|
+
return null;
|
|
402
|
+
return readStoredArtifact(multiFrontierArtifactPath(collaborationId, artifactId));
|
|
403
|
+
}
|
|
404
|
+
export function listMultiFrontierArtifacts(collaborationId) {
|
|
405
|
+
if (!SAFE_ID.test(collaborationId))
|
|
406
|
+
return [];
|
|
407
|
+
const directory = multiFrontierArtifactsRunDir(collaborationId);
|
|
408
|
+
if (!fs.existsSync(directory))
|
|
409
|
+
return [];
|
|
410
|
+
return fs
|
|
411
|
+
.readdirSync(directory)
|
|
412
|
+
.filter((file) => file.endsWith(".json"))
|
|
413
|
+
.map((file) => readStoredArtifact(path.join(directory, file)))
|
|
414
|
+
.filter((artifact) => artifact !== null)
|
|
415
|
+
.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
258
416
|
}
|
|
259
417
|
export function listMultiFrontierParticipantEvents(collaborationId) {
|
|
260
418
|
if (!SAFE_ID.test(collaborationId))
|
|
@@ -269,21 +427,36 @@ export function listMultiFrontierParticipantEvents(collaborationId) {
|
|
|
269
427
|
.map(readParticipantEvent)
|
|
270
428
|
.filter((event) => event !== null);
|
|
271
429
|
}
|
|
430
|
+
export function getMultiFrontierParticipantEventRetention(collaborationId) {
|
|
431
|
+
if (!SAFE_ID.test(collaborationId))
|
|
432
|
+
return null;
|
|
433
|
+
return readParticipantEventRetention(multiFrontierParticipantEventRetentionPath(collaborationId));
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Coordinator maintenance for a bounded event journal. It deliberately keeps
|
|
437
|
+
* the newest contiguous tail and a replay marker instead of deleting run state.
|
|
438
|
+
*/
|
|
439
|
+
export function compactMultiFrontierParticipantEvents(collaborationId, limits = DEFAULT_MULTI_FRONTIER_PARTICIPANT_EVENT_RETENTION) {
|
|
440
|
+
assertSafeId(collaborationId, "collaboration id");
|
|
441
|
+
assertRetentionLimits(limits);
|
|
442
|
+
const runPath = multiFrontierRunPath(collaborationId);
|
|
443
|
+
return withFileLockSync(runPath, () => {
|
|
444
|
+
if (!readStoredRun(runPath))
|
|
445
|
+
return null;
|
|
446
|
+
return compactParticipantEventJournal(collaborationId, limits);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
272
449
|
function writeMultiFrontierRun(run) {
|
|
273
|
-
|
|
274
|
-
writeFileAtomically(multiFrontierRunPath(run.collaborationId), `${JSON.stringify(run, null, 2)}\n`);
|
|
450
|
+
writeJsonFileAtomically(multiFrontierRunPath(run.collaborationId), run);
|
|
275
451
|
}
|
|
276
452
|
function writeNewMultiFrontierRun(run) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
fs.
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
if (error.code === "EEXIST") {
|
|
453
|
+
const filePath = multiFrontierRunPath(run.collaborationId);
|
|
454
|
+
withFileLockSync(filePath, () => {
|
|
455
|
+
if (fs.existsSync(filePath)) {
|
|
283
456
|
throw new Error(`Multi-frontier run already exists: ${run.collaborationId}`);
|
|
284
457
|
}
|
|
285
|
-
|
|
286
|
-
}
|
|
458
|
+
writeJsonFileAtomically(filePath, run);
|
|
459
|
+
});
|
|
287
460
|
}
|
|
288
461
|
function writeNextStoredRun(current, nextState, updatedAt) {
|
|
289
462
|
const next = {
|
|
@@ -295,8 +468,64 @@ function writeNextStoredRun(current, nextState, updatedAt) {
|
|
|
295
468
|
return next;
|
|
296
469
|
}
|
|
297
470
|
function appendParticipantEvent(event) {
|
|
298
|
-
|
|
299
|
-
|
|
471
|
+
appendUniqueJsonLineAtomically(multiFrontierParticipantEventsPath(event.collaborationId), event, readParticipantEventValue, { mode: 0o600 });
|
|
472
|
+
}
|
|
473
|
+
function compactParticipantEventJournal(collaborationId, limits = DEFAULT_MULTI_FRONTIER_PARTICIPANT_EVENT_RETENTION) {
|
|
474
|
+
const eventPath = multiFrontierParticipantEventsPath(collaborationId);
|
|
475
|
+
return withFileLockSync(eventPath, () => {
|
|
476
|
+
const previous = readParticipantEventRetention(multiFrontierParticipantEventRetentionPath(collaborationId));
|
|
477
|
+
const events = listMultiFrontierParticipantEvents(collaborationId);
|
|
478
|
+
const retained = [];
|
|
479
|
+
let retainedByteCount = 0;
|
|
480
|
+
for (const event of [...events].reverse()) {
|
|
481
|
+
const bytes = eventLineByteLength(event);
|
|
482
|
+
if (retained.length >= limits.maxEventCount ||
|
|
483
|
+
retainedByteCount + bytes > limits.maxBytes) {
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
retained.push(event);
|
|
487
|
+
retainedByteCount += bytes;
|
|
488
|
+
}
|
|
489
|
+
retained.reverse();
|
|
490
|
+
const retainedIds = new Set(retained.map((event) => event.id));
|
|
491
|
+
const dropped = events.filter((event) => !retainedIds.has(event.id));
|
|
492
|
+
if (dropped.length > 0) {
|
|
493
|
+
writeTextFileAtomically(eventPath, retained.map((event) => `${JSON.stringify(event)}\n`).join(""));
|
|
494
|
+
}
|
|
495
|
+
const retention = {
|
|
496
|
+
schemaVersion: 1,
|
|
497
|
+
retainedEventCount: retained.length,
|
|
498
|
+
retainedByteCount,
|
|
499
|
+
droppedEventCount: (previous?.droppedEventCount ?? 0) + dropped.length,
|
|
500
|
+
droppedByteCount: (previous?.droppedByteCount ?? 0) +
|
|
501
|
+
dropped.reduce((total, event) => total + eventLineByteLength(event), 0),
|
|
502
|
+
truncated: (previous?.truncated ?? false) || dropped.length > 0,
|
|
503
|
+
replay: {
|
|
504
|
+
requiresSnapshot: (previous?.truncated ?? false) || dropped.length > 0,
|
|
505
|
+
...(retained[0] ? { firstRetainedEventId: retained[0].id } : {}),
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
writeJsonFileAtomically(multiFrontierParticipantEventRetentionPath(collaborationId), retention, { mode: 0o600 });
|
|
509
|
+
return retention;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function mutateStoredMultiFrontierRun(collaborationId, updatedAt, mutate) {
|
|
513
|
+
const filePath = multiFrontierRunPath(collaborationId);
|
|
514
|
+
return withFileLockSync(filePath, () => {
|
|
515
|
+
const current = readStoredRun(filePath);
|
|
516
|
+
if (!current)
|
|
517
|
+
return null;
|
|
518
|
+
const nextState = mutate(current);
|
|
519
|
+
if (!nextState)
|
|
520
|
+
return null;
|
|
521
|
+
const next = {
|
|
522
|
+
...nextState,
|
|
523
|
+
createdAt: current.createdAt,
|
|
524
|
+
updatedAt,
|
|
525
|
+
};
|
|
526
|
+
writeMultiFrontierRun(next);
|
|
527
|
+
return next;
|
|
528
|
+
});
|
|
300
529
|
}
|
|
301
530
|
function multiFrontierRunPath(collaborationId) {
|
|
302
531
|
return path.join(multiFrontierRunsDir(), `${collaborationId}.json`);
|
|
@@ -304,41 +533,86 @@ function multiFrontierRunPath(collaborationId) {
|
|
|
304
533
|
function multiFrontierParticipantEventsPath(collaborationId) {
|
|
305
534
|
return path.join(multiFrontierParticipantEventsDir(), `${collaborationId}.jsonl`);
|
|
306
535
|
}
|
|
536
|
+
function multiFrontierParticipantEventRetentionPath(collaborationId) {
|
|
537
|
+
return path.join(multiFrontierParticipantEventsDir(), `${collaborationId}.retention.json`);
|
|
538
|
+
}
|
|
539
|
+
function multiFrontierArtifactsRunDir(collaborationId) {
|
|
540
|
+
return path.join(multiFrontierArtifactsDir(), collaborationId);
|
|
541
|
+
}
|
|
542
|
+
function multiFrontierArtifactPath(collaborationId, artifactId) {
|
|
543
|
+
return path.join(multiFrontierArtifactsRunDir(collaborationId), `${artifactId}.json`);
|
|
544
|
+
}
|
|
307
545
|
function readStoredRun(filePath) {
|
|
308
546
|
try {
|
|
309
547
|
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const run = raw;
|
|
313
|
-
if (run.schemaVersion !== 1 ||
|
|
314
|
-
typeof run.collaborationId !== "string" ||
|
|
315
|
-
!SAFE_ID.test(run.collaborationId) ||
|
|
316
|
-
!isPhase(run.phase) ||
|
|
317
|
-
!isStoredRunParticipantsAndDriverValid(run.participants, run.driver) ||
|
|
318
|
-
!isApproval(run.approval) ||
|
|
319
|
-
!Array.isArray(run.checkpointIds) ||
|
|
320
|
-
!run.checkpointIds.every((checkpointId) => typeof checkpointId === "string") ||
|
|
321
|
-
typeof run.round !== "number" ||
|
|
322
|
-
!Number.isInteger(run.round) ||
|
|
323
|
-
run.round < 1 ||
|
|
324
|
-
!isStringArray(run.proposalIds) ||
|
|
325
|
-
!isStringArray(run.reviewIds) ||
|
|
326
|
-
(run.recovery !== undefined && !isRecovery(run.recovery)) ||
|
|
327
|
-
typeof run.createdAt !== "string" ||
|
|
328
|
-
Number.isNaN(Date.parse(run.createdAt)) ||
|
|
329
|
-
typeof run.updatedAt !== "string" ||
|
|
330
|
-
Number.isNaN(Date.parse(run.updatedAt))) {
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
return run;
|
|
548
|
+
const normalized = normalizeStoredRunDefaults(raw);
|
|
549
|
+
return isCompleteMultiFrontierStoredRun(normalized) ? normalized : null;
|
|
334
550
|
}
|
|
335
551
|
catch {
|
|
336
552
|
return null;
|
|
337
553
|
}
|
|
338
554
|
}
|
|
555
|
+
function isCompleteMultiFrontierStoredRun(value) {
|
|
556
|
+
if (!hasStoredRunTimestamps(value))
|
|
557
|
+
return false;
|
|
558
|
+
return (isCompleteMultiFrontierRunState(value) &&
|
|
559
|
+
!Number.isNaN(Date.parse(value.createdAt)) &&
|
|
560
|
+
!Number.isNaN(Date.parse(value.updatedAt)));
|
|
561
|
+
}
|
|
562
|
+
function isCompleteMultiFrontierRunState(value) {
|
|
563
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
564
|
+
return false;
|
|
565
|
+
const run = value;
|
|
566
|
+
return (run.schemaVersion === 1 &&
|
|
567
|
+
typeof run.collaborationId === "string" &&
|
|
568
|
+
SAFE_ID.test(run.collaborationId) &&
|
|
569
|
+
(run.workspaceId === undefined ||
|
|
570
|
+
(typeof run.workspaceId === "string" && SAFE_ID.test(run.workspaceId))) &&
|
|
571
|
+
isPhase(run.phase) &&
|
|
572
|
+
isStoredRunParticipantsAndDriverValid(run.participants, run.driver) &&
|
|
573
|
+
isApproval(run.approval) &&
|
|
574
|
+
Array.isArray(run.checkpointIds) &&
|
|
575
|
+
run.checkpointIds.every((checkpointId) => typeof checkpointId === "string") &&
|
|
576
|
+
typeof run.round === "number" &&
|
|
577
|
+
Number.isInteger(run.round) &&
|
|
578
|
+
run.round >= 1 &&
|
|
579
|
+
isStringArray(run.proposalIds) &&
|
|
580
|
+
isStringArray(run.reviewIds) &&
|
|
581
|
+
typeof run.autoContinueAfterAgreement === "boolean" &&
|
|
582
|
+
(run.recovery === undefined || isRecovery(run.recovery)));
|
|
583
|
+
}
|
|
584
|
+
function normalizeStoredRunDefaults(value) {
|
|
585
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
586
|
+
return value;
|
|
587
|
+
if (!Object.hasOwn(value, "autoContinueAfterAgreement")) {
|
|
588
|
+
return { ...value, autoContinueAfterAgreement: false };
|
|
589
|
+
}
|
|
590
|
+
return value;
|
|
591
|
+
}
|
|
592
|
+
function hasStoredRunTimestamps(value) {
|
|
593
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
594
|
+
return false;
|
|
595
|
+
const run = value;
|
|
596
|
+
return typeof run.createdAt === "string" && typeof run.updatedAt === "string";
|
|
597
|
+
}
|
|
598
|
+
function cloneStoredMultiFrontierRun(run) {
|
|
599
|
+
return JSON.parse(JSON.stringify(run));
|
|
600
|
+
}
|
|
601
|
+
function isThenable(value) {
|
|
602
|
+
return (Boolean(value) &&
|
|
603
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
604
|
+
typeof value.then === "function");
|
|
605
|
+
}
|
|
339
606
|
function readParticipantEvent(line) {
|
|
340
607
|
try {
|
|
341
|
-
|
|
608
|
+
return readParticipantEventValue(JSON.parse(line));
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function readParticipantEventValue(raw) {
|
|
615
|
+
try {
|
|
342
616
|
if (!raw || typeof raw !== "object")
|
|
343
617
|
return null;
|
|
344
618
|
const event = raw;
|
|
@@ -360,6 +634,465 @@ function readParticipantEvent(line) {
|
|
|
360
634
|
return null;
|
|
361
635
|
}
|
|
362
636
|
}
|
|
637
|
+
function readStoredArtifact(filePath) {
|
|
638
|
+
try {
|
|
639
|
+
return readArtifactValue(JSON.parse(fs.readFileSync(filePath, "utf-8")));
|
|
640
|
+
}
|
|
641
|
+
catch {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function readArtifactValue(raw) {
|
|
646
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
647
|
+
return null;
|
|
648
|
+
const artifact = raw;
|
|
649
|
+
if (artifact.schemaVersion !== 1 ||
|
|
650
|
+
!isSafeId(artifact.id) ||
|
|
651
|
+
!isSafeId(artifact.collaborationId) ||
|
|
652
|
+
!isArtifactKind(artifact.kind) ||
|
|
653
|
+
typeof artifact.createdAt !== "string" ||
|
|
654
|
+
Number.isNaN(Date.parse(artifact.createdAt)) ||
|
|
655
|
+
(artifact.participantId !== undefined &&
|
|
656
|
+
!isSafeId(artifact.participantId)) ||
|
|
657
|
+
!isBoundedText(artifact.title, MAX_ARTIFACT_TITLE_BYTES) ||
|
|
658
|
+
!isArtifactSummary(artifact.summary) ||
|
|
659
|
+
(artifact.supersedesArtifactId !== undefined &&
|
|
660
|
+
!isSafeId(artifact.supersedesArtifactId)) ||
|
|
661
|
+
(artifact.contentHash !== undefined &&
|
|
662
|
+
!SAFE_CONTENT_HASH.test(artifact.contentHash)) ||
|
|
663
|
+
!isArtifactFileRefs(artifact.fileRefs) ||
|
|
664
|
+
!isArtifactTestSummaries(artifact.testSummary) ||
|
|
665
|
+
!isOrchestrationArtifact(artifact.orchestration)) {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
return artifact;
|
|
669
|
+
}
|
|
670
|
+
function readParticipantEventRetention(filePath) {
|
|
671
|
+
try {
|
|
672
|
+
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
673
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
674
|
+
return null;
|
|
675
|
+
const retention = raw;
|
|
676
|
+
if (retention.schemaVersion !== 1 ||
|
|
677
|
+
!isNonNegativeInteger(retention.retainedEventCount) ||
|
|
678
|
+
!isNonNegativeInteger(retention.retainedByteCount) ||
|
|
679
|
+
!isNonNegativeInteger(retention.droppedEventCount) ||
|
|
680
|
+
!isNonNegativeInteger(retention.droppedByteCount) ||
|
|
681
|
+
typeof retention.truncated !== "boolean" ||
|
|
682
|
+
!retention.replay ||
|
|
683
|
+
typeof retention.replay !== "object" ||
|
|
684
|
+
typeof retention.replay.requiresSnapshot !== "boolean" ||
|
|
685
|
+
(retention.replay.firstRetainedEventId !== undefined &&
|
|
686
|
+
!isSafeId(retention.replay.firstRetainedEventId))) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
return retention;
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function assertArtifactInput(input) {
|
|
696
|
+
assertAllowlistedArtifactInput(input);
|
|
697
|
+
assertSafeId(input.id, "artifact id");
|
|
698
|
+
assertSafeId(input.collaborationId, "collaboration id");
|
|
699
|
+
if (!isArtifactKind(input.kind))
|
|
700
|
+
throw new Error("Invalid multi-frontier artifact kind.");
|
|
701
|
+
if (input.participantId !== undefined) {
|
|
702
|
+
assertSafeId(input.participantId, "participant id");
|
|
703
|
+
}
|
|
704
|
+
assertBoundedText(input.title, MAX_ARTIFACT_TITLE_BYTES, "artifact title");
|
|
705
|
+
if (!isArtifactSummary(input.summary)) {
|
|
706
|
+
throw new Error("Invalid multi-frontier artifact summary.");
|
|
707
|
+
}
|
|
708
|
+
if (input.supersedesArtifactId !== undefined) {
|
|
709
|
+
assertSafeId(input.supersedesArtifactId, "superseded artifact id");
|
|
710
|
+
}
|
|
711
|
+
if (input.contentHash !== undefined &&
|
|
712
|
+
!SAFE_CONTENT_HASH.test(input.contentHash)) {
|
|
713
|
+
throw new Error("Invalid multi-frontier artifact content hash.");
|
|
714
|
+
}
|
|
715
|
+
if (!isArtifactFileRefs(input.fileRefs)) {
|
|
716
|
+
throw new Error("Invalid multi-frontier artifact file references.");
|
|
717
|
+
}
|
|
718
|
+
if (!isArtifactTestSummaries(input.testSummary)) {
|
|
719
|
+
throw new Error("Invalid multi-frontier artifact test summary.");
|
|
720
|
+
}
|
|
721
|
+
if (!isOrchestrationArtifact(input.orchestration)) {
|
|
722
|
+
throw new Error("Invalid multi-frontier orchestration artifact.");
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function assertAllowlistedArtifactInput(input) {
|
|
726
|
+
const allowed = new Set([
|
|
727
|
+
"id",
|
|
728
|
+
"collaborationId",
|
|
729
|
+
"kind",
|
|
730
|
+
"createdAt",
|
|
731
|
+
"participantId",
|
|
732
|
+
"title",
|
|
733
|
+
"summary",
|
|
734
|
+
"supersedesArtifactId",
|
|
735
|
+
"contentHash",
|
|
736
|
+
"fileRefs",
|
|
737
|
+
"testSummary",
|
|
738
|
+
"orchestration",
|
|
739
|
+
]);
|
|
740
|
+
if (!input ||
|
|
741
|
+
typeof input !== "object" ||
|
|
742
|
+
Array.isArray(input) ||
|
|
743
|
+
Object.keys(input).some((key) => !allowed.has(key))) {
|
|
744
|
+
throw new Error("Multi-frontier artifacts only accept allowlisted summary fields.");
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function toPersistedArtifact(input, createdAt) {
|
|
748
|
+
return {
|
|
749
|
+
schemaVersion: 1,
|
|
750
|
+
id: input.id,
|
|
751
|
+
collaborationId: input.collaborationId,
|
|
752
|
+
kind: input.kind,
|
|
753
|
+
createdAt,
|
|
754
|
+
title: input.title,
|
|
755
|
+
summary: input.summary,
|
|
756
|
+
...(input.participantId === undefined
|
|
757
|
+
? {}
|
|
758
|
+
: { participantId: input.participantId }),
|
|
759
|
+
...(input.supersedesArtifactId === undefined
|
|
760
|
+
? {}
|
|
761
|
+
: { supersedesArtifactId: input.supersedesArtifactId }),
|
|
762
|
+
...(input.contentHash === undefined
|
|
763
|
+
? {}
|
|
764
|
+
: { contentHash: input.contentHash }),
|
|
765
|
+
...(input.fileRefs === undefined ? {} : { fileRefs: [...input.fileRefs] }),
|
|
766
|
+
...(input.testSummary === undefined
|
|
767
|
+
? {}
|
|
768
|
+
: {
|
|
769
|
+
testSummary: input.testSummary.map((summary) => ({ ...summary })),
|
|
770
|
+
}),
|
|
771
|
+
...(input.orchestration === undefined
|
|
772
|
+
? {}
|
|
773
|
+
: { orchestration: cloneOrchestrationArtifact(input.orchestration) }),
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function artifactIdsForKind(run, kind) {
|
|
777
|
+
if (kind === "proposal")
|
|
778
|
+
return run.proposalIds;
|
|
779
|
+
if (kind === "review")
|
|
780
|
+
return run.reviewIds;
|
|
781
|
+
return run.checkpointIds;
|
|
782
|
+
}
|
|
783
|
+
function withArtifactId(run, kind, artifactId) {
|
|
784
|
+
if (kind === "proposal") {
|
|
785
|
+
return { ...run, proposalIds: [...run.proposalIds, artifactId] };
|
|
786
|
+
}
|
|
787
|
+
if (kind === "review") {
|
|
788
|
+
return { ...run, reviewIds: [...run.reviewIds, artifactId] };
|
|
789
|
+
}
|
|
790
|
+
return { ...run, checkpointIds: [...run.checkpointIds, artifactId] };
|
|
791
|
+
}
|
|
792
|
+
function isMatchingArtifact(existing, candidate) {
|
|
793
|
+
return JSON.stringify(existing) === JSON.stringify(candidate);
|
|
794
|
+
}
|
|
795
|
+
function isArtifactKind(value) {
|
|
796
|
+
return value === "proposal" || value === "review" || value === "checkpoint";
|
|
797
|
+
}
|
|
798
|
+
function isArtifactSummary(value) {
|
|
799
|
+
return (isBoundedText(value, MAX_ARTIFACT_SUMMARY_BYTES) &&
|
|
800
|
+
!/(?:^|\n)diff --git\s|(?:^|\n)@@\s+-\d+/m.test(value));
|
|
801
|
+
}
|
|
802
|
+
function isArtifactFileRefs(value) {
|
|
803
|
+
return (value === undefined ||
|
|
804
|
+
(Array.isArray(value) &&
|
|
805
|
+
value.length <= MAX_ARTIFACT_FILE_REFS &&
|
|
806
|
+
value.every((fileRef) => typeof fileRef === "string" &&
|
|
807
|
+
Buffer.byteLength(fileRef, "utf-8") <= 512 &&
|
|
808
|
+
SAFE_FILE_REF.test(fileRef))));
|
|
809
|
+
}
|
|
810
|
+
function isArtifactTestSummaries(value) {
|
|
811
|
+
return (value === undefined ||
|
|
812
|
+
(Array.isArray(value) &&
|
|
813
|
+
value.length <= MAX_ARTIFACT_TEST_SUMMARIES &&
|
|
814
|
+
value.every((summary) => Boolean(summary) &&
|
|
815
|
+
typeof summary === "object" &&
|
|
816
|
+
isBoundedText(summary.name, MAX_ARTIFACT_TEST_NAME_BYTES) &&
|
|
817
|
+
["passed", "failed", "skipped"].includes(summary.status) &&
|
|
818
|
+
(summary.summary ===
|
|
819
|
+
undefined ||
|
|
820
|
+
isBoundedText(summary.summary, MAX_ARTIFACT_TEST_SUMMARY_BYTES)))));
|
|
821
|
+
}
|
|
822
|
+
function isOrchestrationArtifact(value) {
|
|
823
|
+
if (value === undefined)
|
|
824
|
+
return true;
|
|
825
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
826
|
+
return false;
|
|
827
|
+
const artifact = value;
|
|
828
|
+
return (hasExactKeys(artifact, [
|
|
829
|
+
"id",
|
|
830
|
+
"kind",
|
|
831
|
+
"round",
|
|
832
|
+
"participantId",
|
|
833
|
+
"text",
|
|
834
|
+
"attribution",
|
|
835
|
+
"supersedesArtifactId",
|
|
836
|
+
"metadata",
|
|
837
|
+
]) &&
|
|
838
|
+
isSafeId(artifact.id) &&
|
|
839
|
+
[
|
|
840
|
+
"proposal",
|
|
841
|
+
"cross_review",
|
|
842
|
+
"revision",
|
|
843
|
+
"synthesis",
|
|
844
|
+
"checkpoint",
|
|
845
|
+
"watchdog_review",
|
|
846
|
+
"finding_disposition",
|
|
847
|
+
"completion",
|
|
848
|
+
].includes(artifact.kind ?? "") &&
|
|
849
|
+
Number.isSafeInteger(artifact.round) &&
|
|
850
|
+
artifact.round >= 1 &&
|
|
851
|
+
artifact.round <= 3 &&
|
|
852
|
+
(artifact.participantId === undefined ||
|
|
853
|
+
isSafeId(artifact.participantId)) &&
|
|
854
|
+
isSafeOrchestrationText(artifact.text) &&
|
|
855
|
+
isOrchestrationAttribution(artifact.attribution) &&
|
|
856
|
+
(artifact.supersedesArtifactId === undefined ||
|
|
857
|
+
isSafeId(artifact.supersedesArtifactId)) &&
|
|
858
|
+
isSafeOrchestrationMetadata(artifact.kind, artifact.metadata));
|
|
859
|
+
}
|
|
860
|
+
function hasExactKeys(value, allowed) {
|
|
861
|
+
return Object.keys(value).every((key) => allowed.includes(key));
|
|
862
|
+
}
|
|
863
|
+
function isOrchestrationAttribution(value) {
|
|
864
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
865
|
+
return false;
|
|
866
|
+
const attribution = value;
|
|
867
|
+
return (hasExactKeys(attribution, [
|
|
868
|
+
"participantIds",
|
|
869
|
+
"sourceArtifactIds",
|
|
870
|
+
]) &&
|
|
871
|
+
isSafeIdArray(attribution.participantIds, 2) &&
|
|
872
|
+
isSafeIdArray(attribution.sourceArtifactIds, MAX_MULTI_FRONTIER_ARTIFACTS_PER_RUN));
|
|
873
|
+
}
|
|
874
|
+
function isSafeIdArray(value, max) {
|
|
875
|
+
return (Array.isArray(value) &&
|
|
876
|
+
value.length <= max &&
|
|
877
|
+
value.every((item) => isSafeId(item)));
|
|
878
|
+
}
|
|
879
|
+
function isSafeOrchestrationText(value) {
|
|
880
|
+
return (isArtifactSummary(value) &&
|
|
881
|
+
!/(?:bearer\s+|api[_-]?key\s*[:=]|authorization\s*[:=]|(?:sk|rk|pk)[_-][A-Za-z0-9_-]{12,}|(?:access|refresh)[_-]?token\s*[:=])/i.test(value));
|
|
882
|
+
}
|
|
883
|
+
function isSafeOrchestrationMetadata(kind, value) {
|
|
884
|
+
const allowedKeys = metadataKeysForOrchestrationKind(kind);
|
|
885
|
+
if (allowedKeys.length === 0)
|
|
886
|
+
return value === undefined;
|
|
887
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
888
|
+
return false;
|
|
889
|
+
const entries = Object.entries(value);
|
|
890
|
+
if (entries.length === 0 ||
|
|
891
|
+
entries.some(([key]) => !allowedKeys.includes(key)) ||
|
|
892
|
+
entries.some(([key]) => /(token|secret|authorization|credential|password|email|organization|account)/i.test(key))) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
try {
|
|
896
|
+
return (isArtifactSummary(JSON.stringify(value)) &&
|
|
897
|
+
isValidOrchestrationMetadata(kind, value));
|
|
898
|
+
}
|
|
899
|
+
catch {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function isValidOrchestrationMetadata(kind, metadata) {
|
|
904
|
+
if (kind === "synthesis") {
|
|
905
|
+
return (typeof metadata.agreed === "boolean" &&
|
|
906
|
+
typeof metadata.deterministicallyResolved === "boolean" &&
|
|
907
|
+
(metadata.reversibleResolution === undefined ||
|
|
908
|
+
isReversibleResolution(metadata.reversibleResolution)));
|
|
909
|
+
}
|
|
910
|
+
if (kind === "checkpoint")
|
|
911
|
+
return isCheckpointBundle(metadata.bundle);
|
|
912
|
+
if (kind === "cross_review" || kind === "watchdog_review") {
|
|
913
|
+
return isFindingList(metadata.findings);
|
|
914
|
+
}
|
|
915
|
+
if (kind === "finding_disposition") {
|
|
916
|
+
return isFindingDispositionList(metadata.dispositions);
|
|
917
|
+
}
|
|
918
|
+
if (kind === "completion") {
|
|
919
|
+
return (isCompletionTests(metadata.tests) &&
|
|
920
|
+
isSafeTextList(metadata.proofRefs, 40) &&
|
|
921
|
+
isSafeTextList(metadata.remainingRisks, 40));
|
|
922
|
+
}
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
function isFindingList(value) {
|
|
926
|
+
return (Array.isArray(value) &&
|
|
927
|
+
value.length <= 40 &&
|
|
928
|
+
value.every((finding) => {
|
|
929
|
+
if (!finding || typeof finding !== "object" || Array.isArray(finding))
|
|
930
|
+
return false;
|
|
931
|
+
const item = finding;
|
|
932
|
+
return (hasExactKeys(item, [
|
|
933
|
+
"id",
|
|
934
|
+
"category",
|
|
935
|
+
"summary",
|
|
936
|
+
"rawFindingId",
|
|
937
|
+
"reviewerParticipantId",
|
|
938
|
+
]) &&
|
|
939
|
+
isSafeId(item.id) &&
|
|
940
|
+
isSafeOrchestrationText(item.summary) &&
|
|
941
|
+
isSafeId(item.rawFindingId) &&
|
|
942
|
+
isSafeId(item.reviewerParticipantId) &&
|
|
943
|
+
[
|
|
944
|
+
"reversible_technical",
|
|
945
|
+
"intent_or_scope",
|
|
946
|
+
"destructive_action",
|
|
947
|
+
"security_or_privacy",
|
|
948
|
+
"outward_effect",
|
|
949
|
+
"meaningful_cost_expansion",
|
|
950
|
+
"irreversible_architecture",
|
|
951
|
+
].includes(item.category));
|
|
952
|
+
}));
|
|
953
|
+
}
|
|
954
|
+
function isFindingDispositionList(value) {
|
|
955
|
+
return (Array.isArray(value) &&
|
|
956
|
+
value.length <= 40 &&
|
|
957
|
+
value.every((disposition) => {
|
|
958
|
+
if (!disposition ||
|
|
959
|
+
typeof disposition !== "object" ||
|
|
960
|
+
Array.isArray(disposition))
|
|
961
|
+
return false;
|
|
962
|
+
const item = disposition;
|
|
963
|
+
return (hasExactKeys(item, ["findingId", "disposition", "reason"]) &&
|
|
964
|
+
isSafeId(item.findingId) &&
|
|
965
|
+
["addressed", "rejected", "deferred"].includes(item.disposition) &&
|
|
966
|
+
isSafeOrchestrationText(item.reason));
|
|
967
|
+
}));
|
|
968
|
+
}
|
|
969
|
+
function isReversibleResolution(value) {
|
|
970
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
971
|
+
return false;
|
|
972
|
+
const item = value;
|
|
973
|
+
return (hasExactKeys(item, [
|
|
974
|
+
"alternatives",
|
|
975
|
+
"comparator",
|
|
976
|
+
"selected",
|
|
977
|
+
"reversibility",
|
|
978
|
+
]) &&
|
|
979
|
+
isSafeTextList(item.alternatives, 8) &&
|
|
980
|
+
isSafeOrchestrationText(item.comparator) &&
|
|
981
|
+
isSafeOrchestrationText(item.selected) &&
|
|
982
|
+
isSafeOrchestrationText(item.reversibility));
|
|
983
|
+
}
|
|
984
|
+
function isCheckpointBundle(value) {
|
|
985
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
986
|
+
return false;
|
|
987
|
+
const item = value;
|
|
988
|
+
return (hasExactKeys(item, [
|
|
989
|
+
"requestSummary",
|
|
990
|
+
"requestRef",
|
|
991
|
+
"acceptedPlanArtifactId",
|
|
992
|
+
"contentRef",
|
|
993
|
+
"contentHash",
|
|
994
|
+
"testOutput",
|
|
995
|
+
"driverSummary",
|
|
996
|
+
"openRisks",
|
|
997
|
+
"unresolvedFindingIds",
|
|
998
|
+
]) &&
|
|
999
|
+
isSafeOrchestrationText(item.requestSummary) &&
|
|
1000
|
+
(item.requestRef === undefined || isSafeId(item.requestRef)) &&
|
|
1001
|
+
isSafeId(item.acceptedPlanArtifactId) &&
|
|
1002
|
+
typeof item.contentRef === "string" &&
|
|
1003
|
+
/^(?!\/)(?!.*\.\.)[A-Za-z0-9._/@:-]{1,512}$/.test(item.contentRef) &&
|
|
1004
|
+
typeof item.contentHash === "string" &&
|
|
1005
|
+
SAFE_CONTENT_HASH.test(item.contentHash) &&
|
|
1006
|
+
isSafeOrchestrationText(item.testOutput) &&
|
|
1007
|
+
isSafeOrchestrationText(item.driverSummary) &&
|
|
1008
|
+
isSafeTextList(item.openRisks, 40) &&
|
|
1009
|
+
isSafeIdArray(item.unresolvedFindingIds, 40));
|
|
1010
|
+
}
|
|
1011
|
+
function isCompletionTests(value) {
|
|
1012
|
+
return (Array.isArray(value) &&
|
|
1013
|
+
value.length <= 40 &&
|
|
1014
|
+
value.every((test) => {
|
|
1015
|
+
if (!test || typeof test !== "object" || Array.isArray(test))
|
|
1016
|
+
return false;
|
|
1017
|
+
const item = test;
|
|
1018
|
+
return (hasExactKeys(item, ["name", "status", "evidence"]) &&
|
|
1019
|
+
isSafeOrchestrationText(item.name) &&
|
|
1020
|
+
["passed", "failed", "skipped"].includes(item.status) &&
|
|
1021
|
+
isSafeOrchestrationText(item.evidence));
|
|
1022
|
+
}));
|
|
1023
|
+
}
|
|
1024
|
+
function isSafeTextList(value, max) {
|
|
1025
|
+
return (Array.isArray(value) &&
|
|
1026
|
+
value.length <= max &&
|
|
1027
|
+
value.every((item) => isSafeOrchestrationText(item)));
|
|
1028
|
+
}
|
|
1029
|
+
function metadataKeysForOrchestrationKind(kind) {
|
|
1030
|
+
if (kind === "synthesis") {
|
|
1031
|
+
return ["agreed", "deterministicallyResolved", "reversibleResolution"];
|
|
1032
|
+
}
|
|
1033
|
+
if (kind === "checkpoint")
|
|
1034
|
+
return ["bundle"];
|
|
1035
|
+
if (kind === "cross_review" || kind === "watchdog_review") {
|
|
1036
|
+
return ["findings"];
|
|
1037
|
+
}
|
|
1038
|
+
if (kind === "finding_disposition")
|
|
1039
|
+
return ["dispositions"];
|
|
1040
|
+
if (kind === "completion")
|
|
1041
|
+
return ["tests", "proofRefs", "remainingRisks"];
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1044
|
+
function containsUnsafeOrchestrationValue(value, depth) {
|
|
1045
|
+
if (depth > 8)
|
|
1046
|
+
return true;
|
|
1047
|
+
if (typeof value === "string")
|
|
1048
|
+
return !isSafeOrchestrationText(value);
|
|
1049
|
+
if (value === null ||
|
|
1050
|
+
typeof value === "boolean" ||
|
|
1051
|
+
(typeof value === "number" && Number.isFinite(value))) {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
if (Array.isArray(value)) {
|
|
1055
|
+
return (value.length > 40 ||
|
|
1056
|
+
value.some((item) => containsUnsafeOrchestrationValue(item, depth + 1)));
|
|
1057
|
+
}
|
|
1058
|
+
if (!value || typeof value !== "object")
|
|
1059
|
+
return true;
|
|
1060
|
+
const entries = Object.entries(value);
|
|
1061
|
+
return (entries.length > 40 ||
|
|
1062
|
+
entries.some(([key, item]) => !isBoundedText(key, 160) ||
|
|
1063
|
+
/(token|secret|authorization|credential|password|email|organization|account)/i.test(key) ||
|
|
1064
|
+
containsUnsafeOrchestrationValue(item, depth + 1)));
|
|
1065
|
+
}
|
|
1066
|
+
function cloneOrchestrationArtifact(artifact) {
|
|
1067
|
+
return JSON.parse(JSON.stringify(artifact));
|
|
1068
|
+
}
|
|
1069
|
+
function isBoundedText(value, maxBytes) {
|
|
1070
|
+
return (typeof value === "string" &&
|
|
1071
|
+
value.trim().length > 0 &&
|
|
1072
|
+
Buffer.byteLength(value, "utf-8") <= maxBytes);
|
|
1073
|
+
}
|
|
1074
|
+
function assertBoundedText(value, maxBytes, label) {
|
|
1075
|
+
if (!isBoundedText(value, maxBytes)) {
|
|
1076
|
+
throw new Error(`Invalid multi-frontier ${label}.`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function assertRetentionLimits(limits) {
|
|
1080
|
+
if (!isNonNegativeInteger(limits.maxEventCount) ||
|
|
1081
|
+
limits.maxEventCount < 1 ||
|
|
1082
|
+
!isNonNegativeInteger(limits.maxBytes) ||
|
|
1083
|
+
limits.maxBytes < 512) {
|
|
1084
|
+
throw new Error("Invalid multi-frontier participant event retention limits.");
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function eventLineByteLength(event) {
|
|
1088
|
+
return Buffer.byteLength(`${JSON.stringify(event)}\n`, "utf-8");
|
|
1089
|
+
}
|
|
1090
|
+
function isSafeId(value) {
|
|
1091
|
+
return typeof value === "string" && SAFE_ID.test(value);
|
|
1092
|
+
}
|
|
1093
|
+
function isNonNegativeInteger(value) {
|
|
1094
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
1095
|
+
}
|
|
363
1096
|
function assertParticipants(participants) {
|
|
364
1097
|
if (participants.length === 0) {
|
|
365
1098
|
throw new Error("A multi-frontier run needs at least one participant.");
|
|
@@ -511,19 +1244,4 @@ function assertTimestamp(value, label) {
|
|
|
511
1244
|
throw new Error(`Invalid multi-frontier ${label}.`);
|
|
512
1245
|
}
|
|
513
1246
|
}
|
|
514
|
-
function writeFileAtomically(filePath, contents) {
|
|
515
|
-
const directory = path.dirname(filePath);
|
|
516
|
-
const temporaryPath = path.join(directory, `.${path.basename(filePath)}.tmp-${process.pid}-${crypto.randomUUID()}`);
|
|
517
|
-
try {
|
|
518
|
-
fs.writeFileSync(temporaryPath, contents, {
|
|
519
|
-
encoding: "utf-8",
|
|
520
|
-
flag: "wx",
|
|
521
|
-
});
|
|
522
|
-
fs.renameSync(temporaryPath, filePath);
|
|
523
|
-
}
|
|
524
|
-
catch (error) {
|
|
525
|
-
fs.rmSync(temporaryPath, { force: true });
|
|
526
|
-
throw error;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
1247
|
//# sourceMappingURL=multi-frontier-runs.js.map
|