Commit graph

333 commits

Author SHA1 Message Date
Nexus Dev
f8df254777 feat(36-01): VoicePipelineService with transcribe, synthesize, formatForVoice, transcodeToWav16k
- Install ffmpeg-static and @types/ffmpeg-static
- Create voice-pipeline.ts with voicePipelineService factory function
- transcodeToWav16k: pipes audio through ffmpeg at 16kHz mono WAV
- transcribe: whisper-cpp cascade with --language auto, falls back to openai-whisper
- synthesize: piper TTS with sentence chunking and 8s timeout via Promise.race
- formatForVoice: extracts SPOKEN marker or strips markdown as fallback
- Unit tests with mocked child_process (12 tests all passing)
2026-04-04 01:31:53 +00:00
Nexus Dev
044e3dad54 feat(36-02): extend nexus-settings schema with voiceMode, telegramToken, and binary paths
- Export VOICE_MODES constant and VoiceMode type from nexus-settings
- Export nexusSettingsSchema for testing
- Add voiceMode field with default 'text' to nexusSettingsSchema
- Add telegramToken optional field to nexusSettingsSchema
- Add piperBinaryPath and whisperBinaryPath optional fields
- Update fallback in get() to use nexusSettingsSchema.parse({}) for consistent defaults
- Add 5 passing tests for nexus-settings schema in 36-voice-schema.test.ts
2026-04-04 01:23:46 +00:00
Nexus Dev
0d318a31d3 feat(34-01): register chatFileRoutes + nexusSettingsRoutes in app.ts, add voiceEnabled to nexus-settings
- Add chatFileRoutes(db, storageService) after assistantHandoffRoutes (inside boardMutationGuard)
- Add nexusSettingsRoutes() after chatFileRoutes
- Extend nexusSettingsSchema with voiceEnabled: z.boolean().default(false)
- Update default return values in nexusSettingsService.get() to include voiceEnabled: false
- Add voiceEnabled?: boolean to NexusSettings client interface in hardware.ts
2026-04-03 22:33:43 +00:00
Nexus Dev
0bfec2a3c8 feat(33-01,33-02): memory service + sanitizer, personal assistant page
33-01: memory-sanitizer, assistant-memory service, REST routes, 17 tests
33-02: useNexusMode hook, PersonalAssistantPage, sidebar nav, route wiring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:03:09 +00:00
Nexus Dev
13bc39b1d4 feat(31-01): implement puterProxyService, puterProxyRoutes, and unit tests
- puterProxyService with storeToken (create/rotate idempotent), resolveToken, chatStream
- chatStream relays to Puter OpenAI-compat endpoint with SSE streaming
- Cost recording with provider=puter, billingType=subscription_included, costCents=0
- Cost recording skipped when agentId is null/undefined (no FK violation)
- puterProxyRoutes with POST /puter-proxy/token and POST /puter-proxy/chat
- Board auth (assertBoard + assertCompanyAccess) on all routes
- All 10 TDD tests passing
2026-04-03 00:35:11 +00:00
Nexus Dev
720455132a feat(31-02): add googleOAuthService with PKCE generation and token management
- generatePkce() using crypto.randomBytes base64url verifier and SHA256 challenge
- generateAuthUrl() builds Google OAuth URL with PKCE params for Gemini scopes
- exchangeCode() POSTs to Google token endpoint with code_verifier
- storeTokens() upserts google_gemini_oauth_token via secretService
- resolveTokens() retrieves and parses stored tokens by companyId
2026-04-03 00:33:46 +00:00
Nexus Dev
766460a163 feat(30-01): hardware detection, nexus-settings, extended model catalog
- Add hardwareService with Apple Silicon / GPU / cpu_only tier detection
- Add 3s Promise.race timeout for si.graphics() with cpu_only fallback
- Add nexusSettingsService with Zod validation and file-backed persistence
- Extend ollama-model-catalog.json with tier arrays on every variant
- Add qwen3:8b family to catalog
- Update getRecommendedModel to accept optional hardwareTier parameter
- All 13 unit tests pass (TDD green)
2026-04-02 23:19:09 +00:00
Nexus Dev
a3802a9dd6 feat(28-02,28-03): Ollama UI surface + Hermes runtime dashboard
28-02: ollamaApi client, model dropdown in config, skill badge
28-03: stateJson merge after heartbeat, HermesRuntimeCard in AgentOverview

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:08:53 +00:00
Nexus Dev
5266d25727 feat(28-03): merge Hermes runtime data into stateJson and add HermesRuntimeCard
- Add OllamaPsResponse interface and getOllamaMemoryUsage() to ollama.ts
- Import getOllamaMemoryUsage in heartbeat.ts
- Add hermes_local block in updateRuntimeState: COALESCE jsonb merge of hermesModel + hermesMemoryBytes
- Add HermesRuntimeCard component in AgentDetail.tsx
- Render HermesRuntimeCard in AgentOverview gated by adapterType === hermes_local
- Native skill count derived from agentsApi.skills entries with originLabel === Hermes skill
2026-04-02 17:07:53 +00:00
Nexus Dev
f052066f58 feat(28-01): Ollama service, routes, model catalog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:57:27 +00:00
Nexus Dev
6a7b9bd5f0 feat(27-01): close Hermes adapter integration gaps
- Add hermes_local to SESSIONED_LOCAL_ADAPTERS (HERM-03)
- Fix create-mode toolsets field guard (HERM-02)
- Add hermes session codec round-trip tests (HERM-04)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:27:20 +00:00
Nexus Dev
3f1535f295 feat(26-04): create push_subscriptions schema, migration, pushService, and push routes
- Add push_subscriptions pgTable with endpoint, p256dh, auth, userId, companyId, deviceLabel
- Add 0055_create_push_subscriptions.sql migration with CREATE TABLE and endpoint index
- Export pushSubscriptions from schema/index.ts
- Create pushService with initVapid, getVapidPublicKey, saveSubscription, removeSubscription, sendPushToAll
- sendPushToAll auto-deletes stale subscriptions on 410/404 response
- Create pushRoutes: GET /vapid-public-key, POST /subscribe, DELETE /subscribe
- Mount /api/push routes and call initVapid() in app.ts with graceful skip
- Install web-push and @types/web-push
2026-04-02 15:08:51 +00:00
Nexus Dev
3b3bdc2b39 feat(25-06): merge git file service and history endpoint from worktree
Adds gitFileService with commitFile/getLog, wires git commits into
upload flow, adds GET /files/:fileId/history endpoint, and exports
ChatFileHistoryEntry type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:08:51 +00:00
Nexus Dev
2cafff8054 feat(25-07): create placeholderService and add markAsPlaceholder method
- Create server/src/services/placeholder-service.ts with addEntry, replaceEntry, listEntries
- Generates PLACEHOLDERS.md with Active Placeholders and Replaced markdown tables
- Add ChatPlaceholderEntry interface to packages/shared/src/types/chat.ts
- Export ChatPlaceholderEntry from packages/shared/src/index.ts
- Add markAsPlaceholder method to chatFileService in chat-files.ts
2026-04-02 15:08:51 +00:00
Nexus Dev
ff3aa8793d feat(25-04): create ChatCodeFilePreview with syntax highlighting
- Add ChatCodeFilePreview component with hljs syntax highlighting
- Fetch file content from contentPath with credentials
- Use DOMParser-based safe rendering (no dangerouslySetInnerHTML)
- Include copy button, language label, and ChatFileCard download below
- Add extToLang extension-to-language mapping
- Register 14 common languages with hljs
- Add highlight.js as direct dependency in ui/package.json
2026-04-02 15:08:51 +00:00
Nexus Dev
41f1880a29 fix(25): handle missing chat_files table in listMessages and update addMessage test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:08:51 +00:00
Nexus Dev
35a7814032 feat(25-03): wire files into ChatMessage, ChatPanel, and server listMessages
- ChatMessage: files prop, renders ChatFilePreview for each attached file
- ChatMessageList: passes files prop through to ChatMessage
- ChatPanel: wires useChatFileUpload, passes pendingFiles/onRemoveFile/onFilesPicked to ChatInput
- ChatPanel handleSend: attaches uploaded files to message after creation, invalidates query
- chatApi: adds attachFilesToMessage (parallel PATCH /files/:fileId for each fileId)
- server/chat.ts: listMessages fetches chatFiles by messageId (inArray), attaches files array
- server/chat.ts: addMessage returns files: [] for consistency
2026-04-02 15:08:51 +00:00
Nexus Dev
db4eb801d3 feat(25-01): create chatFileService with DB operations and deriveCategory helper
- Implement chatFileService(db) with create, getById, listByConversation, listByMessage, createReference, listReferences, attachToMessage
- Export deriveCategory() helper mapping MIME types to image/code/document/other
- Add unit tests verifying service methods and category derivation with mocked DB
2026-04-02 15:08:51 +00:00
Nexus Dev
430588a54e feat(24-01): add searchMessages, toggleBookmark, getBookmarks, branchConversation, listBranches, exportConversation service methods
- searchMessages: tsvector FTS with ts_rank ordering, joins conversations for companyId scoping
- toggleBookmark: transactional insert-or-delete bookmark
- getBookmarks: joins bookmarks+messages+conversations, supports conversationId filter
- branchConversation: copies messages up to branch point into new child conversation
- listBranches: queries child conversations by parentConversationId
- exportConversation: LEFT JOINs agents for name resolution, produces Markdown or JSON with file headers
2026-04-02 15:08:51 +00:00
Nexus Dev
2a0c5769f3 feat(23-01): extend chat service with messageType support and addSystemMessage
- addMessage now accepts optional messageType parameter
- addSystemMessage helper inserts system-role messages with typed messageType
- Both methods bump conversation updatedAt for correct sort order
2026-04-02 15:08:51 +00:00
Nexus Dev
ea1a176b67 feat(22-01): add SSE streaming endpoint and edit/truncate service methods
- Add editMessage, truncateMessagesAfter, streamEcho methods to chatService
- Add POST /conversations/:id/stream SSE endpoint with flushHeaders before loop (PERF-02)
- Add PATCH /conversations/:id/messages/:msgId for message editing
- Add DELETE /conversations/:id/messages/after/:msgId for message truncation
- Import gt from drizzle-orm for createdAt comparison in truncateMessagesAfter
- Guard all res.write() calls with res.writable check (prevents write-after-end)
2026-04-02 15:08:50 +00:00
Nexus Dev
6367789992 feat(21-06): add search and agentId filter to listConversations
- Add ilike import and search/agentId conditions in chatService.listConversations
- Extract search and agentId from req.query in GET /conversations route
- Extend chatApi.listConversations opts with search and agentId params
- Update useChatConversations to accept opts.search and include in queryKey
2026-04-02 15:08:50 +00:00
Nexus Dev
d1438192b8 feat(21-03): implement chatService with full conversation + message CRUD
- createConversation, listConversations, getConversation, updateConversation
- softDeleteConversation, listMessages, addMessage
- cursor-based pagination with hasMore for both conversations and messages
- Pitfall 3: addMessage bumps conversation updatedAt after insert
- Pitfall 5: addMessage auto-sets title from first user message (IS NULL guard)
- 21 vitest tests passing
2026-04-02 15:08:50 +00:00
a06eac3278 [nexus] feat(19-01): unit tests for adapter-aware install/uninstall and Hermes dual-source
- skill-registry-adapter-install.test.ts: 9 tests covering install/uninstall/rollback/assignGroup/removeGroup
- hermes-dual-source.test.ts: 7 tests covering syncHermesNativeSkills idempotency and listAgentSkills object shape
- Fix skill-registry-install.test.ts: update uninstall() callers to pass agentSkillsDir (new required param)
- Fix removeGroup() bug: removed incorrect 'individualSkills' guard that prevented file removal for group-installed skills
  (rule 1 auto-fix: group-installed skills were never removed because they appeared in agentSkills with no way to distinguish from direct installs)
