@neuralis/brain-core 0.3.1 → 0.4.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/README.md +88 -1417
- package/app/README.md +4 -4
- package/app/cards/FilesystemCardShell.tsx +42 -4
- package/app/cards/FilesystemChangeCard.tsx +34 -11
- package/app/cards/README.md +6 -5
- package/app/cards/diffUtils.ts +99 -1
- package/app/filesystem/FilesystemHost.tsx +24 -2
- package/app/filesystem/README.md +1 -1
- package/app/filesystem/api/README.md +3 -3
- package/app/filesystem/api/index.ts +38 -14
- package/app/filesystem/api/projectPackages.ts +13 -102
- package/app/filesystem/domain/types.ts +30 -10
- package/app/filesystem/hooks/README.md +4 -4
- package/app/filesystem/hooks/useDiscoverableSources.ts +19 -30
- package/app/filesystem/hooks/useFilesystemSSE.ts +42 -2
- package/app/filesystem/host/README.md +6 -4
- package/app/filesystem/host/buildFilesystemHostValue.ts +28 -2
- package/app/filesystem/host/installFilesystemHostComponents.tsx +1 -1
- package/app/filesystem/lib/sourceKindForm.ts +210 -27
- package/app/filesystem/lib/treeVisualState.tsx +98 -4
- package/app/filesystem/store/README.md +1 -0
- package/app/filesystem/store/filesystemStore.ts +99 -8
- package/app/filesystem/store/gitSlice.ts +5 -0
- package/app/filesystem/store/helpers.ts +17 -0
- package/app/filesystem/store/listSlice.ts +8 -0
- package/app/filesystem/ui/CodeEditor.tsx +18 -3
- package/app/filesystem/ui/DiscoverDock.tsx +5 -1
- package/app/filesystem/ui/FileEditor.tsx +15 -10
- package/app/filesystem/ui/FileTree.tsx +75 -24
- package/app/filesystem/ui/FilesystemWidget.tsx +10 -2
- package/app/filesystem/ui/FolderPicker.tsx +251 -0
- package/app/filesystem/ui/GitControlPanel.tsx +7 -2
- package/app/filesystem/ui/PackagesPanel.tsx +52 -374
- package/app/filesystem/ui/PendingChangesPanel.tsx +1 -1
- package/app/filesystem/ui/PolicyEditor.tsx +109 -24
- package/app/filesystem/ui/README.md +30 -23
- package/app/filesystem/ui/RawMediaPreview.tsx +58 -0
- package/app/filesystem/ui/SourceBranchHeader.tsx +4 -4
- package/app/filesystem/ui/SourcesPanel.tsx +502 -342
- package/app/filesystem/ui/Toolbar.tsx +1 -2
- package/app/filesystem/ui/VectorHealthPanel.tsx +2 -2
- package/app/shared/filesystem.png +0 -0
- package/dist/app/host.js +58 -46
- package/dist/scripts/sourceRootPairV1.d.ts +47 -0
- package/dist/scripts/sourceRootPairV1.d.ts.map +1 -0
- package/dist/scripts/sourceRootPairV1.js +147 -0
- package/dist/scripts/sourceRootPairV1.js.map +1 -0
- package/dist/scripts/sourceToFolderV1.d.ts +56 -0
- package/dist/scripts/sourceToFolderV1.d.ts.map +1 -0
- package/dist/scripts/sourceToFolderV1.js +152 -0
- package/dist/scripts/sourceToFolderV1.js.map +1 -0
- package/dist/scripts/sourceToHostV1.d.ts +56 -0
- package/dist/scripts/sourceToHostV1.d.ts.map +1 -0
- package/dist/scripts/sourceToHostV1.js +132 -0
- package/dist/scripts/sourceToHostV1.js.map +1 -0
- package/dist/src/channels/filesystem.d.ts +1 -1
- package/dist/src/channels/filesystem.js +1 -1
- package/dist/src/connectors/AppSourceConfigStore.d.ts +17 -3
- package/dist/src/connectors/AppSourceConfigStore.d.ts.map +1 -1
- package/dist/src/connectors/AppSourceConfigStore.js +46 -6
- package/dist/src/connectors/AppSourceConfigStore.js.map +1 -1
- package/dist/src/connectors/HostConnector.d.ts +201 -0
- package/dist/src/connectors/HostConnector.d.ts.map +1 -0
- package/dist/src/connectors/HostConnector.js +903 -0
- package/dist/src/connectors/HostConnector.js.map +1 -0
- package/dist/src/connectors/inProcessTransport.d.ts +131 -0
- package/dist/src/connectors/inProcessTransport.d.ts.map +1 -0
- package/dist/src/connectors/inProcessTransport.js +447 -0
- package/dist/src/connectors/inProcessTransport.js.map +1 -0
- package/dist/src/domain/access/callerPermissions.d.ts +29 -0
- package/dist/src/domain/access/callerPermissions.d.ts.map +1 -1
- package/dist/src/domain/access/callerPermissions.js +85 -5
- package/dist/src/domain/access/callerPermissions.js.map +1 -1
- package/dist/src/domain/access/nativeLocalFence.d.ts +65 -0
- package/dist/src/domain/access/nativeLocalFence.d.ts.map +1 -0
- package/dist/src/domain/access/nativeLocalFence.js +171 -0
- package/dist/src/domain/access/nativeLocalFence.js.map +1 -0
- package/dist/src/domain/access/uriPermission.js +3 -3
- package/dist/src/domain/access/uriPermission.js.map +1 -1
- package/dist/src/domain/byteRange.d.ts +15 -0
- package/dist/src/domain/byteRange.d.ts.map +1 -0
- package/dist/src/domain/byteRange.js +30 -0
- package/dist/src/domain/byteRange.js.map +1 -0
- package/dist/src/domain/defaultPermissions.d.ts.map +1 -1
- package/dist/src/domain/defaultPermissions.js +29 -0
- package/dist/src/domain/defaultPermissions.js.map +1 -1
- package/dist/src/domain/mime.d.ts +11 -19
- package/dist/src/domain/mime.d.ts.map +1 -1
- package/dist/src/domain/mime.js +13 -102
- package/dist/src/domain/mime.js.map +1 -1
- package/dist/src/domain/paths.d.ts.map +1 -1
- package/dist/src/domain/paths.js +5 -3
- package/dist/src/domain/paths.js.map +1 -1
- package/dist/src/domain/scopeAccessCtx.d.ts +2 -2
- package/dist/src/domain/scopeAccessCtx.js +2 -2
- package/dist/src/domain/sourceConfig.d.ts +52 -19
- package/dist/src/domain/sourceConfig.d.ts.map +1 -1
- package/dist/src/domain/sourceConfig.js +103 -19
- package/dist/src/domain/sourceConfig.js.map +1 -1
- package/dist/src/domain/sourcePackages.d.ts +8 -8
- package/dist/src/domain/sourcePackages.d.ts.map +1 -1
- package/dist/src/domain/sourceRootPair.d.ts +88 -0
- package/dist/src/domain/sourceRootPair.d.ts.map +1 -0
- package/dist/src/domain/sourceRootPair.js +108 -0
- package/dist/src/domain/sourceRootPair.js.map +1 -0
- package/dist/src/domain/syncTypes.d.ts +8 -1
- package/dist/src/domain/syncTypes.d.ts.map +1 -1
- package/dist/src/domain/syncTypes.js +1 -0
- package/dist/src/domain/syncTypes.js.map +1 -1
- package/dist/src/domain/types.d.ts +32 -2
- package/dist/src/domain/types.d.ts.map +1 -1
- package/dist/src/domain/uri.d.ts.map +1 -1
- package/dist/src/domain/uri.js +3 -2
- package/dist/src/domain/uri.js.map +1 -1
- package/dist/src/embeddings/clients/GeminiEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/GeminiEmbedder.js +2 -5
- package/dist/src/embeddings/clients/GeminiEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/OpenAIEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/OpenAIEmbedder.js +2 -3
- package/dist/src/embeddings/clients/OpenAIEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/QwenEmbedder.d.ts +2 -0
- package/dist/src/embeddings/clients/QwenEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/QwenEmbedder.js +10 -2
- package/dist/src/embeddings/clients/QwenEmbedder.js.map +1 -1
- package/dist/src/embeddings/clients/VoyageEmbedder.d.ts +2 -0
- package/dist/src/embeddings/clients/VoyageEmbedder.d.ts.map +1 -1
- package/dist/src/embeddings/clients/VoyageEmbedder.js +8 -1
- package/dist/src/embeddings/clients/VoyageEmbedder.js.map +1 -1
- package/dist/src/embeddings/factory.d.ts +5 -3
- package/dist/src/embeddings/factory.d.ts.map +1 -1
- package/dist/src/embeddings/factory.js +7 -3
- package/dist/src/embeddings/factory.js.map +1 -1
- package/dist/src/embeddings/models.d.ts +1 -1
- package/dist/src/embeddings/models.d.ts.map +1 -1
- package/dist/src/embeddings/retry.js +1 -1
- package/dist/src/embeddings/retry.js.map +1 -1
- package/dist/src/embeddings/types.d.ts +6 -0
- package/dist/src/embeddings/types.d.ts.map +1 -1
- package/dist/src/embeddings/types.js +11 -0
- package/dist/src/embeddings/types.js.map +1 -1
- package/dist/src/events/pending.d.ts +1 -1
- package/dist/src/events/pending.js +1 -1
- package/dist/src/git/GitRunner.d.ts +110 -30
- package/dist/src/git/GitRunner.d.ts.map +1 -1
- package/dist/src/git/GitRunner.js +511 -199
- package/dist/src/git/GitRunner.js.map +1 -1
- package/dist/src/git/GitWatchRegistry.d.ts +41 -35
- package/dist/src/git/GitWatchRegistry.d.ts.map +1 -1
- package/dist/src/git/GitWatchRegistry.js +61 -79
- package/dist/src/git/GitWatchRegistry.js.map +1 -1
- package/dist/src/git/gitPlace.d.ts +61 -0
- package/dist/src/git/gitPlace.d.ts.map +1 -0
- package/dist/src/git/gitPlace.js +74 -0
- package/dist/src/git/gitPlace.js.map +1 -0
- package/dist/src/git/sourceGitControl.d.ts +33 -21
- package/dist/src/git/sourceGitControl.d.ts.map +1 -1
- package/dist/src/git/sourceGitControl.js +211 -95
- package/dist/src/git/sourceGitControl.js.map +1 -1
- package/dist/src/git/sourceGitStatus.js +1 -1
- package/dist/src/git/sourceGitStatus.js.map +1 -1
- package/dist/src/git/untrackedStats.js +1 -1
- package/dist/src/git/untrackedStats.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lifecycle.d.ts +9 -12
- package/dist/src/lifecycle.d.ts.map +1 -1
- package/dist/src/lifecycle.js +2 -1
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/routes/approve.d.ts +1 -1
- package/dist/src/routes/approve.js +1 -1
- package/dist/src/routes/events.d.ts +1 -1
- package/dist/src/routes/events.js +1 -1
- package/dist/src/routes/git.d.ts +7 -7
- package/dist/src/routes/git.d.ts.map +1 -1
- package/dist/src/routes/git.js +201 -72
- package/dist/src/routes/git.js.map +1 -1
- package/dist/src/routes/health.d.ts +6 -6
- package/dist/src/routes/health.js +12 -12
- package/dist/src/routes/helpers/sourceNotConfigured.d.ts +1 -1
- package/dist/src/routes/helpers/sourceNotConfigured.js +1 -1
- package/dist/src/routes/helpers/uriGuard.d.ts +1 -1
- package/dist/src/routes/helpers/uriGuard.d.ts.map +1 -1
- package/dist/src/routes/helpers/uriGuard.js +3 -2
- package/dist/src/routes/helpers/uriGuard.js.map +1 -1
- package/dist/src/routes/helpers.d.ts +11 -4
- package/dist/src/routes/helpers.d.ts.map +1 -1
- package/dist/src/routes/helpers.js +13 -6
- package/dist/src/routes/helpers.js.map +1 -1
- package/dist/src/routes/list.d.ts +1 -1
- package/dist/src/routes/list.d.ts.map +1 -1
- package/dist/src/routes/list.js +3 -4
- package/dist/src/routes/list.js.map +1 -1
- package/dist/src/routes/pending.d.ts +4 -4
- package/dist/src/routes/pending.js +9 -9
- package/dist/src/routes/policies.d.ts +10 -8
- package/dist/src/routes/policies.d.ts.map +1 -1
- package/dist/src/routes/policies.js +31 -25
- package/dist/src/routes/policies.js.map +1 -1
- package/dist/src/routes/raw.d.ts +1 -1
- package/dist/src/routes/raw.d.ts.map +1 -1
- package/dist/src/routes/raw.js +66 -12
- package/dist/src/routes/raw.js.map +1 -1
- package/dist/src/routes/read.d.ts +1 -1
- package/dist/src/routes/read.js +1 -1
- package/dist/src/routes/search.d.ts +1 -1
- package/dist/src/routes/search.js +1 -1
- package/dist/src/routes/sources.d.ts +63 -6
- package/dist/src/routes/sources.d.ts.map +1 -1
- package/dist/src/routes/sources.js +494 -382
- package/dist/src/routes/sources.js.map +1 -1
- package/dist/src/routes/upload.d.ts +1 -1
- package/dist/src/routes/upload.js +1 -1
- package/dist/src/routes/write.d.ts +2 -2
- package/dist/src/routes/write.js +2 -2
- package/dist/src/services/BridgeService.d.ts +21 -35
- package/dist/src/services/BridgeService.d.ts.map +1 -1
- package/dist/src/services/BridgeService.js +42 -88
- package/dist/src/services/BridgeService.js.map +1 -1
- package/dist/src/services/FilesystemService.d.ts +4 -3
- package/dist/src/services/FilesystemService.d.ts.map +1 -1
- package/dist/src/services/FilesystemService.js +112 -34
- package/dist/src/services/FilesystemService.js.map +1 -1
- package/dist/src/services/FilesystemUiService.d.ts +4 -1
- package/dist/src/services/FilesystemUiService.d.ts.map +1 -1
- package/dist/src/services/FilesystemUiService.js +66 -25
- package/dist/src/services/FilesystemUiService.js.map +1 -1
- package/dist/src/services/SyncScheduler.d.ts +12 -4
- package/dist/src/services/SyncScheduler.d.ts.map +1 -1
- package/dist/src/services/SyncScheduler.js +37 -8
- package/dist/src/services/SyncScheduler.js.map +1 -1
- package/dist/src/services/baselineRepair.d.ts +13 -3
- package/dist/src/services/baselineRepair.d.ts.map +1 -1
- package/dist/src/services/baselineRepair.js +13 -3
- package/dist/src/services/baselineRepair.js.map +1 -1
- package/dist/src/services/declaredSources.d.ts +132 -43
- package/dist/src/services/declaredSources.d.ts.map +1 -1
- package/dist/src/services/declaredSources.js +118 -45
- package/dist/src/services/declaredSources.js.map +1 -1
- package/dist/src/store/dataFormats.d.ts +17 -2
- package/dist/src/store/dataFormats.d.ts.map +1 -1
- package/dist/src/store/dataFormats.js +18 -3
- package/dist/src/store/dataFormats.js.map +1 -1
- package/dist/src/sync/DeltaSyncRunner.d.ts.map +1 -1
- package/dist/src/sync/DeltaSyncRunner.js +144 -4
- package/dist/src/sync/DeltaSyncRunner.js.map +1 -1
- package/dist/src/sync/SyncJobQueue.d.ts +7 -2
- package/dist/src/sync/SyncJobQueue.d.ts.map +1 -1
- package/dist/src/sync/SyncJobQueue.js +16 -10
- package/dist/src/sync/SyncJobQueue.js.map +1 -1
- package/dist/src/sync/SyncWatchService.d.ts +21 -4
- package/dist/src/sync/SyncWatchService.d.ts.map +1 -1
- package/dist/src/sync/SyncWatchService.js +115 -10
- package/dist/src/sync/SyncWatchService.js.map +1 -1
- package/dist/src/sync/categoryInference.d.ts +1 -1
- package/dist/src/sync/categoryInference.js +1 -1
- package/dist/src/tools/execCtx.d.ts +2 -2
- package/dist/src/tools/execCtx.js +2 -2
- package/dist/src/tools/fs_read.d.ts.map +1 -1
- package/dist/src/tools/fs_read.js +5 -0
- package/dist/src/tools/fs_read.js.map +1 -1
- package/dist/src/usecases/admitSourceRoot.d.ts +65 -0
- package/dist/src/usecases/admitSourceRoot.d.ts.map +1 -0
- package/dist/src/usecases/admitSourceRoot.js +85 -0
- package/dist/src/usecases/admitSourceRoot.js.map +1 -0
- package/dist/src/usecases/browseSourceFolders.d.ts +85 -0
- package/dist/src/usecases/browseSourceFolders.d.ts.map +1 -0
- package/dist/src/usecases/browseSourceFolders.js +210 -0
- package/dist/src/usecases/browseSourceFolders.js.map +1 -0
- package/dist/src/usecases/createDeclaredSources.d.ts +13 -2
- package/dist/src/usecases/createDeclaredSources.d.ts.map +1 -1
- package/dist/src/usecases/createDeclaredSources.js +39 -0
- package/dist/src/usecases/createDeclaredSources.js.map +1 -1
- package/dist/src/usecases/embeddingBacklog.js +1 -1
- package/dist/src/usecases/embeddingBacklog.js.map +1 -1
- package/dist/src/usecases/list.d.ts.map +1 -1
- package/dist/src/usecases/list.js +83 -26
- package/dist/src/usecases/list.js.map +1 -1
- package/dist/src/usecases/listAttachableDevices.d.ts +13 -0
- package/dist/src/usecases/listAttachableDevices.d.ts.map +1 -0
- package/dist/src/usecases/listAttachableDevices.js +10 -0
- package/dist/src/usecases/listAttachableDevices.js.map +1 -0
- package/dist/src/usecases/listSourcePackages.d.ts.map +1 -1
- package/dist/src/usecases/listSourcePackages.js +2 -5
- package/dist/src/usecases/listSourcePackages.js.map +1 -1
- package/dist/src/usecases/purgeExcludedPoints.d.ts.map +1 -1
- package/dist/src/usecases/purgeExcludedPoints.js +3 -2
- package/dist/src/usecases/purgeExcludedPoints.js.map +1 -1
- package/dist/src/usecases/readFiles.d.ts.map +1 -1
- package/dist/src/usecases/readFiles.js +99 -1
- package/dist/src/usecases/readFiles.js.map +1 -1
- package/dist/src/usecases/reconcileSourcePackageRoots.d.ts +1 -1
- package/dist/src/usecases/reconcileSourcePackageRoots.d.ts.map +1 -1
- package/dist/src/usecases/reconcileSourcePackageRoots.js +2 -1
- package/dist/src/usecases/reconcileSourcePackageRoots.js.map +1 -1
- package/dist/src/usecases/revokePrincipal.d.ts +7 -5
- package/dist/src/usecases/revokePrincipal.d.ts.map +1 -1
- package/dist/src/usecases/revokePrincipal.js +29 -7
- package/dist/src/usecases/revokePrincipal.js.map +1 -1
- package/dist/src/usecases/scheduleSourceSync.d.ts +7 -0
- package/dist/src/usecases/scheduleSourceSync.d.ts.map +1 -1
- package/dist/src/usecases/scheduleSourceSync.js +6 -5
- package/dist/src/usecases/scheduleSourceSync.js.map +1 -1
- package/dist/src/usecases/syncPointWrites.d.ts +2 -0
- package/dist/src/usecases/syncPointWrites.d.ts.map +1 -1
- package/dist/src/usecases/syncPointWrites.js +5 -1
- package/dist/src/usecases/syncPointWrites.js.map +1 -1
- package/dist/src/usecases/triggerSourceSync.d.ts +46 -0
- package/dist/src/usecases/triggerSourceSync.d.ts.map +1 -0
- package/dist/src/usecases/triggerSourceSync.js +75 -0
- package/dist/src/usecases/triggerSourceSync.js.map +1 -0
- package/dist/src/usecases/uploadBinary.d.ts +2 -1
- package/dist/src/usecases/uploadBinary.d.ts.map +1 -1
- package/dist/src/usecases/uploadBinary.js +2 -2
- package/dist/src/usecases/uploadBinary.js.map +1 -1
- package/dist/src/usecases/vector/endpointGuard.d.ts +1 -1
- package/dist/src/usecases/vector/endpointGuard.js +1 -1
- package/dist/src/usecases/vectorMaintenance.js +1 -1
- package/dist/src/usecases/vectorMaintenance.js.map +1 -1
- package/dist/src/usecases/write/content.js +1 -1
- package/dist/src/vector/qdrantClient.d.ts +1 -1
- package/dist/src/vector/qdrantClient.js +1 -1
- package/dist/src/wiring.d.ts +3 -0
- package/dist/src/wiring.d.ts.map +1 -1
- package/dist/src/wiring.js +72 -6
- package/dist/src/wiring.js.map +1 -1
- package/dist/tools/fs_list.json +1 -1
- package/dist/tools/fs_read.json +1 -1
- package/dist/tools/fs_search.json +1 -1
- package/dist/tools/fs_write.json +1 -1
- package/instructions/brain-model.md +1 -1
- package/package.json +212 -114
- package/rules/brain-rules.md +2 -2
- package/skills/brain-start/SKILL.md +1 -1
- package/skills/connect-business-data/SKILL.md +3 -3
- package/skills/create-package/SKILL.md +26 -26
- package/skills/create-package/references/config-and-credentials.md +16 -13
- package/skills/create-package/references/connectors-and-uri-policies.md +39 -25
- package/skills/create-package/references/manifest-and-discovery.md +12 -12
- package/skills/create-package/references/runtime-and-build.md +19 -19
- package/skills/create-package/references/skills-and-context.md +36 -20
- package/skills/create-package/references/surfaces-and-design.md +16 -16
- package/skills/create-package/references/team-and-workflows.md +2 -2
- package/skills/create-package/references/test-and-ship.md +21 -21
- package/skills/create-package/references/tools-and-schemas.md +5 -5
- package/skills/create-package/scripts/{check-package.sh → check-package.mjs} +79 -65
- package/skills/create-package/scripts/{verify-loaded.sh → verify-loaded.mjs} +156 -162
- package/skills/git/SKILL.md +53 -51
- package/skills/manage-files/SKILL.md +22 -25
- package/skills/manage-sources/SKILL.md +128 -87
- package/skills/manage-uri-policy/SKILL.md +18 -15
- package/skills/organize/SKILL.md +1 -1
- package/skills/sync/SKILL.md +3 -3
- package/tools/fs_list.json +1 -1
- package/tools/fs_read.json +1 -1
- package/tools/fs_search.json +1 -1
- package/tools/fs_write.json +1 -1
- package/workflows/sources-digest.json +1 -1
- package/dist/src/connectors/LocalDiskConnector.d.ts +0 -171
- package/dist/src/connectors/LocalDiskConnector.d.ts.map +0 -1
- package/dist/src/connectors/LocalDiskConnector.js +0 -1233
- package/dist/src/connectors/LocalDiskConnector.js.map +0 -1
- package/docs/drive-overview.md +0 -59
- package/skills/manage-files/scripts/copy.sh +0 -7
- package/skills/manage-files/scripts/create.sh +0 -7
- package/skills/manage-files/scripts/mkdir.sh +0 -7
- package/skills/manage-files/scripts/modify.sh +0 -7
- package/skills/manage-files/scripts/upload.sh +0 -16
- package/skills/manage-files/scripts/write.sh +0 -29
- package/skills/manage-sources/scripts/reembed.sh +0 -23
|
@@ -59,16 +59,16 @@ export function api(state: NotesState) {
|
|
|
59
59
|
|
|
60
60
|
- **The lifecycle FILE is optional; `init` inside it is not.** A package with no routes and no tool handlers (markdown, schemas, connectors) ships no `src/lifecycle.ts` at all. A lifecycle module without `init` is discarded with a warning and `start`/`drain`/`stop`/`api`/`onBootstrapComplete`/`provisionProject`/`deprovisionProject`/`onPrincipalRevoked`/`onCredentialChanged`/`health` are never wired.
|
|
61
61
|
- **`init` returning nothing** leaves the dispatch state `undefined` — symptom: every request returns 503 `Package not initialized — lifecycle failed to load`, though the route files loaded and nothing logged an error.
|
|
62
|
-
- **Peer lookups belong in `onBootstrapComplete(state, ctx)`** — it fires once after every package's `init` and `start` have resolved. Elsewhere load order is not guaranteed: `getPackageApi()` returns `undefined` intermittently, fine on one boot, silently degraded on the next. From a non-
|
|
62
|
+
- **Peer lookups belong in `onBootstrapComplete(state, ctx)`** — it fires once after every package's `init` and `start` have resolved. Elsewhere load order is not guaranteed: `getPackageApi()` returns `undefined` intermittently, fine on one boot, silently degraded on the next. From a non-native package `undefined` may also be a trust **denial** (audited `denied:untrusted` / `denied:trust-mismatch`), not a missing package.
|
|
63
63
|
- **`onPrincipalRevoked(state, event)` closes; it never decides.** The host calls it when a user is disabled, deleted or has their sessions reset (no `event.projectId`), or loses one project (`membership_removed` / `project_archived`, with `projectId`). Stop what you hold open for `event.userId` — sockets, running jobs, pollers — through your existing stop, and return promptly: the call is bounded by a timeout, and the user is already refused everywhere else, so never re-check access here. Only a host-installed package's lifecycle runs at all; a `_packages/` drop never receives it.
|
|
64
64
|
|
|
65
|
-
**`init` receives** `selfId`, `selfTrust`, `data { dataDir, globalDir?, fileStore<T>, jsonlAppender<T>, dataPath, logger }`, `host { id }`, `packages { isLoaded, getPackageApi<T> }`, `credentials?`, `sessions? { createProjectFactory }`, `config { get }`, lazy `platform` facts,
|
|
65
|
+
**`init` receives** `selfId`, `selfTrust`, `data { dataDir, globalDir?, fileStore<T>, jsonlAppender<T>, dataPath, logger }`, `host { id }`, `packages { isLoaded, getPackageApi<T> }`, `credentials?`, `sessions? { createProjectFactory }`, `config { get }`, lazy `platform` facts, native `hostPorts`, `logger`. Platform facts throw before publication and are read where used; bootstrap completion receives the same `platform`, rather than copied aggregates. `dataDir` is **per project**: `<projectsRoot>/<projectId>/data/<zone>/`, mode `0700`, with `logs/`. Native packages (host-installed, `@neuralis/*` and the deploy's own deps) additionally receive `data.globalDir` — a project-independent platform home under the app zone, where their process-global state and their `logs/` live; a project-installed package like yours has NO `globalDir` (its state is project data by nature), and `ctx.data.globalDir` is `undefined` for it.
|
|
66
66
|
|
|
67
67
|
**Write through `dataPath`, and hold ONE appender.** `jsonlAppender()` takes a RESOLVED path, not a string — `ctx.data.dataPath('events.jsonl')` produces one, and it refuses anything that would leave your own data dir (an absolute path, or a relative one that climbs out), so you cannot write into a sibling package's data by accident. The APPENDER creates any missing intermediate directories when it writes; `dataPath` itself only resolves, so if you hand its result to your own `fs` call the directories are yours to create. Keep ONE appender for the life of the package: its write ordering is per-instance, so a fresh one per call gives up the guarantee that entries written together stay together. (The WASM guest plane's `ctx.data.*` also takes paths relative to the package data dir, but REPORTS failure as `{ ok: false, error }` instead of throwing — its answers cross into guest memory and carry a fixed code, never a filesystem path. Do not merge the two contracts in your head.)
|
|
68
68
|
|
|
69
|
-
**The logger is levellable and append-only.** `ctx.data.logger` is a `PackageLogger` already pointed at `<dataDir>/logs/` (for a
|
|
69
|
+
**The logger is levellable and append-only.** `ctx.data.logger` is a `PackageLogger` already pointed at `<dataDir>/logs/` (for a native package: `<globalDir>/logs/`); it resolves its level at every CALL — your package's entry in the operator's `packageLogLevelOverrides` (keyed on the `pkg` field of your log lines: your zone path, `runtime/packages/<dir>` for a drop, the bare name for `@neuralis/*`) → the global `packageLogLevel` → `debug`. Both are platform keys; your manifest declares nothing for them. Never cache `logger.level` in a constructor: the level is live, so a captured value stays pinned after an administrator edit. Every line is appended (O(1)), so the file is OLDEST-first: read "the newest N" with `JsonlAppender.readNewest(path, n)` (`@neuralis/package-system/data`), which takes the tail and then the rotated `.1`, `.2` … generations — never the first lines.
|
|
70
70
|
|
|
71
|
-
`onCredentialChanged(state, event)` receives `{credentialId, scope, change}` after a write or delete; re-resolve a changed secret through `ctx.credentials`, never through the event. `drain(state)` finishes or checkpoints work before `stop`, and `health(state)` returns structured checks with closed status/reason values. Runtime fan-outs are bounded. A
|
|
71
|
+
`onCredentialChanged(state, event)` receives `{credentialId, scope, change}` after a write or delete; re-resolve a changed secret through `ctx.credentials`, never through the event. `drain(state)` finishes or checkpoints work before `stop`, and `health(state)` returns structured checks with closed status/reason values. Runtime fan-outs are bounded. A native package exposes its declared `provides` implementations on the state returned by standalone `init(ctx)`. The discovered adapter retains `state.services` as `PackageRuntimeApi.services`, separately from the public `api(state)`; the host resolves them by contract id.
|
|
72
72
|
|
|
73
73
|
Use those contracts instead of module globals — `createProjectFactory` for per-project services, `ctx.credentials.*` for secrets. Cross-package calls carry the caller (`getPackageApi<T>(id, { userId, projectId, callerId, callerTrust })`) and are trust-gated and audited. Never call another package over MCP.
|
|
74
74
|
|
|
@@ -101,7 +101,7 @@ export const POST: RouteHandler = async (req, state) => {
|
|
|
101
101
|
|
|
102
102
|
**The feature gate runs before your handler**; never re-check the route's own `feature` inside it.
|
|
103
103
|
|
|
104
|
-
> **A route with no `feature` is reachable by any authenticated project member.** It does not "require
|
|
104
|
+
> **A route with no `feature` is reachable by any authenticated project member.** It does not "require native trust". Symptom: no error anywhere — a viewer-role user or an agent's skill script successfully calls an endpoint you assumed was admin-only, and you find out in an audit, not in a test. Declare a `feature` on every route not deliberately open to all members.
|
|
105
105
|
|
|
106
106
|
**Identity.** `req.session` (`userId`, `projectId`, `agentId`, `role`, `grantedFeatures`, `priority`, `agentAccess`, `limits`) is the only identity — never re-state it in a body or query, never trust a caller-supplied `projectId`; best case the gateway rejects with 403 `project_mismatch`, worst case you built a confused deputy. `req.interactive` is a transport marker (browser session vs machine ticket), **never identity**, and is absent on sandboxed dispatch — read it as strict `=== true` or you hand sandboxed callers the privileged human branch.
|
|
107
107
|
|
|
@@ -115,17 +115,17 @@ Enforced automatically by the dispatchers; your package writes no code for it. L
|
|
|
115
115
|
|
|
116
116
|
| | tool calls / min | route calls / min | tool timeout |
|
|
117
117
|
|---|---|---|---|
|
|
118
|
-
|
|
|
118
|
+
| native | unlimited | unlimited | none effectively enforced |
|
|
119
119
|
| trusted | 100 | 300 | 120 s |
|
|
120
120
|
| untrusted | 10 | 30 | 30 s |
|
|
121
121
|
|
|
122
|
-
Only the **trusted** tier's tool rate limit and timeout are administrator-tunable; the rest are hardcoded security floors. Exceeding a route limit returns **429**, and every non-
|
|
122
|
+
Only the **trusted** tier's tool rate limit and timeout are administrator-tunable; the rest are hardcoded security floors. Exceeding a route limit returns **429**, and every non-native route access is audited. Trust is assigned by where the package came from — declaring `access.trust` yourself changes none of it.
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
126
|
## 6. The sandbox — what a dropped package can do
|
|
127
127
|
|
|
128
|
-
A `_packages/` drop runs in a WASM sandbox, untrusted by default; an administrator may elevate it to trusted.
|
|
128
|
+
A `_packages/` drop runs in a WASM sandbox, untrusted by default; an administrator may elevate it to trusted (Admin → Packages, or `neuralis package trust <package> trusted`).
|
|
129
129
|
|
|
130
130
|
**WASM here is the TOOL and ROUTE runtime — it never renders UI.** There is no WASM UI engine and none can be declared: the `wasm-ui` renderer was removed from the surface contract and a manifest still declaring it fails validation. A sandboxed WASM tool feeds a card exactly like any other tool — by returning `structuredContent` — and the UI itself ships as a self-contained `iframe` asset (`references/surfaces-and-design.md`).
|
|
131
131
|
|
|
@@ -162,14 +162,14 @@ failure (`cross_package_denied`, `route_not_bridgeable` for streaming/binary
|
|
|
162
162
|
routes, `route_response_too_large`…); `ok: true` carries the route's own
|
|
163
163
|
`status`/`body` — a 403 arrives there. `ctx.fs.read/list/search/write` are thin
|
|
164
164
|
wrappers over brain-core's `read`/`list`/`search`/`write` routes
|
|
165
|
-
(`
|
|
165
|
+
(`files.read`/`files.search`/`files.write` per op); `write` takes one VERB KEY
|
|
166
166
|
(`create`/`overwrite`/`edit`/`insert`/`lines`/`fields`/`move`/`copy`/`mkdir`/`revert`/`delete`). The route CATALOG —
|
|
167
167
|
which routes exist and which feature each needs — lives on the per-package
|
|
168
168
|
"API & usage" docs pages:
|
|
169
|
-
<https://docs.neuralisapp.com/agent-core/
|
|
170
|
-
<https://docs.neuralisapp.com/brain-core/
|
|
171
|
-
<https://docs.neuralisapp.com/admin/
|
|
172
|
-
<https://docs.neuralisapp.com/machine-core/
|
|
169
|
+
<https://docs.neuralisapp.com/agent-core/routes> ·
|
|
170
|
+
<https://docs.neuralisapp.com/brain-core/routes> ·
|
|
171
|
+
<https://docs.neuralisapp.com/admin/routes> ·
|
|
172
|
+
<https://docs.neuralisapp.com/machine-core/routes> — mechanism:
|
|
173
173
|
<https://docs.neuralisapp.com/package-system/routes>.
|
|
174
174
|
|
|
175
175
|
`callRoute` denials return `{ ok: false, error: '…' }` rather than throwing — a
|
|
@@ -241,13 +241,13 @@ Only a package with `src/tools/*.ts`, `src/routes/*.ts` or `src/lifecycle.ts` ne
|
|
|
241
241
|
|
|
242
242
|
**Fail-closed:** `src/routes/*` present with no valid `dist/routes.json` ⇒ the package wires **no** route at all and loads `partial` (`build-missing`), logging `declares route sources but has no valid dist/routes.json — failing closed`. Tools may still work, which makes it look like a routing bug. Rebuild.
|
|
243
243
|
|
|
244
|
-
**Three triggers:**
|
|
244
|
+
**Three triggers:** **Build** in Admin → Packages or `neuralis package build <package>` (a package manager's action: build + reload + audit — the recommended path); **automatic on install** when `runtime.type` is `wasm` and source is present, where failure is **non-fatal** (the installer warns, the package installs `partial` — this is why "it installed fine but my tool 503s" happens); or the CLI **by absolute path**:
|
|
245
245
|
|
|
246
246
|
```bash
|
|
247
247
|
node <install-root>/node_modules/.bin/neuralis-build <absolute package root>
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
A
|
|
250
|
+
A native package's UI uses the same CLI in `ui` mode — `neuralis-build ui <absolute package root>` bundles the `app.module` entry to `dist/app/` and fails the build if React or another host-shared module would be bundled into it (`BUNDLED_SHARED_MODULE` — two Reacts break every hook), if it imports a module of another UI package that package's `app.module.provides` does not list (`UNDECLARED_SHARED_MODULE`), or if it reads React with no resolvable `react` (`NO_REACT`). Beside the bundle it writes `shared-imports.json` (host API version, React major, shared ids read); the host refuses a module whose record no longer matches it, so rebuild after every platform upgrade.
|
|
251
251
|
|
|
252
252
|
`npx neuralis-build` from a project data folder **does not work** — no module tree above it, so npx goes to the public registry and dies with `E404 'neuralis-build@*' is not in this registry`.
|
|
253
253
|
|
|
@@ -255,9 +255,9 @@ A first-party package's UI uses the same CLI in `ui` mode — `neuralis-build ui
|
|
|
255
255
|
|
|
256
256
|
---
|
|
257
257
|
|
|
258
|
-
## 7b. Developing a
|
|
258
|
+
## 7b. Developing a native package from ITS OWN repository
|
|
259
259
|
|
|
260
|
-
A column-(a) package does not have to live inside the platform's tree, and for a real
|
|
260
|
+
A column-(a) package does not have to live inside the platform's tree, and for a real native
|
|
261
261
|
package it should not. The dependency line is the trust act, and a dependency can point at any
|
|
262
262
|
directory on the deploying machine — so the package gets its own repository, its own git history and
|
|
263
263
|
its own release cadence, and the deployment's image build installs it from that directory (or from a
|
|
@@ -311,7 +311,7 @@ report a suite as green in an environment you did not run it in.
|
|
|
311
311
|
|
|
312
312
|
1. **git remote** — source backup and review. Changes nothing about the deployment.
|
|
313
313
|
2. **dependency registration + image rebuild** — the operator's trust act, then the build that
|
|
314
|
-
installs it. THIS is what makes the package discovered and loaded in-process with
|
|
314
|
+
installs it. THIS is what makes the package discovered and loaded in-process with native
|
|
315
315
|
trust.
|
|
316
316
|
3. **mount** — binds the directory over the installed copy for live editing (build in the folder,
|
|
317
317
|
restart). Plumbing only; without (2) the package is still invisible.
|
|
@@ -346,7 +346,7 @@ every agent that can see it.
|
|
|
346
346
|
| **503 `code-runtime-unavailable`** | the package loaded `partial`; read the reason |
|
|
347
347
|
| `pkg add` or the image build refuses the package, or it is absent and Admin health lists it under `builtin-packages` | it failed the loader's admission check (named per file — a tool in `tools/` missing `x-neuralis.annotations.category` is the usual one: the schema validator alone passes it, the native-tool check does not), or it collides with another package over a service, an OAuth prefix or a config key; only that package is left out |
|
|
348
348
|
|
|
349
|
-
Partial reasons: `build-missing` (no sandbox artifact — rebuild), `runtime-unavailable` (the code runtime crashed, or an MCP subprocess failed to spawn), `untrusted-node` (a non-
|
|
349
|
+
Partial reasons: `build-missing` (no sandbox artifact — rebuild), `runtime-unavailable` (the code runtime crashed, or an MCP subprocess failed to spawn), `untrusted-node` (a non-native package declared or defaulted to the in-process `node` runtime), `untrusted-mcp` (a non-native package declared the `mcp` subprocess runtime — nothing was spawned). **Both trust reasons have the same fix: declare `wasm`, the only code runtime available below native.** A `partial` package still registers every declarative contribution, which is why it looks half-working rather than broken.
|
|
350
350
|
|
|
351
351
|
---
|
|
352
352
|
|
|
@@ -61,10 +61,10 @@ arguments:
|
|
|
61
61
|
Target environment: {{environment}}
|
|
62
62
|
|
|
63
63
|
1. Run the checks — call this with `cwd: "${SKILL_DIR}"`:
|
|
64
|
-
`
|
|
64
|
+
`node "${SKILL_DIR}/scripts/verify.mjs" {{environment}}`
|
|
65
65
|
2. Read `${SKILL_DIR}/references/runbook.md` for the manual steps.
|
|
66
66
|
|
|
67
|
-
Done when `verify.
|
|
67
|
+
Done when `verify.mjs` exited 0 and every runbook step is finished or explicitly reported as skipped.
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
No registration call, no index file, no manifest entry: discovery finds the `SKILL.md`, sets `category: skill` from the folder, derives the id, enumerates the bundle, and turns `requiredFeatures` into the visibility gate.
|
|
@@ -115,18 +115,26 @@ lines and move detail into `references/`, one level deep, each with its own trig
|
|
|
115
115
|
## Ship scripts with a skill
|
|
116
116
|
|
|
117
117
|
There is **no separate script runner**: scripts run through the ordinary shell —
|
|
118
|
-
`execute action="shell" command="
|
|
118
|
+
`execute action="shell" command="node scripts/verify.mjs production"` at the skill's own working
|
|
119
119
|
directory, which is also what selects the execution provider (a source slug is not a kind).
|
|
120
120
|
|
|
121
|
+
**Write a script that must run everywhere in Node (`.mjs`).** The same skill runs under bash on
|
|
122
|
+
Linux, macOS and the Docker app and under PowerShell on a Windows device, which has no bash; `node`
|
|
123
|
+
is present wherever `neuralis request` is, because the CLI is a Node program. A bash script is a
|
|
124
|
+
POSIX-only script — on a Windows device it needs Git for Windows. Quote the path
|
|
125
|
+
(`node "${SKILL_DIR}/scripts/x.mjs"`, a Windows path may hold spaces), and pass JSON and free text
|
|
126
|
+
on stdin or through the `execute` tool's `env`, never as an argument: PowerShell 5.1 strips the
|
|
127
|
+
quotes inside one.
|
|
128
|
+
|
|
121
129
|
**Always pass the emitted working directory** — normally `cwd: "${SKILL_DIR}"`, or, for a
|
|
122
130
|
connector-backed skill, the directory URI itself (`cwd: "<source>://bundle/skills/check"`), never
|
|
123
|
-
that URI rewritten as an
|
|
131
|
+
that URI rewritten as an absolute path. The default working directory is the project data
|
|
124
132
|
root, deliberately, so relative output does not land in the package tree: a body saying
|
|
125
|
-
`
|
|
133
|
+
`node scripts/verify.mjs` with no `cwd` produces a `Cannot find module` error.
|
|
126
134
|
|
|
127
135
|
Body substitutions: `${SKILL_DIR}`, `${SKILL_URI}`, `${SKILL_CONNECTOR}`, `${SKILL_NAME}`, `${SESSION_ID}`, `${NEURALIS_API}`, `${SKILL_CREDENTIALS_RESOLVED}`, `${SKILL_CREDENTIALS_MISSING}`, `${MISSING:<credential-id>}` (renders `(present: id)` / `(missing: id)`).
|
|
128
136
|
|
|
129
|
-
- Syntax is `${UPPER_SNAKE}` **only**. `{SKILL_DIR}` and `$SKILL_DIR` are not substitutions — they are left intact by design, the model passes the literal text to
|
|
137
|
+
- Syntax is `${UPPER_SNAKE}` **only**. `{SKILL_DIR}` and `$SKILL_DIR` are not substitutions — they are left intact by design, the model passes the literal text to the shell, the shell expands it to nothing, and your script path becomes `/scripts/verify.mjs`.
|
|
130
138
|
- **Argument injection is a different syntax and runs first**: `$ARGUMENTS`, `$1`…`$N` (positional, in args-object order), `{{name}}`. Only string values substitute, and `required: true` is **not** enforced — a missing argument renders empty and the model proceeds with a half-rendered body. `$ARGUMENTS` on a no-argument activation expands to the empty string — the agentskills.io/Claude Code semantic, kept so imported skills behave here as they do at home. Write the defensive shape yourself when the empty case matters ("if the line below is empty, do X"); a bare `$N` with nothing behind it stays LITERAL (dollar amounts in prose survive).
|
|
131
139
|
- Scripts must be non-interactive, answer `--help`, and bound their output. For app/host execution, an active skill’s local `SKILL_DIR` subtree is a read and cwd allowance in the shell path gate — not writable, and the only implicit path grant activation confers. Connector execution retains its provider’s gates.
|
|
132
140
|
|
|
@@ -138,10 +146,18 @@ execution: the activation envelope can supply a directory URI for an exec-capabl
|
|
|
138
146
|
that URI with a relative script, and never pass the sentinel as `cwd`.
|
|
139
147
|
|
|
140
148
|
**Authenticating back to the platform from app/host execution:** call package routes with
|
|
141
|
-
`neuralis request <METHOD> <package-id>/<route>` (`-f key=value` fields
|
|
142
|
-
body). It authenticates with the shell's per-stream `NEURALIS_SESSION_TOKEN` — the caller’s
|
|
149
|
+
`neuralis request <METHOD> <package-id>/<route>` (`-f key=value` fields; a JSON
|
|
150
|
+
body as `echo '<json>' | neuralis request … --input -`, which bash and PowerShell both parse). It authenticates with the shell's per-stream `NEURALIS_SESSION_TOKEN` — the caller’s
|
|
143
151
|
real identity, re-minted per call — and on a developer's own machine with their `neuralis login`;
|
|
144
|
-
your script never handles the ticket itself.
|
|
152
|
+
your script never handles the ticket itself. A Node script that makes several calls starts the
|
|
153
|
+
CLI with no shell: `execFileSync(process.execPath, [process.env.NEURALIS_CLI_ENTRY, 'request', 'GET',
|
|
154
|
+
'<package-id>/<route>'], { encoding: 'utf8' })` — every Neuralis runner exports `NEURALIS_CLI_ENTRY`,
|
|
155
|
+
the CLI's own JS entry. With it absent, spawn `neuralis` directly on Linux and macOS and stop with a
|
|
156
|
+
message naming the variable on Windows; never pass `shell: true` (cmd.exe re-reads every argument).
|
|
157
|
+
A script that only reshapes ONE answer reads it from a pipe instead —
|
|
158
|
+
`neuralis request GET <package-id>/<route> | node "${SKILL_DIR}/scripts/x.mjs"` — and must exit non-zero on an empty
|
|
159
|
+
or non-JSON stdin: a non-2xx leaves stdout empty, and neither shell reports the first stage's exit
|
|
160
|
+
code. It reaches ONLY package routes under
|
|
145
161
|
`/api/packages/<package-id>/…`; host-level `/api/*` routes (projects, auth, admin) reject it by design. **Never invent a bearer token and never ask the user for one.** `credentials:` is for **external** secrets only — each entry is both the credential-store id and the env-var name projected into the child process. A miss is non-fatal: it renders `(missing: <id>)` and is reported in the activation result, and the model never sees a value. **Credentials pushed by an activation are scoped to the conversation and the caller, and outlive the turn**;
|
|
146
162
|
app/host shell calls receive the union of the caller's still-active skills. Clear them with
|
|
147
163
|
`args.deactivate=true`. **Connector execution receives no platform ticket, credential env or
|
|
@@ -150,9 +166,9 @@ entirely — never promise that a platform-authenticated script runs there uncha
|
|
|
150
166
|
|
|
151
167
|
**`neuralis request` keeps a route's error body:** a non-2xx answer prints an `HTTP <code>` line
|
|
152
168
|
and the body on stderr, leaves stdout empty and exits 1. So a script that passes the answer
|
|
153
|
-
through needs nothing more, and one that consumes it (`
|
|
154
|
-
|
|
155
|
-
branch on into a `jq` error. Branch on the code by reading that `HTTP` line from stderr; never send
|
|
169
|
+
through needs nothing more, and one that consumes it (`execFileSync` throws on the exit 1;
|
|
170
|
+
`X="$(neuralis request …)"` under `set -e`; `--jq`) stops before a projection can turn the `{code,error}` your SKILL.md tells the model to
|
|
171
|
+
branch on into a `jq` error. Branch on the code by reading that `HTTP` line from stderr (`err.stderr` in Node); never send
|
|
156
172
|
the stderr to `/dev/null`.
|
|
157
173
|
|
|
158
174
|
**For any other HTTP call — an external API — never let curl throw away the error body: `-f` is
|
|
@@ -162,7 +178,7 @@ depends on what the script does with the response:
|
|
|
162
178
|
|
|
163
179
|
- **passes it through** (curl's output IS the script's output) → `curl --fail-with-body -sS …`.
|
|
164
180
|
The body reaches the model, the exit stays non-zero, nothing else changes. (Needs curl ≥ 7.76,
|
|
165
|
-
March 2021; the
|
|
181
|
+
March 2021; the Docker image carries 8.21 — a native host's is its own, check `curl --version`.)
|
|
166
182
|
- **consumes it** (captured into a variable, or piped into a `jq` projection) → capture the status
|
|
167
183
|
instead: `RESPONSE="$(curl -sS -w '\n%{http_code}' …)"`, split `STATUS` off the tail, and print
|
|
168
184
|
the RAW body then `exit 1` **before** the projection runs. Under `set -e` a failing assignment
|
|
@@ -171,7 +187,7 @@ depends on what the script does with the response:
|
|
|
171
187
|
- **is documented as being piped by its CALLER** (`my-script.sh | jq …`) → write the error body to
|
|
172
188
|
**stderr**, or the caller's filter eats it exactly the same way one level up.
|
|
173
189
|
|
|
174
|
-
Neuralis' own
|
|
190
|
+
Neuralis' own native bundles are swept for both rules by a static test; a package you ship
|
|
175
191
|
should carry the equivalent check over its own `scripts/`, because nothing in the platform
|
|
176
192
|
inspects a third-party script for you.
|
|
177
193
|
|
|
@@ -215,7 +231,7 @@ missed a whole class:
|
|
|
215
231
|
|
|
216
232
|
3. **Decide "does this mutate?" from the INVOCATION LINE, never from the script
|
|
217
233
|
file.** A generic helper that takes its verb as an argument —
|
|
218
|
-
`
|
|
234
|
+
`node "${SKILL_DIR}/scripts/api.mjs" POST commit` — hides its own verb
|
|
219
235
|
behind a positional parameter. Keyed on the file, such a helper looks like a GET
|
|
220
236
|
and every one of its write routes vanishes from the audit. If you ship such a
|
|
221
237
|
helper, say so in its header comment; if you audit one, read the argument.
|
|
@@ -260,7 +276,7 @@ requiredFeatures:
|
|
|
260
276
|
|
|
261
277
|
What matters is that the key is top-level and its value parses to a **list**. Anything else is ignored. (The warning you get for a non-list says "expected a flow array" — read that as "expected a list"; the block form is equally valid.)
|
|
262
278
|
|
|
263
|
-
> **The nested form silently produces no gate.** A `requires:` key with an indented `features: [...]`
|
|
279
|
+
> **The nested form silently produces no gate.** A `requires:` key with an indented `features: [...]` is read by nothing, emits **no warning**, and your skill is visible to everyone. A non-array value at least logs `requiredFeatures: frontmatter ignored — expected a flow array of feature ids`; the nested block leaves no trace. Treat it as a security defect in waiting and grep for it before you ship.
|
|
264
280
|
|
|
265
281
|
**Hidden means absent.** A caller missing any listed feature does not see the file in the overview, the files catalog, the slash menu or the prompt — never a greyed-out row — and activating it returns the same generic `Skill not found` as a typo. That is deliberate non-inference: do not "improve" it into a forbidden error, and debug by inspecting the *caller's* granted features, not the skill. Naming a feature no package provides makes the file reachable only by wildcard (`*`) holders — owner-strength roles only. To reach the project `admin` tier as well, DECLARE the feature in `providesFeatures` and grant it in your own `requires.defaultRoleGrants.admin` (for a package installed AFTER project creation — not shipped with the platform — grants to admin-strength roles are never auto-applied: the feature surfaces in the role catalog and an owner grants it by hand): the seeded `admin` role carries no `'*'` wildcard, so an undeclared id never reaches it — a valid way to declare owner-only, but NOT a way to reach project admins.
|
|
266
282
|
|
|
@@ -275,7 +291,7 @@ Only `rules/` and `instructions/` inject by category. Choose a rule only for beh
|
|
|
275
291
|
- **A file with no `activation` block is injected every turn**; the default is `always`, and **`invocation: auto` is identical to `always`** — only `manual` keeps a file out of the prompt. There is no relevance-based injection.
|
|
276
292
|
- **`activation.target` accepts exactly two values**, `prompt-context` and `workspace`. Anything else — a tool-scoped selector, say — is cast through unvalidated and silently ignored, and the file still injects because its folder made it a rule. There is no tool-conditional injection.
|
|
277
293
|
|
|
278
|
-
**Only an installed
|
|
294
|
+
**Only an installed native package can ship a rule at all.** For anything else the loader sanitises at discovery: every `rules/` file is physically moved into the docs array so it can never inject, `overwrite` is forced false, and `activation.target: prompt-context` becomes `workspace`. A package found inside a synced source goes further — its rules and universal identity files (`AGENTS.md`, `CLAUDE.md`) are **default-OFF** until someone opts in per agent or per conversation, and its docs are server-pinned to manual. Design a dropped or synced package so its behaviour never depends on a rule.
|
|
279
295
|
|
|
280
296
|
Three craft rules for the floor: **one fact, one file** — copies of the same guidance drift apart; **floor versus method** — the always-on file carries only what must be obeyed every turn and the how-to goes in a skill, but a floor must **never** live only in a skill body, because an un-activated skill loses it entirely; **never hardcode the tool, skill or source inventory into a rule** — the runtime renders it already and your copy is wrong the moment the runtime changes.
|
|
281
297
|
|
|
@@ -303,7 +319,7 @@ listed as an agent, never summonable — and a skipped file in an installed pack
|
|
|
303
319
|
|
|
304
320
|
In both forms the agent is callable BY NAME through its declared `id`/`name`; a
|
|
305
321
|
filename-identity row without one (a `commands/*.md` skill fork) is listed in `<packages>`
|
|
306
|
-
but not summonable — reachable by address only (`uri`, host path,
|
|
322
|
+
but not summonable — reachable by address only (`uri`, host path, local path).
|
|
307
323
|
|
|
308
324
|
**Same key, two jobs: `allowed-tools` is ENFORCED on an agent file and ADVISORY on a skill.**
|
|
309
325
|
|
|
@@ -337,7 +353,7 @@ Two parsers run on the same file: **discovery** keeps every top-level key, **act
|
|
|
337
353
|
| `allowed-tools` | a CEILING on agent files (a `delegate` call intersects, never replaces); advisory on skills. Absent = inherit; `[]` = no tools. |
|
|
338
354
|
| `arguments` | real `{{name}}` / `$1` substitution; not validated, `required` not enforced. |
|
|
339
355
|
| `disable-model-invocation` | sets a field the overview and file catalog display. On a **skill** it gates nothing — the model can still activate it. Load-bearing only on an `instruction`/`rule`, or on a file with an explicit `activation.pinned` / `activation.target: prompt-context`. |
|
|
340
|
-
| `overwrite` | carried, but no replacement semantics exist; forced off for anything not
|
|
356
|
+
| `overwrite` | carried, but no replacement semantics exist; forced off for anything not native. |
|
|
341
357
|
| `hooks` | **dropped by the activation parser** — no hooks, no warning, no log line. Use the package-level `hooks.json`. |
|
|
342
358
|
| `user-invocable`, `contextMode`, `agentType`, `preferredModel`, `maxSteps`, `manualOnly` | accepted (the first for ecosystem compatibility) and read by nothing today. Zero effect, zero diagnostics. |
|
|
343
359
|
|
|
@@ -358,4 +374,4 @@ Two parsers run on the same file: **discovery** keeps every top-level key, **act
|
|
|
358
374
|
|
|
359
375
|
---
|
|
360
376
|
|
|
361
|
-
Full public reference: **https://docs.neuralisapp.com/package-system/skills** — and the five-category contribution contract at **https://docs.neuralisapp.com/package-system
|
|
377
|
+
Full public reference: **https://docs.neuralisapp.com/package-system/skills** — and the five-category contribution contract at **https://docs.neuralisapp.com/package-system**.
|
|
@@ -18,7 +18,7 @@ Every surface lives in `package.json` under `neuralis.app.surfaces[]` — a unio
|
|
|
18
18
|
| `dock` | `kind`, `id`, `label`, `action` | `icon`, `image`, `position`, `section`, `dock`, `tooltip`, `color` |
|
|
19
19
|
| `card` | `kind`, `id`, `type` | `title`, `render`, `match`, `bridge` |
|
|
20
20
|
|
|
21
|
-
**Every icon comes from ONE platform library** — about 270 curated lucide names in categories, the `ICON_LIBRARY` export of `@neuralis/package-system/icons`. Write the PascalCase name (`ChartLine`) or its lucide-kebab form (`chart-line`); a few aliases (`layout`, `terminal`, `admin`) also resolve. The same rule covers a surface `icon`, a team member's `appearance.icon` and a workflow template's `icon`. Any other name renders the fallback glyph, warns at load, and is an ERROR in `check-package.
|
|
21
|
+
**Every icon comes from ONE platform library** — about 270 curated lucide names in categories, the `ICON_LIBRARY` export of `@neuralis/package-system/icons`. Write the PascalCase name (`ChartLine`) or its lucide-kebab form (`chart-line`); a few aliases (`layout`, `terminal`, `admin`) also resolve. The same rule covers a surface `icon`, a team member's `appearance.icon` and a workflow template's `icon`. Any other name renders the fallback glyph, warns at load, and is an ERROR in `check-package.mjs`.
|
|
22
22
|
|
|
23
23
|
## Task: ship a standing panel
|
|
24
24
|
|
|
@@ -68,7 +68,7 @@ The smallest complete, **reachable** UI a project-dropped package can ship. Four
|
|
|
68
68
|
|
|
69
69
|
**Gate the widget, not the dock entry** — dock visibility inherits the target widget's `requires.features` automatically. Duplicating `requires` only creates drift: a user holding the widget's features but not your accidentally-different dock feature gets a widget they can never open.
|
|
70
70
|
|
|
71
|
-
**An app picture is optional: `"image": "app/shared/<name>.png"`.** A PNG, JPEG or WebP under `app/shared/`, at most 256 KiB, each path segment letters, digits, `.`, `_` or `-`. The dock, the app menu and the inbox show it in a fixed-size box to whoever can see the dock entry; keep the `icon` glyph — it renders until the picture arrives and whenever it cannot be shown. The platform judges the BYTES, never the extension: an SVG, a renamed document or an oversized file is not served and the glyph stays. An invalid path (absolute, remote, outside `app/shared/`, `..`, a non-raster extension) is a validation error that fails the whole package. `check-package.
|
|
71
|
+
**An app picture is optional: `"image": "app/shared/<name>.png"`.** A PNG, JPEG or WebP under `app/shared/`, at most 256 KiB, each path segment letters, digits, `.`, `_` or `-`. The dock, the app menu and the inbox show it in a fixed-size box to whoever can see the dock entry; keep the `icon` glyph — it renders until the picture arrives and whenever it cannot be shown. The platform judges the BYTES, never the extension: an SVG, a renamed document or an oversized file is not served and the glyph stays. An invalid path (absolute, remote, outside `app/shared/`, `..`, a non-raster extension) is a validation error that fails the whole package. `check-package.mjs` checks the path, the file and its bytes.
|
|
72
72
|
|
|
73
73
|
## Task: pick a renderer
|
|
74
74
|
|
|
@@ -76,17 +76,17 @@ The smallest complete, **reachable** UI a project-dropped package can ship. Four
|
|
|
76
76
|
|
|
77
77
|
| renderer | validates for | how it renders |
|
|
78
78
|
|---|---|---|
|
|
79
|
-
| `direct` | `access.trust: "
|
|
80
|
-
| `iframe` | any trust with a **relative** url; an absolute (`https://`, `//`, leading `/`) url needs trusted or
|
|
81
|
-
| `mcp` | trusted or
|
|
79
|
+
| `direct` | `access.trust: "native"` only — hard error otherwise | compiled React the package's **own** host entrypoint registers |
|
|
80
|
+
| `iframe` | any trust with a **relative** url; an absolute (`https://`, `//`, leading `/`) url needs trusted or native | a sandboxed document — every non-native path |
|
|
81
|
+
| `mcp` | trusted or native; widgets only, **rejected on cards** | a marker for imported external surfaces, not an authoring target |
|
|
82
82
|
|
|
83
|
-
**Use `iframe` with a relative url for a dropped package, `direct` for an installed
|
|
83
|
+
**Use `iframe` with a relative url for a dropped package, `direct` for an installed native one.** A project-dropped package is pinned untrusted at scan and capped at trusted; it can **never** be native, so `direct` stays unavailable even after an admin trust elevation. In a dropped package a missing `access.trust` is a warning and the package is treated as untrusted — `direct` in its trust-less manifest fails too. And `direct` is only half a declaration: the manifest entry alone renders nothing, because the host never imports package TSX — the package's own `install…HostComponents` entrypoint is what registers the component, and a native package ships that entrypoint as a prebuilt module named in `app.module` (built with `neuralis-build ui`).
|
|
84
84
|
|
|
85
85
|
An absolute url also needs the remote page to permit framing — `X-Frame-Options: DENY` or `frame-ancestors 'none'` (most large SaaS, banks, mail providers) renders blank whatever the host allows; drive those through the virtual-desktop / browser-automation capability instead.
|
|
86
86
|
|
|
87
87
|
### The entry lives in its own root, and it is self-contained
|
|
88
88
|
|
|
89
|
-
Two rules decide whether a relative `iframe` surface loads at all. Both are checked by `check-package.
|
|
89
|
+
Two rules decide whether a relative `iframe` surface loads at all. Both are checked by `check-package.mjs` before you ship.
|
|
90
90
|
|
|
91
91
|
**1. The canonical root.** Every asset-backed entry lives under its **own** dedicated directory:
|
|
92
92
|
|
|
@@ -159,7 +159,7 @@ The frame mounts `sandbox="allow-scripts allow-forms"` — deliberately **withou
|
|
|
159
159
|
|
|
160
160
|
**Nothing is pushed into a widget iframe.** There is no host→widget message channel: the frame posts nothing and listens for nothing. **A card is different** — see the next section: a sandboxed card gets a bounded, identity-free, strictly one-way `neuralis:card-data/v1` feed. **Do not write a widget that waits for a message**; it will wait forever.
|
|
161
161
|
|
|
162
|
-
The privileged `window.neuralis` JSON-RPC bridge (`submitApproval`, `requestData`) is a separate, opt-in layer for **cards**, gated by `bridge: { enabled: true }`, restricted to trusted/
|
|
162
|
+
The privileged `window.neuralis` JSON-RPC bridge (`submitApproval`, `requestData`) is a separate, opt-in layer for **cards**, gated by `bridge: { enabled: true }`, restricted to trusted/native, and further restricted at runtime to a **remote HTTPS origin that differs from the host's** — an untrusted package opting in is a hard validation error, and a host-served or same-origin frame is refused the bridge fail-closed even when trusted.
|
|
163
163
|
|
|
164
164
|
Asset serving is authenticated per request, scope-bound, traversal-safe, `Cache-Control: private, no-store`, `Referrer-Policy: no-referrer`, capped at 2 MiB per asset, and limited to an extension allowlist (html, css, js, mjs, map, json, images, fonts, txt) — `.ts`/`.tsx` are excluded so package internals never leak. Every asset carries a hard CSP: `default-src 'none'`, `script-src`/`style-src` `'self' 'unsafe-inline'` (an entry's own inline script and style are the point), `img-src 'self' data: blob:`, `font-src 'self' data:`, `connect-src 'self'`, `form-action 'none'`, `base-uri 'self'`, `frame-ancestors 'self'` and `sandbox allow-scripts allow-forms`. That last directive mirrors the sandbox attribute the frame already carries, so it costs your surface nothing — what it buys is that the document stays in an opaque origin even if it is ever opened *outside* the workspace frame. No CDN script, no remote font, no third-party XHR target — every source list is `'self'`-based, and in an opaque sandboxed document `'self'` resolves to the origin that served the document, which is what lets a bundle surface's own same-origin files load. So two shapes work and no others: **a single self-contained document**, or **one document plus its own sibling files under `assetMode: "bundle"`**.
|
|
165
165
|
|
|
@@ -193,7 +193,7 @@ Transparency resolves per-instance override → manifest `chrome.transparent`
|
|
|
193
193
|
|
|
194
194
|
A card binds to a tool through the tool's `x-neuralis.ui.cardType` matching the card's `type`: the handler returns `structuredContent`, the host resolves a card type, the registry maps it to a component, it renders inline in chat. An unknown card type falls back to a generic default card with a console warning. Three names must agree — the tool's `ui.cardType`, the card surface's `type`, and, for `direct`, the type your host entrypoint registered. **Render `running`, `success` and `error`**, or the card shows nothing while the tool runs and nothing when it fails.
|
|
195
195
|
|
|
196
|
-
**While the tool call is still streaming, `tool.input` is a partial JSON STRING, not the parsed object — a `direct` card must never `JSON.parse` (or whole-string-regex) it per delta.** That is O(n²) across a large call and a real per-commit performance cost class in the timeline. Use the exported streaming helpers from `@neuralis/agent-core/app/chat` instead: `previewInput(input, isPending)` for the standard bounded input preview, `extractStreamingField(input, field)` to stream ONE named string field (a command, a task, a question) progressively into your header or body — it reads the settled object and the partial buffer alike — `extractSettledArrayItems(input, field)` to list the already-closed elements of ONE array field while it streams, and `parseSettledObject(input)` as the O(1) "is it complete?" gate before your one real parse. None of them truncates the input; iframe cards need none of this (the `card-data/v1` snapshot already delivers the bounded streaming input).
|
|
196
|
+
**While the tool call is still streaming, `tool.input` is a partial JSON STRING, not the parsed object — a `direct` card must never `JSON.parse` (or whole-string-regex) it per delta.** That is O(n²) across a large call and a real per-commit performance cost class in the timeline. Use the exported streaming helpers from `@neuralis/agent-core/app/chat` instead: `previewInput(input, isPending)` for the standard bounded input preview, `extractStreamingField(input, field)` to stream ONE named string field (a command, a task, a question) progressively into your header or body — it reads the settled object and the partial buffer alike — `extractSettledArrayItems(input, field)` to list the already-closed elements of ONE array field while it streams (and `extractStreamingArrayTail(input, field)` for the element still open), and `parseSettledObject(input)` as the O(1) "is it complete?" gate before your one real parse. None of them truncates the input; iframe cards need none of this (the `card-data/v1` snapshot already delivers the bounded streaming input).
|
|
197
197
|
|
|
198
198
|
Cards may declare `match.state: "interaction-required" | "approval-required"`. An **approval card is preview-only** — the host wraps it with approve/deny chrome. Painting your own approve/deny buttons is a **warning, not a rejection** (the validator cannot scan HTML statically), so the package loads, ships duplicate controls, and users click a button that does nothing.
|
|
199
199
|
|
|
@@ -209,9 +209,9 @@ A sandboxed `iframe` card — and **only** a card — receives the tool's own da
|
|
|
209
209
|
|
|
210
210
|
**The channel is owner-gated.** Only a card whose registration is bound to its **own** package's tool receives the real input and result; a cardType that matches a foreign package's tool, or that no owner claims, gets a safe generic placeholder instead. This is not a cross-package data channel.
|
|
211
211
|
|
|
212
|
-
**The host owns the performance, not your card — with ONE exception: your card's height.** Stream pacing, coalescing, virtualizer retention and the single multiplexed realtime hub are all host-side. Do not write a scheduler, a keep-alive or a per-card `EventSource` into a card — a handful of per-surface streams exhausts the browser's connection pool, and a card that re-renders on every delta is what the host's pacing exists to prevent. And bound your MOUNT cost to what is visible: a compact body clipped at ~90px shows only a handful of rows, so do not mount hundreds of row elements into it (clamp the element count while collapsed and render the full set on expand — the
|
|
212
|
+
**The host owns the performance, not your card — with ONE exception: your card's height.** Stream pacing, coalescing, virtualizer retention and the single multiplexed realtime hub are all host-side. Do not write a scheduler, a keep-alive or a per-card `EventSource` into a card — a handful of per-surface streams exhausts the browser's connection pool, and a card that re-renders on every delta is what the host's pacing exists to prevent. And bound your MOUNT cost to what is visible: a compact body clipped at ~90px shows only a handful of rows, so do not mount hundreds of row elements into it (clamp the element count while collapsed and render the full set on expand — the native diff card renders 8 rows collapsed, 200 expanded), and defer any mount-time network fetch to idle or to expand so a fast scroll over your cards is not a request burst — and cache the fetch RESULT (the negative "nothing there" answer included) in a bounded module-level map keyed by stable identity, because the timeline remounts your card on every scroll-back and idle deferral alone only delays the refetch, it does not remove it.
|
|
213
213
|
|
|
214
|
-
The exception exists because the host CANNOT know it. The timeline is virtualized, so it must reserve a height for a row it has not measured yet, and reserving the wrong amount is exactly what makes a chat list jump when a card appears. That guess used to live in a host-side table that knew only the built-in cards; everything else shared one constant, measured wrong by up to 579px. A **
|
|
214
|
+
The exception exists because the host CANNOT know it. The timeline is virtualized, so it must reserve a height for a row it has not measured yet, and reserving the wrong amount is exactly what makes a chat list jump when a card appears. That guess used to live in a host-side table that knew only the built-in cards; everything else shared one constant, measured wrong by up to 579px. A **native `direct` card registered from your own host entrypoint** may therefore pass an `estimateHeight` model next to its renderer and mount policy:
|
|
215
215
|
|
|
216
216
|
```tsx
|
|
217
217
|
// module scope — NOT an inline arrow at the call site
|
|
@@ -235,7 +235,7 @@ Every rule below has already been violated at least once in this codebase:
|
|
|
235
235
|
- Keep it a **module-level reference**. The registry compares estimators by identity to decide whether a re-registration changed anything; a fresh closure per call turns every reconcile pass into a replace, a fingerprint bump and a retained-View eviction.
|
|
236
236
|
- Keep it **cheap and pure** — it runs inside the virtualizer's measurement arithmetic, for every unmeasured row, on every offset rebuild (and that rebuild sits in a pre-paint `flushSync` window, so an unmemoized parse is scroll jank). If it parses the payload, memoize by `tool` reference **together with every input the result depends on — the `status` it was computed at AND the wrap width**: the tool object mutates BY REFERENCE across pending→settle during a live stream, so a reference-only memo keeps serving the pending-era "Running…" height after the tool settles, and a memo that forgets the width keeps serving the previous panel size after a resize — silently cancelling your `widthScale` branch. Store `{ status, charsPerLine, height }` and recompute when either changes.
|
|
237
237
|
- Compute it **from the same data the component renders from**, ideally by calling the same parser. An estimator that models a different shape than the renderer is worse than none. For text bodies, count lines with the exported `countRenderedLines(text, charsPerLine)` (from `@neuralis/agent-core/app/chat`) — a flat `ceil(length / charsPerLine)` cannot see hard newlines and understated a real markdown row by ~3× (the measured worst overlap source in the timeline) — and pass a `charsPerLine` you have scaled to the live panel, never the bare constant (see the width rule below).
|
|
238
|
-
- **When unsure, round UP — and never return your COMPACT height for an expanded row.** Rows are absolutely positioned, so the next row sits at your row's offset plus your CACHED height: an over-estimate leaves a gap that closes on the first measurement, an under-estimate paints the next row on top of yours. If your card can expand, branch on `disclosure` and model the expanded body too. Skipping that branch is the worst measured bug in this whole surface — a
|
|
238
|
+
- **When unsure, round UP — and never return your COMPACT height for an expanded row.** Rows are absolutely positioned, so the next row sits at your row's offset plus your CACHED height: an over-estimate leaves a gap that closes on the first measurement, an under-estimate paints the next row on top of yours. If your card can expand, branch on `disclosure` and model the expanded body too. Skipping that branch is the worst measured bug in this whole surface — a native card estimated at 114px while rendering 1479px expanded.
|
|
239
239
|
- **Scale every characters-per-line constant by `widthScale`, because "round up" depends on how wide the panel is.** A chat panel is not one width: it is split, resized, docked and widened, and a constant calibrated at one width crosses into the *under*-reserve half of the rule above at every narrower one — measured live, one real row rendered 1251px in a 350px column while the fixed estimate reserved 792px, a 459px shortfall that paints the following row on top of it. Multiply your own constant with the exported `scaleCharsPerLine(authored, widthScale)` (same import as `countRenderedLines`) and use the result everywhere you count lines. Do NOT invent your own formula from the number: it is a ratio against the width the platform's constants were authored at, and the helper carries the degenerate-width floor.
|
|
240
240
|
|
|
241
241
|
`disclosure` carries the row's remembered expand/collapse state when it differs from your default; read it only if your card renders at two heights. `widthScale` carries how wide the row surface is relative to that authored reference width (`1` IS the reference; a narrow panel is below it, a wide one above). **Both are optional and an absent `widthScale` means `1`** — an estimator that ignores it behaves exactly as it did before the field existed, so an imported card needs no changes; reading it is purely an improvement to your card's first paint.
|
|
@@ -246,7 +246,7 @@ Declaring nothing is a legitimate choice: a REGISTERED card without a model gets
|
|
|
246
246
|
|
|
247
247
|
### Task: land your card's activity on the composer's Changes & Actions panel
|
|
248
248
|
|
|
249
|
-
Above the chat composer sits the Changes & Actions panel — the per-conversation aggregation of tool activity (Changes / Todos / Actions / Delegates tabs + the post-stream summary chip of changed files). Which tab your card's activity lands on is declared by the card, through the SAME registration that carries `estimateHeight` — the aggregation surfaces iterate the card registry and hardcode nothing (the
|
|
249
|
+
Above the chat composer sits the Changes & Actions panel — the per-conversation aggregation of tool activity (Changes / Todos / Actions / Delegates tabs + the post-stream summary chip of changed files). Which tab your card's activity lands on is declared by the card, through the SAME registration that carries `estimateHeight` — the aggregation surfaces iterate the card registry and hardcode nothing (the native filesystem card declares its own contract from its own package the same way).
|
|
250
250
|
|
|
251
251
|
**Direct lane** — pass a `composer` contract in `registerCardComponent`:
|
|
252
252
|
|
|
@@ -272,7 +272,7 @@ registerCardComponent('thing.result', ThingCard, {
|
|
|
272
272
|
|
|
273
273
|
**Manifest lane** (`iframe` cards, no JS): declare the minimal subset on the card surface — `"composer": { "tab": "changes", "badgeField": "label", "pathField": "uri" }`. `tab` targets EXISTING builtin tabs only (a manifest can never create a tab); `badgeField`/`pathField` name TOP-LEVEL `structuredContent` fields (string, or string[] for `pathField`) that the host compiles into the same generic extractors. An invalid composer block is a validation ERROR that sinks the package; a `composer` on a `direct`-renderer surface is a WARNING and is **ignored** — the direct lane's contract has ONE owner, your install-file registration, never the manifest.
|
|
274
274
|
|
|
275
|
-
##
|
|
275
|
+
## Native `direct` surfaces
|
|
276
276
|
|
|
277
277
|
`component.import` resolves through a client-side host registry the package populates from its runtime UI module: declare `app.module`, build `app/host.tsx` with `neuralis-build ui`, and the host imports the module and calls each `install…HostComponents({ registry, port })` export with a registry that accepts only your own package's registrations. Every `component.import` your manifest declares must be registered somewhere under `app/`, or the surface resolves to null and shows the placeholder — no error. A module built before a platform upgrade can be refused (its record names an older host API or no React major): rebuild it with `neuralis-build ui`.
|
|
278
278
|
|
|
@@ -288,7 +288,7 @@ import { SkillLauncher } from '@neuralis/package-system/client';
|
|
|
288
288
|
<SkillLauncher
|
|
289
289
|
skill="manage-things" // the ACTIVATION id (SKILL.md frontmatter `name`)
|
|
290
290
|
packageId="@neuralis/your-package" // the REGISTRY id — manifest `neuralis.id`
|
|
291
|
-
scripts={['scripts/list.
|
|
291
|
+
scripts={['scripts/list.mjs']} // optional: prefill specific bundle scripts
|
|
292
292
|
label="Do this with the agent"
|
|
293
293
|
icon="wand"
|
|
294
294
|
variant="icon" // 'icon' | 'label' | 'row'
|
|
@@ -306,7 +306,7 @@ Four rules, each of which has already cost someone a debugging session:
|
|
|
306
306
|
|
|
307
307
|
## Verify
|
|
308
308
|
|
|
309
|
-
Run `
|
|
309
|
+
Run `node "${SKILL_DIR}/scripts/check-package.mjs" <package-root>` first: the canonical entry root, the renderer profile and the self-containment rule are all checked there, and self-containment can be checked **nowhere else** — the load-time validator receives the manifest object with no package root and no filesystem, and the platform blocks nothing at runtime.
|
|
310
310
|
|
|
311
311
|
Then fetch the runtime snapshot with the `x-project-id` header and confirm it lists **both** surfaces — the widget *and* the dock entry. Open it as the **least-privileged role that should have access**: feature-gated surfaces are hidden, never greyed out, so testing as the owner hides every gate bug. Finally look at the rendered surface itself — an unstyled panel is the signature of a subresource that was never served.
|
|
312
312
|
|
|
@@ -42,7 +42,7 @@ Discovery scans `team/*/` at package root and walks `identity/**` and `files/**`
|
|
|
42
42
|
|
|
43
43
|
That is the whole surface — **unknown keys are dropped silently.** The traps:
|
|
44
44
|
|
|
45
|
-
- **There is no top-level `icon`, `color`, `model`, `allowedTools`, `blockedTools` or `tags`.** Appearance nests under `appearance`; the model pin is `config.settings.defaultModelId`; the permission-profile default is `config.settings.guardProfile` (`safe` | `balanced` | `auto`); the loop cap is `config.settings.maxSteps` — **omit it unless this coworker must be capped BELOW the platform**, because a pin can only ever LOWER the live `maxAgentSteps` ceiling, never raise it, and an unpinned agent runs AT the ceiling (both
|
|
45
|
+
- **There is no top-level `icon`, `color`, `model`, `allowedTools`, `blockedTools` or `tags`.** Appearance nests under `appearance`; the model pin is `config.settings.defaultModelId`; the permission-profile default is `config.settings.guardProfile` (`safe` | `balanced` | `auto`); the loop cap is `config.settings.maxSteps` — **omit it unless this coworker must be capped BELOW the platform**, because a pin can only ever LOWER the live `maxAgentSteps` ceiling, never raise it, and an unpinned agent runs AT the ceiling (both native team members ship unpinned for exactly this reason); tool restriction is `config.tools` (`mode: "all" | "custom"` + `selected[]` + per-tool `overrides`; an `owners` choice is stripped from a template — which package answers a shared tool name is picked per agent, inside its project). A top-level `icon` renders the default avatar and reports **no error at all**; `appearance.icon` takes a platform icon-library name (`surfaces-and-design.md`, the icon rule under "All UI is one array").
|
|
46
46
|
- **`description` is the field the model reads; `descriptionForModel` reaches no prompt.** `description` is copied onto the provisioned agent and rendered in the workspace block's agent list, so every agent in the project sees it — write the *"pick this coworker when…"* sentence there. Fill `descriptionForModel` too (right slot, costs nothing), but never rely on it for coworker selection.
|
|
47
47
|
- **`role` is priority-checked at provisioning, not cosmetic.** A caller may only provision a role equal to or weaker than their own (lower is stronger: owner 1, admin 2, manager 10, member 20, viewer 30; a custom role declares its own number in 1..99, and an unrecognised name resolves to the weakest tier and is always assignable). Name a template role `admin` or `owner` and provisioning **throws** for every ordinary member — the package installs and its coworker cannot be hired.
|
|
48
48
|
- **`permissions` grants nothing.** `permissions.features` is a **visibility** gate — a caller lacking them never sees the member in the team catalogue; `permissions.sources` is display-only. Real scope comes from the caller's own role and features plus the package `uriPolicies`.
|
|
@@ -184,4 +184,4 @@ Installing a template is always **explicit**: it instantiates a mutable entry ow
|
|
|
184
184
|
|
|
185
185
|
---
|
|
186
186
|
|
|
187
|
-
Public reference: [Workflows](https://docs.neuralisapp.com/agent-core/workflows) · [Contributions, including team members](https://docs.neuralisapp.com/package-system/
|
|
187
|
+
Public reference: [Workflows](https://docs.neuralisapp.com/agent-core/workflows) · [Contributions, including team members](https://docs.neuralisapp.com/package-system/team) · [Agent files and delegation](https://docs.neuralisapp.com/agent-core/agents)
|