- All 16 new tests pass, all existing tests still pass
2026-04-02 15:08:50 +00:00
1f33b1243a [nexus] feat(19-01): adapter-aware skill service layer — source column, uninstall file removal, syncHermesNativeSkills
- agentSkills schema gets source TEXT NOT NULL DEFAULT 'managed' column
- Migration guard in getSkillRegistryDb() handles existing DBs via ALTER TABLE
- uninstall() now accepts agentSkillsDir and removes files before soft-deleting
- syncHermesNativeSkills() reads ~/.hermes/skills/, creates stub rows with source='native'
- listAgentSkills() returns typed objects {skillId, source, installedAt} not string[]
- Interim uninstall route fix: reads agentSkillsDir from query param until Plan 02 wires agentId
2026-04-02 15:08:50 +00:00
2e9470be62 feat(12-01): ratings routes, community ratings in fetcher, list/getById JOIN, heartbeat hook
- Add POST/GET /skill-registry/skills/:sourceId/:slug/ratings routes
- Import skillRatingService in skill-registry routes
- Add upsertCommunityRatingsStub() in fetcher, called after each skill upsert
- Import communityRatings from schema in fetcher
- Update list() and getById() in skill-registry.ts to LEFT JOIN communityRatings
- Include averageRating, ratingCount, taskCount, avgCostUsd, lastUsedAt in SkillListItem
- Add agentSkills usage aggregation via LEFT JOIN + SUM/AVG/MAX
- Add fire-and-forget recordUsageForAgent call in heartbeat after finalizeAgentStatus
- Dynamic import keeps skill-registry-ratings off critical startup path
- All 44 skill-registry tests pass, full server suite (536) green
2026-04-02 15:08:50 +00:00
b7dddfa266 feat(12-01): personalRatings schema, DB DDL, skillRatingService, and tests
- Add personalRatings table to skill-registry-schema.ts
- Add taskCount, avgCostUsd, lastUsedAt columns to agentSkills in schema
- Add CREATE_PERSONAL_RATINGS_TABLE DDL constant in skill-registry-db.ts
- Add ALTER TABLE statements for new agent_skills usage columns (idempotent)
- Create skill-registry-ratings.ts with skillRatingService factory
- rate() appends personal rating, validates stars 1-5
- getRatings() returns ratings ordered by createdAt DESC
- recordUsageForAgent() atomically updates task_count, avg_cost_usd, last_used_at
- All 8 tests pass
2026-04-02 15:08:50 +00:00
bebacf5406 feat(11-02): skillGroupService() with CRUD, membership, inheritance, assignment, import/export
- Group CRUD: listGroups, getGroup, createGroup, updateGroup, deleteGroup (guards built-in)
- Member management: addMember, removeMember, listMembers
- Inheritance: addParent (with cycle detection BFS), removeParent, listParents
- resolveEffectiveSkills: BFS walk with visited-set guard for cycle safety
- assignGroup: installs all effective skills, tracks in agent_skills, returns installed/skipped/pendingPlugin
- removeGroup: set-difference uninstall with fs.rm() for file removal (not skillRegistryService.uninstall)
- listAgentGroups, listAgentSkills, getAgentEffectiveSkills
- exportGroup / importGroup: GroupExport v1 JSON with cycle check on import
2026-04-02 15:08:50 +00:00
0294ccdd29 feat(11-01): add group table DDL and built-in group seeding to skill-registry-db
- Import LibSQLClient type for seedBuiltinGroups parameter typing
- Add DDL constants for 5 new tables: skill_groups, skill_group_members,
  skill_group_inheritance, agent_skill_groups, agent_skills
- Add BUILTIN_GROUPS constant with 5 entries (pm-essentials, engineer-core,
  frontend, backend, creative)
- Add seedBuiltinGroups() using INSERT OR IGNORE for idempotent seeding
- Extend getSkillRegistryDb() to execute all 5 new DDL statements and seed
2026-04-02 15:08:50 +00:00
3612b3ae66 feat(11-01): add five Drizzle table definitions for skill groups
- Add primaryKey import from drizzle-orm/sqlite-core
- Add skillGroups table with id, name, description, isBuiltin, timestamps
- Add skillGroupMembers junction table with composite PK (groupId, skillId)
- Add skillGroupInheritance table with composite PK (childGroupId, parentGroupId)
- Add agentSkillGroups table with composite PK (agentId, groupId)
- Add agentSkills table with composite PK (agentId, skillId)
2026-04-02 15:08:50 +00:00
ff99723991 feat(09-03): wire skillRegistryService export and startup DB init
- Add skillRegistryService re-export to services/index.ts after companySkillService
- Add fire-and-forget skill registry DB init in server/src/index.ts after reconcile block
- Uses dynamic import to avoid adding libSQL to critical startup path
2026-04-02 15:08:50 +00:00
4ee53bd62a feat(09-03): implement skillRegistryService with install, uninstall, rollback, list
- install() copies cached files to agent .claude/skills/<slug>/ dir
- install() returns pending_plugin_install for skills with file kind=plugin
- uninstall() soft-deletes via removed_at timestamp
- rollback() restores prior version from cache and updates active_version_id
- list() filters soft-deleted by default; includeRemoved=true returns all
- fetchAll() delegates to fetchAllSources for multi-source refresh
2026-04-02 15:08:50 +00:00
12de4a018f feat(09-02): implement multi-source skill fetcher with file caching
- SkillSourceConfig type + BUILT_IN_SOURCES (3 sources: anthropic, schwepps, daymade)
- fetchAllSources() fetches from anthropic-marketplace and github-tree source types
- parseSkillFrontmatter() extracts name/description from SKILL.md YAML blocks
- Idempotency: checks version exists before fetching, skips re-download on same SHA
- Caches SKILL.md to skills/cache/<skill-id>/<sha>/SKILL.md on disk
- Inserts skills, skill_versions, and skill_files rows into registry.db
- All 7 tests passing (TDD GREEN)
2026-04-02 15:08:50 +00:00
11f1ff2b7b feat(09-01): extract GitHub fetch helpers to shared module
- Create github-skill-helpers.ts with fetchText, fetchJson, resolveGitHubDefaultBranch, resolveGitHubCommitSha, parseGitHubSourceUrl, resolveGitHubPinnedRef, resolveRawGitHubUrl
- Update company-skills.ts to import from github-skill-helpers.js instead of defining locally
- All existing company-skill tests pass (15/15)
2026-04-02 15:08:50 +00:00
576fda3adc feat(09-01): install @libsql/client, schema, DB init, path helpers
- Install @libsql/client@^0.17.2 to server package
- Create skill-registry-schema.ts with 4 sqliteTable definitions (skills, skillVersions, skillFiles, communityRatings)
- Create skill-registry-db.ts with lazy singleton getSkillRegistryDb() and resetSkillRegistryDb()
- Add resolveSkillRegistryDbPath() and resolveSkillCacheDir() to home-paths.ts
- Add skill-registry-schema.test.ts with 8 passing tests (TDD green)
2026-04-02 15:08:38 +00:00
d3d042bff6 feat(08-01): add Generalist agent template bundle and wire role mapping
- Create server/src/onboarding-assets/general/ with 4 files (AGENTS.md, SOUL.md, HEARTBEAT.md, TOOLS.md)
- Add general role to DEFAULT_AGENT_BUNDLE_FILES with full 4-file bundle
- Add resolveDefaultAgentInstructionsBundleRole branch for general role
- Rename AGENT_ROLE_LABELS general from 'General' to 'Generalist'
2026-04-02 15:08:38 +00:00
8790e939c1 [nexus] fix(06): resolve verifier gaps — portability fallback, export readme, CLI company descriptions, server error msg 2026-04-02 15:08:37 +00:00
0281e7a6bf feat(06-03): TERM-18 grep audit — fix remaining display-zone corporate strings
- ui/src/App.tsx: Create/first company titles and descriptions → VOCAB.company
- ui/src/components/OnboardingWizard.tsx: 3 company display strings → VOCAB
- ui/src/components/Sidebar.tsx: 'Select company' fallback → VOCAB
- ui/src/pages/CliAuth.tsx: 'Requested company' label → VOCAB
- ui/src/pages/AgentDetail.tsx: company library string → VOCAB
- server/src/services/company-portability.ts: 'Imported Company' x2 → 'Imported Workspace'
- cli/src/commands/client/{issue,approval,agent,dashboard,activity}.ts: option descriptions → VOCAB
- cli/src/commands/worktree.ts: error message and option description → VOCAB
- server/src/index.ts: comment cleanup (actual value already 'Owner')
- server/src/services/company-export-readme.ts: comment cleanup (value already 'Project Manager')
2026-04-02 15:08:37 +00:00
9cb0e1a27b feat(04-01): register pm and engineer bundles in bundle registry
- Add pm and engineer entries to DEFAULT_AGENT_BUNDLE_FILES
- Update resolveDefaultAgentInstructionsBundleRole to handle pm and engineer roles
- DefaultAgentBundleRole type auto-includes new keys via keyof typeof
- All changes marked with // [nexus] for rebase visibility
2026-04-02 15:08:21 +00:00
509b73d8fc fix(03-05): grep audit fixes — CEO→Project Manager in export readme, Board→Owner in local user, test assertion updates
- company-export-readme.ts: ROLE_LABELS ceo changed from 'CEO' to 'Project Manager' [nexus]
- server/index.ts: LOCAL_BOARD_USER_NAME changed from 'Board' to 'Owner' [nexus]
- cli/__tests__/company.test.ts: assertions updated to Workspace vocabulary
- cli/__tests__/http.test.ts: assertion updated to 'Nexus API' from 'Paperclip API'
- ui/OnboardingWizard.tsx: added explicit string type annotation for useState<string>
2026-04-02 15:08:11 +00:00
b247678337 feat(02-02): update resolveDefaultAgentWorkspaceDir to use slugified agent names
- Change signature from (agentId: string) to (agent: { id: string; name?: string | null })
- Use sanitizeFriendlyPathSegment(name) for human-readable workspace dirs
- Fall back to sanitized id when name is empty/null
- Update all 4 call sites in heartbeat.ts with { id, name } objects
- Add agentName field to resolveRuntimeSessionParamsForWorkspace input type
- Update both test call sites in heartbeat-workspace-session.test.ts
2026-04-02 15:07:27 +00:00
Dotta
2c1883fc77
Merge pull request #2449 from statxc/feat/github-enterprise-url-support
feat: GitHub enterprise url support
2026-04-02 06:07:44 -05:00
Devin Foley
1e24e6e84c
fix: auto-detect default branch for worktree creation when baseRef not configured (#2463)
* fix: auto-detect default branch for worktree creation when baseRef not configured

When creating git worktrees, if no explicit baseRef is configured in
the project workspace strategy and no repoRef is set, the system now
auto-detects the repository's default branch instead of blindly
falling back to "HEAD".

Detection strategy:
1. Check refs/remotes/origin/HEAD (set by git clone / remote set-head)
2. Fall back to probing refs/remotes/origin/main, then origin/master
3. Final fallback: HEAD (preserves existing behavior)

This prevents failures like "fatal: invalid reference: main" when a
project's workspace strategy has no baseRef and the repo uses a
non-standard default branch name.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: address Greptile review - fix misleading comment and add symbolic-ref test

- Corrected comment to clarify that the existing test exercises the
  heuristic fallback path (not symbolic-ref)
- Added new test case that explicitly sets refs/remotes/origin/HEAD
  via `git remote set-head` to exercise the symbolic-ref code path

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-01 18:00:49 -07:00
statxc
9d89d74d70 refactor: rename URL validators to looksLikeRepoUrl 2026-04-01 23:21:22 +00:00
statxc
6a7830b07e fix: add HTTPS protocol check to server-side GitHub URL parsers 2026-04-01 21:27:10 +00:00
statxc
f9cebe9b73 fix: harden GHE URL detection and extract shared GitHub helpers 2026-04-01 21:05:48 +00:00
statxc
9e1ee925cd feat: support GitHub Enterprise URLs for skill and company imports 2026-04-01 20:53:41 +00:00
bittoby
99296f95db fix: append short UUID suffix to project slugs when non-ASCII characters are stripped to prevent slug collisions 2026-03-31 16:35:30 +00:00
Dotta
9f1bb350fe
Merge pull request #2065 from edimuj/fix/heartbeat-session-reuse
fix: preserve session continuity for timer/heartbeat wakes
2026-03-31 08:29:45 -05:00
dotta
41f261eaf5 Merge public-gh/master into PAP-881-document-revisions-bulid-it 2026-03-31 07:31:17 -05:00