@agent-native/core 0.4.4 → 0.5.0-dev.b51eaae
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/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +24 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts +6 -3
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +45 -39
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/index.d.ts +1 -1
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +2 -2
- package/dist/a2a/index.js.map +1 -1
- package/dist/a2a/server.d.ts +7 -2
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +54 -14
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/task-store.d.ts +6 -6
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +102 -42
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/a2a/types.d.ts +2 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/action.d.ts +46 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +35 -0
- package/dist/action.js.map +1 -0
- package/dist/adapters/sync/file-sync.js +1 -1
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/production-agent.d.ts +22 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +326 -107
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +43 -0
- package/dist/agent/run-manager.d.ts.map +1 -0
- package/dist/agent/run-manager.js +358 -0
- package/dist/agent/run-manager.js.map +1 -0
- package/dist/agent/run-store.d.ts +26 -0
- package/dist/agent/run-store.d.ts.map +1 -0
- package/dist/agent/run-store.js +145 -0
- package/dist/agent/run-store.js.map +1 -0
- package/dist/agent/thread-data-builder.d.ts +30 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -0
- package/dist/agent/thread-data-builder.js +88 -0
- package/dist/agent/thread-data-builder.js.map +1 -0
- package/dist/agent/types.d.ts +52 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/application-state/emitter.d.ts +3 -2
- package/dist/application-state/emitter.d.ts.map +1 -1
- package/dist/application-state/emitter.js +14 -4
- package/dist/application-state/emitter.js.map +1 -1
- package/dist/application-state/handlers.d.ts.map +1 -1
- package/dist/application-state/handlers.js +13 -16
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/application-state/script-helpers.d.ts +1 -1
- package/dist/application-state/script-helpers.d.ts.map +1 -1
- package/dist/application-state/script-helpers.js +15 -5
- package/dist/application-state/script-helpers.js.map +1 -1
- package/dist/application-state/store.d.ts +4 -3
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +31 -38
- package/dist/application-state/store.js.map +1 -1
- package/dist/chat-threads/emitter.d.ts +9 -0
- package/dist/chat-threads/emitter.d.ts.map +1 -0
- package/dist/chat-threads/emitter.js +14 -0
- package/dist/chat-threads/emitter.js.map +1 -0
- package/dist/chat-threads/store.d.ts +28 -0
- package/dist/chat-threads/store.d.ts.map +1 -0
- package/dist/chat-threads/store.js +124 -0
- package/dist/chat-threads/store.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +4 -18
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +42 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup-agents.d.ts +11 -0
- package/dist/cli/setup-agents.d.ts.map +1 -0
- package/dist/cli/setup-agents.js +123 -0
- package/dist/cli/setup-agents.js.map +1 -0
- package/dist/client/AgentPanel.d.ts +14 -3
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +492 -21
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +27 -3
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +639 -57
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ClientOnly.d.ts +14 -0
- package/dist/client/ClientOnly.d.ts.map +1 -0
- package/dist/client/ClientOnly.js +17 -0
- package/dist/client/ClientOnly.js.map +1 -0
- package/dist/client/CommandMenu.d.ts +71 -0
- package/dist/client/CommandMenu.d.ts.map +1 -0
- package/dist/client/CommandMenu.js +257 -0
- package/dist/client/CommandMenu.js.map +1 -0
- package/dist/client/DefaultSpinner.d.ts +7 -0
- package/dist/client/DefaultSpinner.d.ts.map +1 -0
- package/dist/client/DefaultSpinner.js +28 -0
- package/dist/client/DefaultSpinner.js.map +1 -0
- package/dist/client/MultiTabAssistantChat.d.ts +34 -2
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +346 -67
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/PoweredByBadge.d.ts.map +1 -1
- package/dist/client/PoweredByBadge.js +2 -1
- package/dist/client/PoweredByBadge.js.map +1 -1
- package/dist/client/active-run-state.d.ts +10 -0
- package/dist/client/active-run-state.d.ts.map +1 -0
- package/dist/client/active-run-state.js +32 -0
- package/dist/client/active-run-state.js.map +1 -0
- package/dist/client/agent-chat-adapter.d.ts +2 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +83 -129
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts +2 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +2 -2
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/analytics.d.ts +16 -0
- package/dist/client/analytics.d.ts.map +1 -0
- package/dist/client/analytics.js +17 -0
- package/dist/client/analytics.js.map +1 -0
- package/dist/client/components/ApiKeySettings.d.ts +2 -2
- package/dist/client/components/ApiKeySettings.js +4 -4
- package/dist/client/components/ApiKeySettings.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts +13 -0
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -0
- package/dist/client/components/CodeRequiredDialog.js +160 -0
- package/dist/client/components/CodeRequiredDialog.js.map +1 -0
- package/dist/client/composer/MentionPopover.d.ts +26 -0
- package/dist/client/composer/MentionPopover.d.ts.map +1 -0
- package/dist/client/composer/MentionPopover.js +130 -0
- package/dist/client/composer/MentionPopover.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +19 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -0
- package/dist/client/composer/TiptapComposer.js +415 -0
- package/dist/client/composer/TiptapComposer.js.map +1 -0
- package/dist/client/composer/extensions/FileReference.d.ts +3 -0
- package/dist/client/composer/extensions/FileReference.d.ts.map +1 -0
- package/dist/client/composer/extensions/FileReference.js +36 -0
- package/dist/client/composer/extensions/FileReference.js.map +1 -0
- package/dist/client/composer/extensions/MentionReference.d.ts +3 -0
- package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -0
- package/dist/client/composer/extensions/MentionReference.js +63 -0
- package/dist/client/composer/extensions/MentionReference.js.map +1 -0
- package/dist/client/composer/extensions/SkillReference.d.ts +3 -0
- package/dist/client/composer/extensions/SkillReference.d.ts.map +1 -0
- package/dist/client/composer/extensions/SkillReference.js +40 -0
- package/dist/client/composer/extensions/SkillReference.js.map +1 -0
- package/dist/client/composer/index.d.ts +8 -0
- package/dist/client/composer/index.d.ts.map +1 -0
- package/dist/client/composer/index.js +7 -0
- package/dist/client/composer/index.js.map +1 -0
- package/dist/client/composer/types.d.ts +32 -0
- package/dist/client/composer/types.d.ts.map +1 -0
- package/dist/client/composer/types.js +2 -0
- package/dist/client/composer/types.js.map +1 -0
- package/dist/client/composer/use-file-search.d.ts +6 -0
- package/dist/client/composer/use-file-search.d.ts.map +1 -0
- package/dist/client/composer/use-file-search.js +40 -0
- package/dist/client/composer/use-file-search.js.map +1 -0
- package/dist/client/composer/use-mention-search.d.ts +6 -0
- package/dist/client/composer/use-mention-search.d.ts.map +1 -0
- package/dist/client/composer/use-mention-search.js +39 -0
- package/dist/client/composer/use-mention-search.js.map +1 -0
- package/dist/client/composer/use-skills.d.ts +7 -0
- package/dist/client/composer/use-skills.d.ts.map +1 -0
- package/dist/client/composer/use-skills.js +38 -0
- package/dist/client/composer/use-skills.js.map +1 -0
- package/dist/client/index.d.ts +13 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +12 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/ResourceEditor.d.ts +7 -0
- package/dist/client/resources/ResourceEditor.d.ts.map +1 -0
- package/dist/client/resources/ResourceEditor.js +851 -0
- package/dist/client/resources/ResourceEditor.js.map +1 -0
- package/dist/client/resources/ResourceTree.d.ts +13 -0
- package/dist/client/resources/ResourceTree.d.ts.map +1 -0
- package/dist/client/resources/ResourceTree.js +122 -0
- package/dist/client/resources/ResourceTree.js.map +1 -0
- package/dist/client/resources/ResourcesPanel.d.ts +2 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -0
- package/dist/client/resources/ResourcesPanel.js +341 -0
- package/dist/client/resources/ResourcesPanel.js.map +1 -0
- package/dist/client/resources/index.d.ts +5 -0
- package/dist/client/resources/index.d.ts.map +1 -0
- package/dist/client/resources/index.js +5 -0
- package/dist/client/resources/index.js.map +1 -0
- package/dist/client/resources/use-resources.d.ts +45 -0
- package/dist/client/resources/use-resources.d.ts.map +1 -0
- package/dist/client/resources/use-resources.js +102 -0
- package/dist/client/resources/use-resources.js.map +1 -0
- package/dist/client/sse-event-processor.d.ts +50 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -0
- package/dist/client/sse-event-processor.js +267 -0
- package/dist/client/sse-event-processor.js.map +1 -0
- package/dist/client/terminal/AgentTerminal.d.ts +1 -1
- package/dist/client/terminal/AgentTerminal.d.ts.map +1 -1
- package/dist/client/terminal/AgentTerminal.js +12 -7
- package/dist/client/terminal/AgentTerminal.js.map +1 -1
- package/dist/client/use-agent-chat.d.ts +1 -1
- package/dist/client/use-agent-chat.d.ts.map +1 -1
- package/dist/client/use-agent-chat.js +3 -3
- package/dist/client/use-agent-chat.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +36 -0
- package/dist/client/use-chat-threads.d.ts.map +1 -0
- package/dist/client/use-chat-threads.js +175 -0
- package/dist/client/use-chat-threads.js.map +1 -0
- package/dist/client/use-db-sync.d.ts +35 -0
- package/dist/client/use-db-sync.d.ts.map +1 -0
- package/dist/client/use-db-sync.js +74 -0
- package/dist/client/use-db-sync.js.map +1 -0
- package/dist/client/use-dev-mode.d.ts +11 -0
- package/dist/client/use-dev-mode.d.ts.map +1 -0
- package/dist/client/use-dev-mode.js +71 -0
- package/dist/client/use-dev-mode.js.map +1 -0
- package/dist/client/use-file-sync-status.d.ts +1 -1
- package/dist/client/use-file-sync-status.js +3 -3
- package/dist/client/use-file-sync-status.js.map +1 -1
- package/dist/client/use-send-to-agent-chat.d.ts +17 -0
- package/dist/client/use-send-to-agent-chat.d.ts.map +1 -0
- package/dist/client/use-send-to-agent-chat.js +40 -0
- package/dist/client/use-send-to-agent-chat.js.map +1 -0
- package/dist/client/use-session.d.ts +1 -1
- package/dist/client/use-session.d.ts.map +1 -1
- package/dist/client/use-session.js +13 -4
- package/dist/client/use-session.js.map +1 -1
- package/dist/client/useProductionAgent.d.ts +1 -1
- package/dist/client/useProductionAgent.d.ts.map +1 -1
- package/dist/client/useProductionAgent.js +38 -3
- package/dist/client/useProductionAgent.js.map +1 -1
- package/dist/credentials/index.d.ts +18 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +32 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/db/client.d.ts +35 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +248 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +108 -12
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/db/index.d.ts +10 -6
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +10 -4
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +52 -21
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/schema.d.ts +45 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +61 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/deploy/build.d.ts +16 -0
- package/dist/deploy/build.d.ts.map +1 -0
- package/dist/deploy/build.js +453 -0
- package/dist/deploy/build.js.map +1 -0
- package/dist/deploy/route-discovery.d.ts +43 -0
- package/dist/deploy/route-discovery.d.ts.map +1 -0
- package/dist/deploy/route-discovery.js +115 -0
- package/dist/deploy/route-discovery.js.map +1 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/oauth-tokens/index.d.ts +1 -1
- package/dist/oauth-tokens/index.d.ts.map +1 -1
- package/dist/oauth-tokens/index.js +1 -1
- package/dist/oauth-tokens/index.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts +18 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +81 -40
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/resources/emitter.d.ts +13 -0
- package/dist/resources/emitter.d.ts.map +1 -0
- package/dist/resources/emitter.js +32 -0
- package/dist/resources/emitter.js.map +1 -0
- package/dist/resources/handlers.d.ts +45 -0
- package/dist/resources/handlers.d.ts.map +1 -0
- package/dist/resources/handlers.js +219 -0
- package/dist/resources/handlers.js.map +1 -0
- package/dist/resources/index.d.ts +5 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +5 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/script-helpers.d.ts +24 -0
- package/dist/resources/script-helpers.d.ts.map +1 -0
- package/dist/resources/script-helpers.js +36 -0
- package/dist/resources/script-helpers.js.map +1 -0
- package/dist/resources/store.d.ts +35 -0
- package/dist/resources/store.d.ts.map +1 -0
- package/dist/resources/store.js +453 -0
- package/dist/resources/store.js.map +1 -0
- package/dist/scripts/call-agent.d.ts +5 -0
- package/dist/scripts/call-agent.d.ts.map +1 -0
- package/dist/scripts/call-agent.js +107 -0
- package/dist/scripts/call-agent.js.map +1 -0
- package/dist/scripts/core-scripts.d.ts.map +1 -1
- package/dist/scripts/core-scripts.js +2 -0
- package/dist/scripts/core-scripts.js.map +1 -1
- package/dist/scripts/db/exec.d.ts +6 -2
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +127 -36
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/query.d.ts +7 -2
- package/dist/scripts/db/query.d.ts.map +1 -1
- package/dist/scripts/db/query.js +89 -45
- package/dist/scripts/db/query.js.map +1 -1
- package/dist/scripts/db/schema.d.ts +2 -2
- package/dist/scripts/db/schema.d.ts.map +1 -1
- package/dist/scripts/db/schema.js +145 -6
- package/dist/scripts/db/schema.js.map +1 -1
- package/dist/scripts/db/scoping.d.ts +36 -0
- package/dist/scripts/db/scoping.d.ts.map +1 -0
- package/dist/scripts/db/scoping.js +198 -0
- package/dist/scripts/db/scoping.js.map +1 -0
- package/dist/scripts/dev/index.d.ts +2 -2
- package/dist/scripts/dev/index.js +1 -1
- package/dist/scripts/dev/list-files.d.ts +2 -2
- package/dist/scripts/dev/read-file.d.ts +2 -2
- package/dist/scripts/dev/read-file.js +1 -1
- package/dist/scripts/dev/read-file.js.map +1 -1
- package/dist/scripts/dev/search-files.d.ts +2 -2
- package/dist/scripts/dev/search-files.js +1 -1
- package/dist/scripts/dev/search-files.js.map +1 -1
- package/dist/scripts/dev/shell.d.ts +2 -2
- package/dist/scripts/dev/shell.js +1 -1
- package/dist/scripts/dev/shell.js.map +1 -1
- package/dist/scripts/dev/write-file.d.ts +2 -2
- package/dist/scripts/dev/write-file.js +1 -1
- package/dist/scripts/dev/write-file.js.map +1 -1
- package/dist/scripts/resources/delete.d.ts +10 -0
- package/dist/scripts/resources/delete.d.ts.map +1 -0
- package/dist/scripts/resources/delete.js +38 -0
- package/dist/scripts/resources/delete.js.map +1 -0
- package/dist/scripts/resources/index.d.ts +2 -0
- package/dist/scripts/resources/index.d.ts.map +1 -0
- package/dist/scripts/resources/index.js +8 -0
- package/dist/scripts/resources/index.js.map +1 -0
- package/dist/scripts/resources/list.d.ts +10 -0
- package/dist/scripts/resources/list.d.ts.map +1 -0
- package/dist/scripts/resources/list.js +57 -0
- package/dist/scripts/resources/list.js.map +1 -0
- package/dist/scripts/resources/migrate-learnings.d.ts +10 -0
- package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -0
- package/dist/scripts/resources/migrate-learnings.js +23 -0
- package/dist/scripts/resources/migrate-learnings.js.map +1 -0
- package/dist/scripts/resources/read.d.ts +10 -0
- package/dist/scripts/resources/read.d.ts.map +1 -0
- package/dist/scripts/resources/read.js +59 -0
- package/dist/scripts/resources/read.js.map +1 -0
- package/dist/scripts/resources/write.d.ts +10 -0
- package/dist/scripts/resources/write.d.ts.map +1 -0
- package/dist/scripts/resources/write.js +67 -0
- package/dist/scripts/resources/write.js.map +1 -0
- package/dist/scripts/runner.d.ts +7 -7
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +68 -27
- package/dist/scripts/runner.js.map +1 -1
- package/dist/scripts/utils.d.ts +4 -1
- package/dist/scripts/utils.d.ts.map +1 -1
- package/dist/scripts/utils.js +5 -3
- package/dist/scripts/utils.js.map +1 -1
- package/dist/server/action-discovery.d.ts +40 -0
- package/dist/server/action-discovery.d.ts.map +1 -0
- package/dist/server/action-discovery.js +189 -0
- package/dist/server/action-discovery.js.map +1 -0
- package/dist/server/agent-chat-plugin.d.ts +12 -23
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +1102 -39
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts +16 -0
- package/dist/server/agent-discovery.d.ts.map +1 -0
- package/dist/server/agent-discovery.js +136 -0
- package/dist/server/agent-discovery.js.map +1 -0
- package/dist/server/analytics.d.ts +19 -0
- package/dist/server/analytics.d.ts.map +1 -0
- package/dist/server/analytics.js +61 -0
- package/dist/server/analytics.js.map +1 -0
- package/dist/server/auth-plugin.d.ts +5 -0
- package/dist/server/auth-plugin.d.ts.map +1 -1
- package/dist/server/auth-plugin.js +12 -1
- package/dist/server/auth-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +3 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +598 -104
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +57 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -0
- package/dist/server/core-routes-plugin.js +125 -0
- package/dist/server/core-routes-plugin.js.map +1 -0
- package/dist/server/create-server.d.ts +4 -4
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +5 -5
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/default-watcher.d.ts +9 -3
- package/dist/server/default-watcher.d.ts.map +1 -1
- package/dist/server/default-watcher.js +26 -6
- package/dist/server/default-watcher.js.map +1 -1
- package/dist/server/google-auth-plugin.d.ts.map +1 -1
- package/dist/server/google-auth-plugin.js +4 -3
- package/dist/server/google-auth-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts +72 -0
- package/dist/server/google-oauth.d.ts.map +1 -0
- package/dist/server/google-oauth.js +187 -0
- package/dist/server/google-oauth.js.map +1 -0
- package/dist/server/index.d.ts +10 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/oauth-helpers.d.ts +16 -0
- package/dist/server/oauth-helpers.d.ts.map +1 -0
- package/dist/server/oauth-helpers.js +25 -0
- package/dist/server/oauth-helpers.js.map +1 -0
- package/dist/server/poll.d.ts +40 -0
- package/dist/server/poll.d.ts.map +1 -0
- package/dist/server/poll.js +49 -0
- package/dist/server/poll.js.map +1 -0
- package/dist/server/resources-plugin.d.ts +27 -0
- package/dist/server/resources-plugin.d.ts.map +1 -0
- package/dist/server/resources-plugin.js +74 -0
- package/dist/server/resources-plugin.js.map +1 -0
- package/dist/server/script-discovery.d.ts +6 -0
- package/dist/server/script-discovery.d.ts.map +1 -0
- package/dist/server/script-discovery.js +6 -0
- package/dist/server/script-discovery.js.map +1 -0
- package/dist/server/sse.d.ts +1 -1
- package/dist/server/sse.js +1 -1
- package/dist/settings/handlers.d.ts +3 -3
- package/dist/settings/handlers.d.ts.map +1 -1
- package/dist/settings/handlers.js +8 -6
- package/dist/settings/handlers.js.map +1 -1
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/store.d.ts +6 -2
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +26 -36
- package/dist/settings/store.js.map +1 -1
- package/dist/settings/user-settings.d.ts +6 -10
- package/dist/settings/user-settings.d.ts.map +1 -1
- package/dist/settings/user-settings.js +9 -18
- package/dist/settings/user-settings.js.map +1 -1
- package/dist/tailwind.preset.d.ts +7 -6
- package/dist/tailwind.preset.d.ts.map +1 -1
- package/dist/tailwind.preset.js +18 -1
- package/dist/tailwind.preset.js.map +1 -1
- package/dist/terminal/cli-registry.d.ts +3 -1
- package/dist/terminal/cli-registry.d.ts.map +1 -1
- package/dist/terminal/cli-registry.js +10 -5
- package/dist/terminal/cli-registry.js.map +1 -1
- package/dist/terminal/pty-server.d.ts.map +1 -1
- package/dist/terminal/pty-server.js +65 -11
- package/dist/terminal/pty-server.js.map +1 -1
- package/dist/terminal/terminal-plugin.d.ts +2 -2
- package/dist/terminal/terminal-plugin.d.ts.map +1 -1
- package/dist/terminal/terminal-plugin.js +27 -8
- package/dist/terminal/terminal-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +114 -8
- package/dist/vite/client.js.map +1 -1
- package/dist/vite/dev-api-server.d.ts +1 -1
- package/dist/vite/dev-api-server.d.ts.map +1 -1
- package/dist/vite/dev-api-server.js +105 -22
- package/dist/vite/dev-api-server.js.map +1 -1
- package/package.json +23 -7
- package/src/templates/default/.agents/skills/actions/SKILL.md +136 -0
- package/src/templates/default/.agents/skills/create-skill/SKILL.md +1 -1
- package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +1 -1
- package/src/templates/default/.agents/skills/files-as-database/SKILL.md +2 -2
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +112 -0
- package/src/templates/default/AGENTS.md +56 -164
- package/src/templates/default/DEVELOPING.md +117 -0
- package/src/templates/default/{scripts → actions}/hello.ts +1 -1
- package/src/templates/default/actions/navigate.ts +53 -0
- package/src/templates/default/actions/view-screen.ts +39 -0
- package/src/templates/default/app/entry.server.tsx +2 -1
- package/src/templates/default/app/global.css +2 -2
- package/src/templates/default/app/root.tsx +27 -15
- package/src/templates/default/app/routes/_index.tsx +1 -1
- package/src/templates/default/package.json +4 -0
- package/src/templates/default/public/icon-180.svg +4 -0
- package/src/templates/default/public/icon-192.svg +4 -0
- package/src/templates/default/public/icon-512.svg +4 -0
- package/src/templates/default/public/manifest.json +13 -0
- package/src/templates/default/server/plugins/.gitkeep +0 -0
- package/dist/a2a/middleware.d.ts +0 -3
- package/dist/a2a/middleware.d.ts.map +0 -1
- package/dist/a2a/middleware.js +0 -36
- package/dist/a2a/middleware.js.map +0 -1
- package/dist/client/use-file-watcher.d.ts +0 -23
- package/dist/client/use-file-watcher.d.ts.map +0 -1
- package/dist/client/use-file-watcher.js +0 -50
- package/dist/client/use-file-watcher.js.map +0 -1
- package/src/templates/default/.agents/skills/scripts/SKILL.md +0 -121
- package/src/templates/default/.agents/skills/sse-file-watcher/SKILL.md +0 -80
- package/src/templates/default/server/plugins/agent-chat.ts +0 -1
- package/src/templates/default/server/plugins/auth.ts +0 -1
- package/src/templates/default/server/plugins/file-sync.ts +0 -1
- package/src/templates/default/server/plugins/terminal.ts +0 -1
- package/src/templates/default/server/routes/api/events.get.ts +0 -3
- package/src/templates/default/server/routes/api/file-sync/status.get.ts +0 -4
- /package/src/templates/default/{scripts → actions}/run.ts +0 -0
|
@@ -1,28 +1,293 @@
|
|
|
1
|
-
import { createProductionAgentHandler, } from "../agent/production-agent.js";
|
|
2
|
-
import {
|
|
3
|
-
import { defineEventHandler, readBody, setResponseStatus, getMethod } from "h3";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
|
|
1
|
+
import { createProductionAgentHandler, getActiveRunForThreadAsync, abortRun, subscribeToRun, } from "../agent/production-agent.js";
|
|
2
|
+
import { buildAssistantMessage, extractThreadMeta, } from "../agent/thread-data-builder.js";
|
|
3
|
+
import { defineEventHandler, readBody, setResponseStatus, setResponseHeader, getMethod, getQuery, } from "h3";
|
|
4
|
+
import { getSession } from "./auth.js";
|
|
5
|
+
import { createThread, getThread, listThreads, searchThreads, updateThreadData, deleteThread, } from "../chat-threads/store.js";
|
|
6
|
+
import { resourceListAccessible, resourceList, resourceGet, resourceGetByPath, ensurePersonalDefaults, SHARED_OWNER, } from "../resources/store.js";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import nodePath from "node:path";
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a core CLI script (that writes to console.log) as a ActionEntry
|
|
11
|
+
* by capturing stdout.
|
|
12
|
+
*/
|
|
13
|
+
function wrapCliScript(tool, cliDefault) {
|
|
14
|
+
return {
|
|
15
|
+
tool,
|
|
16
|
+
run: async (args) => {
|
|
17
|
+
const cliArgs = [];
|
|
18
|
+
for (const [k, v] of Object.entries(args)) {
|
|
19
|
+
cliArgs.push(`--${k}`, v);
|
|
20
|
+
}
|
|
21
|
+
const logs = [];
|
|
22
|
+
const origLog = console.log;
|
|
23
|
+
const origError = console.error;
|
|
24
|
+
const origStdoutWrite = process.stdout.write;
|
|
25
|
+
console.log = (...a) => {
|
|
26
|
+
logs.push(a.map(String).join(" "));
|
|
27
|
+
};
|
|
28
|
+
console.error = (...a) => {
|
|
29
|
+
logs.push(a.map(String).join(" "));
|
|
30
|
+
};
|
|
31
|
+
// Intercept process.stdout.write so scripts that write directly
|
|
32
|
+
// (e.g. resource-read) have their output captured
|
|
33
|
+
process.stdout.write = ((chunk, ...rest) => {
|
|
34
|
+
if (typeof chunk === "string") {
|
|
35
|
+
logs.push(chunk);
|
|
36
|
+
}
|
|
37
|
+
else if (Buffer.isBuffer(chunk)) {
|
|
38
|
+
logs.push(chunk.toString());
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
await cliDefault(cliArgs);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
logs.push(`Error: ${err?.message ?? String(err)}`);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
console.log = origLog;
|
|
50
|
+
console.error = origError;
|
|
51
|
+
process.stdout.write = origStdoutWrite;
|
|
52
|
+
}
|
|
53
|
+
return logs.join("\n") || "(no output)";
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates resource ScriptEntries available in both prod and dev modes.
|
|
59
|
+
*/
|
|
60
|
+
async function createResourceScriptEntries() {
|
|
61
|
+
try {
|
|
62
|
+
const [list, read, write, del] = await Promise.all([
|
|
63
|
+
import("../scripts/resources/list.js"),
|
|
64
|
+
import("../scripts/resources/read.js"),
|
|
65
|
+
import("../scripts/resources/write.js"),
|
|
66
|
+
import("../scripts/resources/delete.js"),
|
|
67
|
+
]);
|
|
68
|
+
return {
|
|
69
|
+
"resource-list": wrapCliScript({
|
|
70
|
+
description: "List resources (persistent files/notes). Returns file paths, sizes, and metadata.",
|
|
71
|
+
parameters: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
prefix: {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "Filter by path prefix (e.g. 'notes/')",
|
|
77
|
+
},
|
|
78
|
+
scope: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "Which resources to list: personal, shared, or all (default: all)",
|
|
81
|
+
enum: ["personal", "shared", "all"],
|
|
82
|
+
},
|
|
83
|
+
format: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: 'Output format: "json" or "text" (default: text)',
|
|
86
|
+
enum: ["json", "text"],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}, list.default),
|
|
91
|
+
"resource-read": wrapCliScript({
|
|
92
|
+
description: "Read a resource by path. Returns the file contents.",
|
|
93
|
+
parameters: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
path: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Resource path (e.g. 'LEARNINGS.md', 'notes/ideas.md')",
|
|
99
|
+
},
|
|
100
|
+
scope: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "personal or shared (default: personal, falls back to shared)",
|
|
103
|
+
enum: ["personal", "shared"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ["path"],
|
|
107
|
+
},
|
|
108
|
+
}, read.default),
|
|
109
|
+
"resource-write": wrapCliScript({
|
|
110
|
+
description: "Write or update a resource. Creates the resource if it doesn't exist.",
|
|
111
|
+
parameters: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
path: {
|
|
115
|
+
type: "string",
|
|
116
|
+
description: "Resource path (e.g. 'LEARNINGS.md', 'notes/ideas.md')",
|
|
117
|
+
},
|
|
118
|
+
content: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "The content to write",
|
|
121
|
+
},
|
|
122
|
+
scope: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "personal or shared (default: personal)",
|
|
125
|
+
enum: ["personal", "shared"],
|
|
126
|
+
},
|
|
127
|
+
mime: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "MIME type (default: inferred from extension)",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
required: ["path", "content"],
|
|
133
|
+
},
|
|
134
|
+
}, write.default),
|
|
135
|
+
"resource-delete": wrapCliScript({
|
|
136
|
+
description: "Delete a resource by path.",
|
|
137
|
+
parameters: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
path: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Resource path to delete",
|
|
143
|
+
},
|
|
144
|
+
scope: {
|
|
145
|
+
type: "string",
|
|
146
|
+
description: "personal or shared (default: personal)",
|
|
147
|
+
enum: ["personal", "shared"],
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
required: ["path"],
|
|
151
|
+
},
|
|
152
|
+
}, del.default),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Resources not available — skip silently
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Creates the call-agent ActionEntry for cross-agent A2A communication.
|
|
162
|
+
*/
|
|
163
|
+
async function createCallAgentScriptEntry() {
|
|
164
|
+
try {
|
|
165
|
+
const mod = await import("../scripts/call-agent.js");
|
|
166
|
+
return {
|
|
167
|
+
"call-agent": {
|
|
168
|
+
tool: mod.tool,
|
|
169
|
+
run: mod.run,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Framework-level instructions injected into every agent's system prompt.
|
|
179
|
+
* This is the single source of truth for the core philosophy, rules, and patterns.
|
|
180
|
+
* Template AGENTS.md resources only need template-specific content.
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* Framework instructions shared across both modes. The mode-specific
|
|
184
|
+
* preamble is prepended by the prompt composition below.
|
|
185
|
+
*/
|
|
186
|
+
const FRAMEWORK_CORE = `
|
|
187
|
+
### Core Rules
|
|
7
188
|
|
|
8
|
-
|
|
9
|
-
|
|
189
|
+
1. **Data lives in SQL** — All app state is in a SQL database (could be SQLite, Postgres, Turso, or Cloudflare D1 — never assume which). Use the available database tools.
|
|
190
|
+
2. **Context awareness** — Before taking action, understand what the user is looking at. Use the \`view-screen\` tool if available — it returns the current UI state (which page, which item is focused, what's selected).
|
|
191
|
+
3. **Navigate the UI** — Use the \`navigate\` tool to switch views, open items, or focus elements for the user.
|
|
192
|
+
4. **Application state** — Ephemeral UI state (drafts, selections, navigation) lives in \`application_state\`. Use \`readAppState\`/\`writeAppState\` to read and write it. When you write state, the UI updates automatically.
|
|
193
|
+
5. **Resources for memory** — Use the Resources system for persistent notes and context. Update LEARNINGS.md when you learn user preferences or corrections. Update the shared AGENTS.md for instructions that should apply to all users.
|
|
10
194
|
|
|
11
|
-
|
|
12
|
-
- Read, write, and edit any file in the project (read-file, write-file)
|
|
13
|
-
- List and search files (list-files, search-files)
|
|
14
|
-
- Run shell commands (shell) — use for git, build, install, etc.
|
|
15
|
-
- Query and modify the database (db-schema, db-query, db-exec)
|
|
16
|
-
- Plus all application-specific tools
|
|
195
|
+
### Resources
|
|
17
196
|
|
|
18
|
-
|
|
197
|
+
You have access to a Resources system for persistent notes, learnings, and context files.
|
|
198
|
+
Use resource-list, resource-read, resource-write, and resource-delete to manage resources.
|
|
199
|
+
Resources can be personal (per-user) or shared (team-wide). By default, resources are personal.
|
|
19
200
|
|
|
201
|
+
When you learn something important (user corrections, preferences, patterns), update the "LEARNINGS.md" resource. Keep it tidy — revise, consolidate, and remove outdated entries rather than only appending.
|
|
202
|
+
When the user gives instructions that should apply to all users/sessions, update the shared "AGENTS.md" resource instead.
|
|
203
|
+
|
|
204
|
+
### Navigation Rule
|
|
205
|
+
|
|
206
|
+
When the user says "show me", "go to", "open", "switch to", or similar navigation language, ALWAYS use the \`navigate\` action to update the UI. The user expects to SEE the result in the main app, not just read it in chat. Navigate first, then fetch/display data.
|
|
20
207
|
`;
|
|
208
|
+
const PROD_FRAMEWORK_PROMPT = `## Agent-Native Framework — Production Mode
|
|
209
|
+
|
|
210
|
+
You are an AI agent in an agent-native application, running in **production mode**.
|
|
211
|
+
|
|
212
|
+
The agent and the UI are equal partners — everything the UI can do, you can do via your tools, and vice versa. They share the same SQL database and stay in sync automatically.
|
|
213
|
+
|
|
214
|
+
**In production mode, you operate through registered actions exposed as tools.** These are your capabilities — use them to read data, take actions, and help the user. You cannot edit source code or access the filesystem directly. Your tools are the app's API.
|
|
215
|
+
${FRAMEWORK_CORE}`;
|
|
216
|
+
const DEV_FRAMEWORK_PROMPT = `## Agent-Native Framework — Development Mode
|
|
217
|
+
|
|
218
|
+
You are an AI agent in an agent-native application, running in **development mode**.
|
|
219
|
+
|
|
220
|
+
The agent and the UI are equal partners — everything the UI can do, you can do via tools/scripts, and vice versa. They share the same SQL database and stay in sync automatically.
|
|
221
|
+
|
|
222
|
+
**In development mode, you have full access to the project filesystem, shell, and database** — in addition to all the app's production tools. You can edit source code, run commands, install packages, and modify the app directly.
|
|
223
|
+
|
|
224
|
+
When editing code, follow the agent-native architecture:
|
|
225
|
+
- Every feature needs all four areas: UI + scripts + skills/instructions + application-state sync
|
|
226
|
+
- All SQL must be dialect-agnostic (works on SQLite and Postgres)
|
|
227
|
+
- No Node.js-specific APIs in server routes (must work on Cloudflare Workers, etc.)
|
|
228
|
+
- Use shadcn/ui components and Tabler Icons for all UI work
|
|
229
|
+
${FRAMEWORK_CORE}`;
|
|
230
|
+
const DEFAULT_SYSTEM_PROMPT = PROD_FRAMEWORK_PROMPT;
|
|
231
|
+
/**
|
|
232
|
+
* Pre-load AGENTS.md and LEARNINGS.md (personal + shared) and append to the system prompt.
|
|
233
|
+
* This ensures the agent always has the user's context without needing to call tools first.
|
|
234
|
+
*/
|
|
235
|
+
async function loadResourcesForPrompt(owner) {
|
|
236
|
+
await ensurePersonalDefaults(owner);
|
|
237
|
+
const resourceNames = ["AGENTS.md", "LEARNINGS.md"];
|
|
238
|
+
const sections = [];
|
|
239
|
+
for (const name of resourceNames) {
|
|
240
|
+
// Read shared
|
|
241
|
+
try {
|
|
242
|
+
const shared = await resourceGetByPath(SHARED_OWNER, name);
|
|
243
|
+
if (shared?.content?.trim()) {
|
|
244
|
+
sections.push(`<resource name="${name}" scope="shared">\n${shared.content.trim()}\n</resource>`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch { }
|
|
248
|
+
// Read personal (skip if owner is the shared sentinel)
|
|
249
|
+
if (owner !== SHARED_OWNER) {
|
|
250
|
+
try {
|
|
251
|
+
const personal = await resourceGetByPath(owner, name);
|
|
252
|
+
if (personal?.content?.trim()) {
|
|
253
|
+
sections.push(`<resource name="${name}" scope="personal">\n${personal.content.trim()}\n</resource>`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch { }
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (sections.length === 0)
|
|
260
|
+
return "";
|
|
261
|
+
return ("\n\nThe following resources contain template-specific instructions and user context. Use the information in them to help the user.\n\n" +
|
|
262
|
+
sections.join("\n\n"));
|
|
263
|
+
}
|
|
264
|
+
/** @deprecated Kept for backward compat — dev prompt is now part of DEV_FRAMEWORK_PROMPT */
|
|
265
|
+
const DEFAULT_DEV_PROMPT = "";
|
|
266
|
+
/**
|
|
267
|
+
* Generates a system prompt section describing registered template actions.
|
|
268
|
+
* This helps the agent prefer template-specific actions over raw db-query/db-exec.
|
|
269
|
+
*/
|
|
270
|
+
function generateActionsPrompt(registry) {
|
|
271
|
+
if (!registry || Object.keys(registry).length === 0)
|
|
272
|
+
return "";
|
|
273
|
+
const lines = Object.entries(registry).map(([name, entry]) => {
|
|
274
|
+
const desc = entry.tool.description;
|
|
275
|
+
const params = entry.tool.parameters?.properties;
|
|
276
|
+
if (params) {
|
|
277
|
+
const paramList = Object.entries(params)
|
|
278
|
+
.map(([k, v]) => `--${k}${v.description ? ` (${v.description})` : ""}`)
|
|
279
|
+
.join(", ");
|
|
280
|
+
return `- \`${name}\` — ${desc} Args: ${paramList}`;
|
|
281
|
+
}
|
|
282
|
+
return `- \`${name}\` — ${desc}`;
|
|
283
|
+
});
|
|
284
|
+
return `\n\n## Available Actions\n\nThese are your registered template actions. ALWAYS prefer these over raw db-query/db-exec when a matching action exists:\n\n${lines.join("\n")}`;
|
|
285
|
+
}
|
|
21
286
|
/**
|
|
22
287
|
* Creates a Nitro plugin that mounts the agent chat endpoint.
|
|
23
288
|
*
|
|
24
289
|
* In dev mode (NODE_ENV !== "production"), automatically includes
|
|
25
|
-
* file system, shell, and database tools alongside any template-specific
|
|
290
|
+
* file system, shell, and database tools alongside any template-specific actions.
|
|
26
291
|
*
|
|
27
292
|
* Usage in templates:
|
|
28
293
|
* ```ts
|
|
@@ -36,29 +301,371 @@ When editing code, maintain existing patterns and conventions. After writing fil
|
|
|
36
301
|
* });
|
|
37
302
|
* ```
|
|
38
303
|
*/
|
|
304
|
+
function collectFiles(dir, prefix, depth, results) {
|
|
305
|
+
if (depth > 4 || results.length >= 500)
|
|
306
|
+
return;
|
|
307
|
+
const skip = new Set([
|
|
308
|
+
"node_modules",
|
|
309
|
+
".git",
|
|
310
|
+
".next",
|
|
311
|
+
".output",
|
|
312
|
+
"dist",
|
|
313
|
+
".cache",
|
|
314
|
+
".turbo",
|
|
315
|
+
"data",
|
|
316
|
+
]);
|
|
317
|
+
let entries;
|
|
318
|
+
try {
|
|
319
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
for (const entry of entries) {
|
|
325
|
+
if (results.length >= 500)
|
|
326
|
+
return;
|
|
327
|
+
if (skip.has(entry.name) || entry.name.startsWith("."))
|
|
328
|
+
continue;
|
|
329
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
330
|
+
const isDir = entry.isDirectory();
|
|
331
|
+
results.push({
|
|
332
|
+
path: relPath,
|
|
333
|
+
name: entry.name,
|
|
334
|
+
type: isDir ? "folder" : "file",
|
|
335
|
+
});
|
|
336
|
+
if (isDir)
|
|
337
|
+
collectFiles(nodePath.join(dir, entry.name), relPath, depth + 1, results);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function parseSkillFrontmatter(content) {
|
|
341
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
342
|
+
if (!match)
|
|
343
|
+
return {};
|
|
344
|
+
const fm = match[1];
|
|
345
|
+
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim();
|
|
346
|
+
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim();
|
|
347
|
+
return { name, description };
|
|
348
|
+
}
|
|
349
|
+
function isLocalhost(event) {
|
|
350
|
+
try {
|
|
351
|
+
const host = event.node?.req?.headers?.host || event.headers?.get?.("host") || "";
|
|
352
|
+
const hostname = host.split(":")[0];
|
|
353
|
+
return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
39
359
|
export function createAgentChatPlugin(options) {
|
|
40
360
|
return async (nitroApp) => {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
?
|
|
50
|
-
:
|
|
51
|
-
//
|
|
52
|
-
const
|
|
361
|
+
const env = process.env.NODE_ENV;
|
|
362
|
+
// AGENT_MODE=production forces production agent constraints even in dev
|
|
363
|
+
const canToggle = (env === "development" || env === "test") &&
|
|
364
|
+
process.env.AGENT_MODE !== "production";
|
|
365
|
+
const routePath = options?.path ?? "/_agent-native/agent-chat";
|
|
366
|
+
// Resolve actions — prefer `actions`, fall back to deprecated `scripts`
|
|
367
|
+
const rawActions = options?.actions ?? options?.scripts;
|
|
368
|
+
const templateScripts = typeof rawActions === "function"
|
|
369
|
+
? await rawActions()
|
|
370
|
+
: (rawActions ?? {});
|
|
371
|
+
// Resource and cross-agent scripts are available in both prod and dev modes
|
|
372
|
+
const resourceScripts = await createResourceScriptEntries();
|
|
373
|
+
const callAgentScript = await createCallAgentScriptEntry();
|
|
374
|
+
// Auto-mount A2A protocol endpoints so every app is discoverable
|
|
375
|
+
// and callable by other agents via the standard protocol.
|
|
376
|
+
// The custom handler calls the local agent-chat endpoint to process
|
|
377
|
+
// A2A messages using the same production agent pipeline.
|
|
378
|
+
const allScripts = {
|
|
379
|
+
...templateScripts,
|
|
380
|
+
...resourceScripts,
|
|
381
|
+
...callAgentScript,
|
|
382
|
+
};
|
|
383
|
+
const { mountA2A } = await import("../a2a/server.js");
|
|
384
|
+
mountA2A(nitroApp, {
|
|
385
|
+
name: options?.appId
|
|
386
|
+
? options.appId.charAt(0).toUpperCase() + options.appId.slice(1)
|
|
387
|
+
: "Agent",
|
|
388
|
+
description: `Agent-native ${options?.appId ?? "app"} agent`,
|
|
389
|
+
skills: Object.entries(allScripts).map(([name, entry]) => ({
|
|
390
|
+
id: name,
|
|
391
|
+
name,
|
|
392
|
+
description: entry.tool.description,
|
|
393
|
+
})),
|
|
394
|
+
streaming: true,
|
|
395
|
+
handler: async function* (message, context) {
|
|
396
|
+
// Resolve the caller's identity for user-scoped data access.
|
|
397
|
+
// Dev: use most recent session from local DB (single user, no verification).
|
|
398
|
+
// Prod: verify Google OAuth token from caller, check email domain.
|
|
399
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
400
|
+
let userEmail;
|
|
401
|
+
if (isDev) {
|
|
402
|
+
// Dev mode: trust the caller's claimed email, or auto-detect from local sessions
|
|
403
|
+
userEmail = context.metadata?.userEmail || undefined;
|
|
404
|
+
if (!userEmail) {
|
|
405
|
+
try {
|
|
406
|
+
const { getDbExec } = await import("../db/client.js");
|
|
407
|
+
const db = getDbExec();
|
|
408
|
+
const { rows } = await db.execute({
|
|
409
|
+
sql: "SELECT email FROM sessions ORDER BY created_at DESC LIMIT 1",
|
|
410
|
+
args: [],
|
|
411
|
+
});
|
|
412
|
+
if (rows[0])
|
|
413
|
+
userEmail = rows[0].email;
|
|
414
|
+
}
|
|
415
|
+
catch { }
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
// Prod mode: verify identity via Google OAuth token
|
|
420
|
+
const googleToken = context.metadata?.googleToken;
|
|
421
|
+
if (googleToken) {
|
|
422
|
+
try {
|
|
423
|
+
const res = await fetch(`https://oauth2.googleapis.com/tokeninfo?access_token=${encodeURIComponent(googleToken)}`);
|
|
424
|
+
if (res.ok) {
|
|
425
|
+
const info = (await res.json());
|
|
426
|
+
if (info.email && info.email_verified === "true") {
|
|
427
|
+
userEmail = info.email;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch { }
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (userEmail) {
|
|
435
|
+
process.env.AGENT_USER_EMAIL = userEmail;
|
|
436
|
+
}
|
|
437
|
+
const text = message.parts
|
|
438
|
+
.filter((p) => p.type === "text")
|
|
439
|
+
.map((p) => p.text)
|
|
440
|
+
.join("\n");
|
|
441
|
+
if (!text) {
|
|
442
|
+
yield {
|
|
443
|
+
role: "agent",
|
|
444
|
+
parts: [
|
|
445
|
+
{ type: "text", text: "No text content in message" },
|
|
446
|
+
],
|
|
447
|
+
};
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
451
|
+
const apiKey = options?.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
452
|
+
if (!apiKey) {
|
|
453
|
+
yield {
|
|
454
|
+
role: "agent",
|
|
455
|
+
parts: [
|
|
456
|
+
{
|
|
457
|
+
type: "text",
|
|
458
|
+
text: "Anthropic API key is not configured. Set ANTHROPIC_API_KEY in .env.",
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
};
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const client = new Anthropic({ apiKey });
|
|
465
|
+
const model = options?.model ?? "claude-sonnet-4-6";
|
|
466
|
+
// Load shared AGENTS.md resource so the agent knows how to use its tools
|
|
467
|
+
const owner = userEmail || "local@localhost";
|
|
468
|
+
const resources = await loadResourcesForPrompt(owner);
|
|
469
|
+
const a2aSystemPrompt = `You are the ${options?.appId ?? "app"} agent responding to a request from another agent. Be concise and helpful. Use your tools to look up data or take actions as needed. Do not ask for clarification — use your tools to find the answer.` +
|
|
470
|
+
resources;
|
|
471
|
+
const tools = Object.entries(templateScripts).map(([name, entry]) => ({
|
|
472
|
+
name,
|
|
473
|
+
description: entry.tool.description,
|
|
474
|
+
input_schema: entry.tool.parameters ?? {
|
|
475
|
+
type: "object",
|
|
476
|
+
properties: {},
|
|
477
|
+
},
|
|
478
|
+
}));
|
|
479
|
+
const msgs = [{ role: "user", content: text }];
|
|
480
|
+
let accumulatedText = "";
|
|
481
|
+
for (let i = 0; i < 10; i++) {
|
|
482
|
+
// Stream the Anthropic response so we can yield text chunks
|
|
483
|
+
const apiStream = client.messages.stream({
|
|
484
|
+
model,
|
|
485
|
+
max_tokens: 4096,
|
|
486
|
+
system: a2aSystemPrompt,
|
|
487
|
+
tools: tools.length > 0 ? tools : undefined,
|
|
488
|
+
messages: msgs,
|
|
489
|
+
});
|
|
490
|
+
// Yield text deltas as they arrive
|
|
491
|
+
for await (const event of apiStream) {
|
|
492
|
+
if (event.type === "content_block_delta" &&
|
|
493
|
+
event.delta?.type === "text_delta") {
|
|
494
|
+
accumulatedText += event.delta.text;
|
|
495
|
+
yield {
|
|
496
|
+
role: "agent",
|
|
497
|
+
parts: [{ type: "text", text: accumulatedText }],
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Get the complete message for tool_use detection
|
|
502
|
+
const finalMessage = await apiStream.finalMessage();
|
|
503
|
+
const toolUseBlocks = finalMessage.content.filter((b) => b.type === "tool_use");
|
|
504
|
+
if (toolUseBlocks.length === 0 ||
|
|
505
|
+
finalMessage.stop_reason !== "tool_use") {
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
// Execute tools and continue the loop
|
|
509
|
+
msgs.push({ role: "assistant", content: finalMessage.content });
|
|
510
|
+
const toolResults = [];
|
|
511
|
+
for (const tb of toolUseBlocks) {
|
|
512
|
+
const script = allScripts[tb.name];
|
|
513
|
+
let result = `Unknown tool: ${tb.name}`;
|
|
514
|
+
if (script) {
|
|
515
|
+
try {
|
|
516
|
+
result = await script.run(tb.input);
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
result = `Error: ${err?.message}`;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
toolResults.push({
|
|
523
|
+
type: "tool_result",
|
|
524
|
+
tool_use_id: tb.id,
|
|
525
|
+
content: result,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
msgs.push({ role: "user", content: toolResults });
|
|
529
|
+
}
|
|
530
|
+
// Final yield if nothing was yielded yet
|
|
531
|
+
if (!accumulatedText) {
|
|
532
|
+
yield {
|
|
533
|
+
role: "agent",
|
|
534
|
+
parts: [{ type: "text", text: "(no response)" }],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
// Generate an "Available Actions" section from template-specific actions
|
|
540
|
+
// so the agent knows to use them instead of raw SQL
|
|
541
|
+
const actionsPrompt = generateActionsPrompt(templateScripts);
|
|
542
|
+
// Build system prompts — dynamic functions that pre-load resources per-request.
|
|
543
|
+
// Production gets PROD_FRAMEWORK_PROMPT, dev gets DEV_FRAMEWORK_PROMPT.
|
|
544
|
+
// Custom systemPrompt from options overrides the framework default entirely.
|
|
545
|
+
const prodPrompt = (options?.systemPrompt ?? PROD_FRAMEWORK_PROMPT) + actionsPrompt;
|
|
546
|
+
const devPrompt = (options?.devSystemPrompt
|
|
547
|
+
? options.devSystemPrompt +
|
|
548
|
+
(options?.systemPrompt ?? PROD_FRAMEWORK_PROMPT)
|
|
549
|
+
: DEV_FRAMEWORK_PROMPT) + actionsPrompt;
|
|
550
|
+
// Keep legacy names for the composition below
|
|
551
|
+
const basePrompt = prodPrompt;
|
|
53
552
|
const devPrefix = options?.devSystemPrompt ?? DEFAULT_DEV_PROMPT;
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
553
|
+
// Resolve owner from the H3 event's session — matches how resources are created
|
|
554
|
+
const getOwnerFromEvent = async (event) => {
|
|
555
|
+
try {
|
|
556
|
+
const session = await getSession(event);
|
|
557
|
+
return session?.email || "local@localhost";
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return "local@localhost";
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
// Callback to persist agent response when run finishes (even if client disconnected).
|
|
564
|
+
// Reconstructs the assistant message from buffered events and appends to thread_data.
|
|
565
|
+
const onRunComplete = async (run, threadId) => {
|
|
566
|
+
if (!threadId)
|
|
567
|
+
return;
|
|
568
|
+
try {
|
|
569
|
+
const thread = await getThread(threadId);
|
|
570
|
+
if (!thread)
|
|
571
|
+
return;
|
|
572
|
+
const assistantMsg = buildAssistantMessage(run.events ?? [], run.runId);
|
|
573
|
+
if (!assistantMsg) {
|
|
574
|
+
// No content produced — just bump timestamp
|
|
575
|
+
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
// Parse existing thread_data, append assistant message only if
|
|
579
|
+
// the frontend hasn't already saved it (avoids duplicates when
|
|
580
|
+
// the client is still connected during a normal flow).
|
|
581
|
+
let repo;
|
|
582
|
+
try {
|
|
583
|
+
repo = JSON.parse(thread.threadData || "{}");
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
repo = {};
|
|
587
|
+
}
|
|
588
|
+
if (!Array.isArray(repo.messages))
|
|
589
|
+
repo.messages = [];
|
|
590
|
+
const lastMsg = repo.messages[repo.messages.length - 1];
|
|
591
|
+
if (lastMsg?.role === "assistant") {
|
|
592
|
+
// Frontend already saved the assistant response — just bump timestamp
|
|
593
|
+
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
repo.messages.push(assistantMsg);
|
|
597
|
+
const meta = extractThreadMeta(repo);
|
|
598
|
+
await updateThreadData(threadId, JSON.stringify(repo), meta.title || thread.title, meta.preview || thread.preview, repo.messages.length);
|
|
599
|
+
}
|
|
600
|
+
catch {
|
|
601
|
+
// Best-effort — don't break cleanup
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
// Always build the production handler (includes resource tools + call-agent)
|
|
605
|
+
const prodHandler = createProductionAgentHandler({
|
|
606
|
+
actions: { ...templateScripts, ...resourceScripts, ...callAgentScript },
|
|
607
|
+
systemPrompt: async (event) => {
|
|
608
|
+
const owner = await getOwnerFromEvent(event);
|
|
609
|
+
const resources = await loadResourcesForPrompt(owner);
|
|
610
|
+
return basePrompt + resources;
|
|
611
|
+
},
|
|
58
612
|
model: options?.model,
|
|
59
613
|
apiKey: options?.apiKey,
|
|
614
|
+
onRunComplete,
|
|
60
615
|
});
|
|
616
|
+
// Build the dev handler (with filesystem/shell/db tools) if environment allows toggling
|
|
617
|
+
let devHandler = null;
|
|
618
|
+
if (canToggle) {
|
|
619
|
+
const { createDevScriptRegistry } = await import("../scripts/dev/index.js");
|
|
620
|
+
const devActions = {
|
|
621
|
+
...templateScripts,
|
|
622
|
+
...resourceScripts,
|
|
623
|
+
...callAgentScript,
|
|
624
|
+
...(await createDevScriptRegistry()),
|
|
625
|
+
};
|
|
626
|
+
devHandler = createProductionAgentHandler({
|
|
627
|
+
actions: devActions,
|
|
628
|
+
systemPrompt: async (event) => {
|
|
629
|
+
const owner = await getOwnerFromEvent(event);
|
|
630
|
+
const resources = await loadResourcesForPrompt(owner);
|
|
631
|
+
return devPrompt + resources;
|
|
632
|
+
},
|
|
633
|
+
model: options?.model,
|
|
634
|
+
apiKey: options?.apiKey,
|
|
635
|
+
onRunComplete,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
// Resolve mention providers
|
|
639
|
+
const rawProviders = options?.mentionProviders;
|
|
640
|
+
const mentionProviders = typeof rawProviders === "function"
|
|
641
|
+
? await rawProviders()
|
|
642
|
+
: (rawProviders ?? {});
|
|
643
|
+
// Mutable mode flag — starts in dev if environment allows
|
|
644
|
+
let currentDevMode = canToggle;
|
|
645
|
+
// Mount mode endpoint — GET returns current mode, POST toggles it (localhost only)
|
|
646
|
+
nitroApp.h3App.use(`${routePath}/mode`, defineEventHandler(async (event) => {
|
|
647
|
+
if (getMethod(event) === "POST") {
|
|
648
|
+
if (!canToggle) {
|
|
649
|
+
setResponseStatus(event, 403);
|
|
650
|
+
return { error: "Mode switching not available in production" };
|
|
651
|
+
}
|
|
652
|
+
if (!isLocalhost(event)) {
|
|
653
|
+
setResponseStatus(event, 403);
|
|
654
|
+
return { error: "Mode switching only available on localhost" };
|
|
655
|
+
}
|
|
656
|
+
const body = await readBody(event);
|
|
657
|
+
if (typeof body?.devMode === "boolean") {
|
|
658
|
+
currentDevMode = body.devMode;
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
currentDevMode = !currentDevMode;
|
|
662
|
+
}
|
|
663
|
+
return { devMode: currentDevMode, canToggle };
|
|
664
|
+
}
|
|
665
|
+
return { devMode: currentDevMode, canToggle };
|
|
666
|
+
}));
|
|
61
667
|
// Mount save-key BEFORE the prefix handler so it isn't shadowed
|
|
668
|
+
// Only functional in Node.js environments (writes to .env file)
|
|
62
669
|
nitroApp.h3App.use(`${routePath}/save-key`, defineEventHandler(async (event) => {
|
|
63
670
|
if (getMethod(event) !== "POST") {
|
|
64
671
|
setResponseStatus(event, 405);
|
|
@@ -71,20 +678,476 @@ export function createAgentChatPlugin(options) {
|
|
|
71
678
|
return { error: "API key is required" };
|
|
72
679
|
}
|
|
73
680
|
const trimmedKey = key.trim();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
|
|
681
|
+
try {
|
|
682
|
+
const path = await import("path");
|
|
683
|
+
const { upsertEnvFile } = await import("./create-server.js");
|
|
684
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
685
|
+
upsertEnvFile(envPath, [
|
|
686
|
+
{ key: "ANTHROPIC_API_KEY", value: trimmedKey },
|
|
687
|
+
]);
|
|
688
|
+
}
|
|
689
|
+
catch {
|
|
690
|
+
// Edge runtime — can't write .env, but can still update process.env
|
|
691
|
+
}
|
|
78
692
|
// Update process.env so the agent works immediately
|
|
79
693
|
process.env.ANTHROPIC_API_KEY = trimmedKey;
|
|
80
694
|
return { ok: true };
|
|
81
695
|
}));
|
|
82
|
-
// Mount
|
|
83
|
-
nitroApp.h3App.use(routePath
|
|
696
|
+
// Mount file search endpoint
|
|
697
|
+
nitroApp.h3App.use(`${routePath}/files`, defineEventHandler(async (event) => {
|
|
698
|
+
if (getMethod(event) !== "GET") {
|
|
699
|
+
setResponseStatus(event, 405);
|
|
700
|
+
return { error: "Method not allowed" };
|
|
701
|
+
}
|
|
702
|
+
const query = getQuery(event);
|
|
703
|
+
const q = typeof query.q === "string" ? query.q.toLowerCase() : "";
|
|
704
|
+
const files = [];
|
|
705
|
+
const seen = new Set();
|
|
706
|
+
// In dev mode, walk the filesystem
|
|
707
|
+
if (currentDevMode) {
|
|
708
|
+
const codebaseFiles = [];
|
|
709
|
+
try {
|
|
710
|
+
collectFiles(process.cwd(), "", 0, codebaseFiles);
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
// Filesystem access failed — skip
|
|
714
|
+
}
|
|
715
|
+
for (const f of codebaseFiles) {
|
|
716
|
+
if (!seen.has(f.path)) {
|
|
717
|
+
seen.add(f.path);
|
|
718
|
+
files.push({
|
|
719
|
+
path: f.path,
|
|
720
|
+
name: f.name,
|
|
721
|
+
source: "codebase",
|
|
722
|
+
type: f.type,
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// Query resources
|
|
728
|
+
try {
|
|
729
|
+
const resources = currentDevMode
|
|
730
|
+
? await resourceListAccessible("local@localhost")
|
|
731
|
+
: await resourceList(SHARED_OWNER);
|
|
732
|
+
for (const r of resources) {
|
|
733
|
+
if (!seen.has(r.path)) {
|
|
734
|
+
seen.add(r.path);
|
|
735
|
+
files.push({
|
|
736
|
+
path: r.path,
|
|
737
|
+
name: r.path.split("/").pop() || r.path,
|
|
738
|
+
source: "resource",
|
|
739
|
+
type: "file",
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
catch {
|
|
745
|
+
// Resources not available — skip
|
|
746
|
+
}
|
|
747
|
+
// Filter by query and limit
|
|
748
|
+
const filtered = q
|
|
749
|
+
? files.filter((f) => f.path.toLowerCase().includes(q))
|
|
750
|
+
: files;
|
|
751
|
+
return { files: filtered.slice(0, 30) };
|
|
752
|
+
}));
|
|
753
|
+
// Mount skills listing endpoint
|
|
754
|
+
nitroApp.h3App.use(`${routePath}/skills`, defineEventHandler(async (event) => {
|
|
755
|
+
if (getMethod(event) !== "GET") {
|
|
756
|
+
setResponseStatus(event, 405);
|
|
757
|
+
return { error: "Method not allowed" };
|
|
758
|
+
}
|
|
759
|
+
const skills = [];
|
|
760
|
+
const seenNames = new Set();
|
|
761
|
+
// In dev mode, scan .agents/skills/ directory
|
|
762
|
+
if (currentDevMode) {
|
|
763
|
+
try {
|
|
764
|
+
const skillsDir = nodePath.join(process.cwd(), ".agents", "skills");
|
|
765
|
+
const entries = fs.readdirSync(skillsDir, {
|
|
766
|
+
withFileTypes: true,
|
|
767
|
+
});
|
|
768
|
+
for (const entry of entries) {
|
|
769
|
+
// Support both flat .md files and subdirectory-based skills (dir/SKILL.md)
|
|
770
|
+
let skillFilePath;
|
|
771
|
+
let skillRelPath;
|
|
772
|
+
if (entry.isDirectory()) {
|
|
773
|
+
// Subdirectory layout: .agents/skills/<name>/SKILL.md
|
|
774
|
+
const candidate = nodePath.join(skillsDir, entry.name, "SKILL.md");
|
|
775
|
+
if (!fs.existsSync(candidate))
|
|
776
|
+
continue;
|
|
777
|
+
skillFilePath = candidate;
|
|
778
|
+
skillRelPath = `.agents/skills/${entry.name}/SKILL.md`;
|
|
779
|
+
}
|
|
780
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
781
|
+
// Flat layout: .agents/skills/<name>.md
|
|
782
|
+
skillFilePath = nodePath.join(skillsDir, entry.name);
|
|
783
|
+
skillRelPath = `.agents/skills/${entry.name}`;
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
try {
|
|
789
|
+
const content = fs.readFileSync(skillFilePath, "utf-8");
|
|
790
|
+
const fm = parseSkillFrontmatter(content);
|
|
791
|
+
const skillName = fm.name || entry.name.replace(/\.md$/, "");
|
|
792
|
+
if (!seenNames.has(skillName)) {
|
|
793
|
+
seenNames.add(skillName);
|
|
794
|
+
skills.push({
|
|
795
|
+
name: skillName,
|
|
796
|
+
description: fm.description,
|
|
797
|
+
path: skillRelPath,
|
|
798
|
+
source: "codebase",
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
// Could not read individual skill file — skip
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
// .agents/skills/ directory doesn't exist or not readable — skip
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
// Query resources with skills/ prefix
|
|
812
|
+
try {
|
|
813
|
+
const resourceSkills = currentDevMode
|
|
814
|
+
? await resourceListAccessible("local@localhost", "skills/")
|
|
815
|
+
: await resourceList(SHARED_OWNER, "skills/");
|
|
816
|
+
for (const r of resourceSkills) {
|
|
817
|
+
// Try to get content to parse frontmatter
|
|
818
|
+
let skillName = r.path.split("/").pop()?.replace(/\.md$/, "") || r.path;
|
|
819
|
+
let description;
|
|
820
|
+
try {
|
|
821
|
+
const full = await resourceGet(r.id);
|
|
822
|
+
if (full) {
|
|
823
|
+
const fm = parseSkillFrontmatter(full.content);
|
|
824
|
+
if (fm.name)
|
|
825
|
+
skillName = fm.name;
|
|
826
|
+
description = fm.description;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
// Could not read resource content — use path-based name
|
|
831
|
+
}
|
|
832
|
+
if (!seenNames.has(skillName)) {
|
|
833
|
+
seenNames.add(skillName);
|
|
834
|
+
skills.push({
|
|
835
|
+
name: skillName,
|
|
836
|
+
description,
|
|
837
|
+
path: r.path,
|
|
838
|
+
source: "resource",
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
catch {
|
|
844
|
+
// Resources not available — skip
|
|
845
|
+
}
|
|
846
|
+
const result = { skills };
|
|
847
|
+
if (skills.length === 0) {
|
|
848
|
+
result.hint =
|
|
849
|
+
"No skills found. Add skill files under skills/ in Resources. Learn more: https://agent-native.com/docs/resources#skills";
|
|
850
|
+
}
|
|
851
|
+
return result;
|
|
852
|
+
}));
|
|
853
|
+
// Mount unified mentions endpoint (files + resources + custom providers)
|
|
854
|
+
nitroApp.h3App.use(`${routePath}/mentions`, defineEventHandler(async (event) => {
|
|
855
|
+
if (getMethod(event) !== "GET") {
|
|
856
|
+
setResponseStatus(event, 405);
|
|
857
|
+
return { error: "Method not allowed" };
|
|
858
|
+
}
|
|
859
|
+
const query = getQuery(event);
|
|
860
|
+
const q = typeof query.q === "string" ? query.q.toLowerCase() : "";
|
|
861
|
+
const items = [];
|
|
862
|
+
// 1. Built-in: files from codebase (dev mode only)
|
|
863
|
+
if (currentDevMode) {
|
|
864
|
+
const codebaseFiles = [];
|
|
865
|
+
try {
|
|
866
|
+
collectFiles(process.cwd(), "", 0, codebaseFiles);
|
|
867
|
+
}
|
|
868
|
+
catch { }
|
|
869
|
+
for (const f of codebaseFiles) {
|
|
870
|
+
items.push({
|
|
871
|
+
id: `codebase:${f.path}`,
|
|
872
|
+
label: f.name,
|
|
873
|
+
description: f.path !== f.name ? f.path : undefined,
|
|
874
|
+
icon: f.type,
|
|
875
|
+
source: "codebase",
|
|
876
|
+
refType: "file",
|
|
877
|
+
refPath: f.path,
|
|
878
|
+
section: "Files",
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
// 2. Built-in: resources from SQL
|
|
883
|
+
try {
|
|
884
|
+
const resources = currentDevMode
|
|
885
|
+
? await resourceListAccessible("local@localhost")
|
|
886
|
+
: await resourceList(SHARED_OWNER);
|
|
887
|
+
for (const r of resources) {
|
|
888
|
+
const isShared = r.owner === SHARED_OWNER;
|
|
889
|
+
items.push({
|
|
890
|
+
id: `resource:${r.path}`,
|
|
891
|
+
label: r.path.split("/").pop() || r.path,
|
|
892
|
+
description: r.path,
|
|
893
|
+
icon: "file",
|
|
894
|
+
source: isShared ? "resource:shared" : "resource:private",
|
|
895
|
+
refType: "file",
|
|
896
|
+
refPath: r.path,
|
|
897
|
+
section: "Files",
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
catch { }
|
|
902
|
+
// 3. Custom mention providers
|
|
903
|
+
const providerResults = await Promise.all(Object.entries(mentionProviders).map(async ([key, provider]) => {
|
|
904
|
+
try {
|
|
905
|
+
const providerItems = await provider.search(q);
|
|
906
|
+
return providerItems.map((item) => ({
|
|
907
|
+
id: item.id,
|
|
908
|
+
label: item.label,
|
|
909
|
+
description: item.description,
|
|
910
|
+
icon: item.icon || provider.icon || "file",
|
|
911
|
+
source: key,
|
|
912
|
+
refType: item.refType,
|
|
913
|
+
refPath: item.refPath,
|
|
914
|
+
refId: item.refId,
|
|
915
|
+
section: provider.label,
|
|
916
|
+
}));
|
|
917
|
+
}
|
|
918
|
+
catch {
|
|
919
|
+
return [];
|
|
920
|
+
}
|
|
921
|
+
}));
|
|
922
|
+
for (const batch of providerResults) {
|
|
923
|
+
items.push(...batch);
|
|
924
|
+
}
|
|
925
|
+
// 4. Discovered peer agents
|
|
926
|
+
try {
|
|
927
|
+
const { discoverAgents } = await import("./agent-discovery.js");
|
|
928
|
+
const agents = discoverAgents(options?.appId);
|
|
929
|
+
for (const agent of agents) {
|
|
930
|
+
items.push({
|
|
931
|
+
id: `agent:${agent.id}`,
|
|
932
|
+
label: agent.name,
|
|
933
|
+
description: agent.description,
|
|
934
|
+
icon: "agent",
|
|
935
|
+
source: "agent",
|
|
936
|
+
refType: "agent",
|
|
937
|
+
refPath: agent.url,
|
|
938
|
+
refId: agent.id,
|
|
939
|
+
section: "Agents",
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
catch {
|
|
944
|
+
// Agent discovery not available — skip
|
|
945
|
+
}
|
|
946
|
+
// Filter by query and limit
|
|
947
|
+
const filtered = q
|
|
948
|
+
? items.filter((item) => item.label.toLowerCase().includes(q) ||
|
|
949
|
+
(item.description?.toLowerCase().includes(q) ?? false))
|
|
950
|
+
: items;
|
|
951
|
+
return { items: filtered.slice(0, 30) };
|
|
952
|
+
}));
|
|
953
|
+
// ─── Generate thread title ──────────────────────────────────────────
|
|
954
|
+
nitroApp.h3App.use(`${routePath}/generate-title`, defineEventHandler(async (event) => {
|
|
955
|
+
if (getMethod(event) !== "POST") {
|
|
956
|
+
setResponseStatus(event, 405);
|
|
957
|
+
return { error: "Method not allowed" };
|
|
958
|
+
}
|
|
959
|
+
await getOwnerFromEvent(event);
|
|
960
|
+
const body = await readBody(event);
|
|
961
|
+
const message = body?.message;
|
|
962
|
+
if (!message || typeof message !== "string") {
|
|
963
|
+
setResponseStatus(event, 400);
|
|
964
|
+
return { error: "message is required" };
|
|
965
|
+
}
|
|
966
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
967
|
+
if (!apiKey) {
|
|
968
|
+
// Fallback: truncate the message
|
|
969
|
+
return { title: message.trim().slice(0, 60) };
|
|
970
|
+
}
|
|
971
|
+
try {
|
|
972
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
973
|
+
method: "POST",
|
|
974
|
+
headers: {
|
|
975
|
+
"Content-Type": "application/json",
|
|
976
|
+
"x-api-key": apiKey,
|
|
977
|
+
"anthropic-version": "2023-06-01",
|
|
978
|
+
},
|
|
979
|
+
body: JSON.stringify({
|
|
980
|
+
model: "claude-haiku-4-5-20251001",
|
|
981
|
+
max_tokens: 30,
|
|
982
|
+
messages: [
|
|
983
|
+
{
|
|
984
|
+
role: "user",
|
|
985
|
+
content: `Generate a very short title (3-6 words, no quotes) for a chat that starts with this message:\n\n${message.slice(0, 500)}`,
|
|
986
|
+
},
|
|
987
|
+
],
|
|
988
|
+
}),
|
|
989
|
+
});
|
|
990
|
+
if (!res.ok) {
|
|
991
|
+
return { title: message.trim().slice(0, 60) };
|
|
992
|
+
}
|
|
993
|
+
const data = (await res.json());
|
|
994
|
+
const text = data.content?.[0]?.text?.trim();
|
|
995
|
+
return { title: text || message.trim().slice(0, 60) };
|
|
996
|
+
}
|
|
997
|
+
catch {
|
|
998
|
+
return { title: message.trim().slice(0, 60) };
|
|
999
|
+
}
|
|
1000
|
+
}));
|
|
1001
|
+
// ─── Run management endpoints (for hot-reload resilience) ─────────────
|
|
1002
|
+
// GET /runs/active?threadId=X — check if there's an active run for a thread
|
|
1003
|
+
nitroApp.h3App.use(`${routePath}/runs`, defineEventHandler(async (event) => {
|
|
1004
|
+
// Auth check — ensure the user is authenticated
|
|
1005
|
+
await getOwnerFromEvent(event);
|
|
1006
|
+
const method = getMethod(event);
|
|
1007
|
+
const url = event.node?.req?.url || event.path || "";
|
|
1008
|
+
// Route: POST /runs/:id/abort
|
|
1009
|
+
const abortMatch = url.match(/\/runs\/([^/?]+)\/abort/);
|
|
1010
|
+
if (abortMatch && method === "POST") {
|
|
1011
|
+
const runId = decodeURIComponent(abortMatch[1]);
|
|
1012
|
+
abortRun(runId); // Aborts in-memory + marks aborted in SQL
|
|
1013
|
+
return { ok: true };
|
|
1014
|
+
}
|
|
1015
|
+
// Route: GET /runs/:id/events?after=N
|
|
1016
|
+
const eventsMatch = url.match(/\/runs\/([^/?]+)\/events/);
|
|
1017
|
+
if (eventsMatch && method === "GET") {
|
|
1018
|
+
const runId = decodeURIComponent(eventsMatch[1]);
|
|
1019
|
+
const query = getQuery(event);
|
|
1020
|
+
const after = parseInt(String(query.after ?? "0"), 10) || 0;
|
|
1021
|
+
const stream = subscribeToRun(runId, after);
|
|
1022
|
+
if (!stream) {
|
|
1023
|
+
setResponseStatus(event, 404);
|
|
1024
|
+
return { error: "Run not found" };
|
|
1025
|
+
}
|
|
1026
|
+
setResponseHeader(event, "Content-Type", "text/event-stream");
|
|
1027
|
+
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
1028
|
+
setResponseHeader(event, "Connection", "keep-alive");
|
|
1029
|
+
return stream;
|
|
1030
|
+
}
|
|
1031
|
+
// Route: GET /runs/active?threadId=X
|
|
1032
|
+
if (method === "GET") {
|
|
1033
|
+
const query = getQuery(event);
|
|
1034
|
+
const threadId = query.threadId ? String(query.threadId) : null;
|
|
1035
|
+
if (!threadId) {
|
|
1036
|
+
setResponseStatus(event, 400);
|
|
1037
|
+
return { error: "threadId query parameter is required" };
|
|
1038
|
+
}
|
|
1039
|
+
// Check in-memory first, then SQL (cross-isolate on Workers)
|
|
1040
|
+
const run = await getActiveRunForThreadAsync(threadId);
|
|
1041
|
+
if (!run) {
|
|
1042
|
+
setResponseStatus(event, 404);
|
|
1043
|
+
return { error: "No active run for this thread" };
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
runId: run.runId,
|
|
1047
|
+
threadId: run.threadId,
|
|
1048
|
+
status: run.status,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
setResponseStatus(event, 405);
|
|
1052
|
+
return { error: "Method not allowed" };
|
|
1053
|
+
}));
|
|
1054
|
+
// ─── Thread management endpoints ──────────────────────────────────────
|
|
1055
|
+
// Single handler for /threads and /threads/:id — h3's use() does prefix
|
|
1056
|
+
// matching so we can't reliably split them into separate handlers.
|
|
1057
|
+
nitroApp.h3App.use(`${routePath}/threads`, defineEventHandler(async (event) => {
|
|
1058
|
+
const owner = await getOwnerFromEvent(event);
|
|
1059
|
+
const method = getMethod(event);
|
|
1060
|
+
// Determine if this is a specific-thread request.
|
|
1061
|
+
// h3's use() strips the mount prefix, so event.path contains
|
|
1062
|
+
// only the remainder after /threads — e.g., "/thread-abc" or "/".
|
|
1063
|
+
// We also check the original URL as a fallback.
|
|
1064
|
+
const remainder = (event.path || "").replace(/^\/+/, "");
|
|
1065
|
+
const fromUrl = (event.node?.req?.url || "").match(/\/threads\/([^/?]+)/);
|
|
1066
|
+
const threadId = remainder
|
|
1067
|
+
? decodeURIComponent(remainder.split("?")[0].split("/")[0])
|
|
1068
|
+
: fromUrl
|
|
1069
|
+
? decodeURIComponent(fromUrl[1])
|
|
1070
|
+
: null;
|
|
1071
|
+
// ── Specific thread: GET/PUT/DELETE /threads/:id ──
|
|
1072
|
+
if (threadId) {
|
|
1073
|
+
if (method === "GET") {
|
|
1074
|
+
const thread = await getThread(threadId);
|
|
1075
|
+
if (!thread || thread.ownerEmail !== owner) {
|
|
1076
|
+
setResponseStatus(event, 404);
|
|
1077
|
+
return { error: "Thread not found" };
|
|
1078
|
+
}
|
|
1079
|
+
return thread;
|
|
1080
|
+
}
|
|
1081
|
+
if (method === "PUT") {
|
|
1082
|
+
const thread = await getThread(threadId);
|
|
1083
|
+
if (!thread || thread.ownerEmail !== owner) {
|
|
1084
|
+
setResponseStatus(event, 404);
|
|
1085
|
+
return { error: "Thread not found" };
|
|
1086
|
+
}
|
|
1087
|
+
const body = await readBody(event);
|
|
1088
|
+
await updateThreadData(threadId, body.threadData || thread.threadData, body.title ?? thread.title, body.preview ?? thread.preview, body.messageCount || thread.messageCount);
|
|
1089
|
+
return { ok: true };
|
|
1090
|
+
}
|
|
1091
|
+
if (method === "DELETE") {
|
|
1092
|
+
const thread = await getThread(threadId);
|
|
1093
|
+
if (!thread || thread.ownerEmail !== owner) {
|
|
1094
|
+
setResponseStatus(event, 404);
|
|
1095
|
+
return { error: "Thread not found" };
|
|
1096
|
+
}
|
|
1097
|
+
await deleteThread(threadId);
|
|
1098
|
+
return { ok: true };
|
|
1099
|
+
}
|
|
1100
|
+
setResponseStatus(event, 405);
|
|
1101
|
+
return { error: "Method not allowed" };
|
|
1102
|
+
}
|
|
1103
|
+
// ── Thread list: GET/POST /threads ──
|
|
1104
|
+
if (method === "GET") {
|
|
1105
|
+
const query = getQuery(event);
|
|
1106
|
+
const limit = Math.min(parseInt(String(query.limit ?? "50"), 10) || 50, 200);
|
|
1107
|
+
const q = query.q ? String(query.q).trim() : "";
|
|
1108
|
+
if (q) {
|
|
1109
|
+
const threads = await searchThreads(owner, q, limit);
|
|
1110
|
+
return { threads };
|
|
1111
|
+
}
|
|
1112
|
+
const offset = parseInt(String(query.offset ?? "0"), 10) || 0;
|
|
1113
|
+
const threads = await listThreads(owner, limit, offset);
|
|
1114
|
+
return { threads };
|
|
1115
|
+
}
|
|
1116
|
+
if (method === "POST") {
|
|
1117
|
+
const body = await readBody(event);
|
|
1118
|
+
const thread = await createThread(owner, {
|
|
1119
|
+
title: body?.title ?? "",
|
|
1120
|
+
});
|
|
1121
|
+
return thread;
|
|
1122
|
+
}
|
|
1123
|
+
setResponseStatus(event, 405);
|
|
1124
|
+
return { error: "Method not allowed" };
|
|
1125
|
+
}));
|
|
1126
|
+
// Mount the main chat handler — delegates to dev or prod handler based on current mode.
|
|
1127
|
+
// This is mounted last because h3's use() is prefix-based, meaning /_agent-native/agent-chat
|
|
1128
|
+
// also matches /_agent-native/agent-chat/threads/... — we skip sub-path requests here so the
|
|
1129
|
+
// earlier-mounted handlers (mode, save-key, files, skills, mentions, threads) handle them.
|
|
1130
|
+
nitroApp.h3App.use(routePath, defineEventHandler(async (event) => {
|
|
1131
|
+
// Skip sub-path requests — they're handled by earlier-mounted handlers
|
|
1132
|
+
const url = event.node?.req?.url || event.path || "";
|
|
1133
|
+
const afterBase = url.slice(url.indexOf(routePath) + routePath.length);
|
|
1134
|
+
if (afterBase && afterBase !== "/" && !afterBase.startsWith("?")) {
|
|
1135
|
+
// Not for us — return 404 so h3 doesn't swallow the request
|
|
1136
|
+
setResponseStatus(event, 404);
|
|
1137
|
+
return { error: "Not found" };
|
|
1138
|
+
}
|
|
1139
|
+
// Set AGENT_USER_EMAIL so scripts resolve the same owner as the session.
|
|
1140
|
+
// Without this, scripts default to "local@localhost" and miss resources
|
|
1141
|
+
// created by users who authenticated via OAuth (e.g., Gmail).
|
|
1142
|
+
const owner = await getOwnerFromEvent(event);
|
|
1143
|
+
process.env.AGENT_USER_EMAIL = owner;
|
|
1144
|
+
const handler = currentDevMode && devHandler ? devHandler : prodHandler;
|
|
1145
|
+
return handler(event);
|
|
1146
|
+
}));
|
|
84
1147
|
};
|
|
85
1148
|
}
|
|
86
1149
|
/**
|
|
87
|
-
* Default agent chat plugin with no template-specific
|
|
1150
|
+
* Default agent chat plugin with no template-specific actions.
|
|
88
1151
|
* In dev mode, provides file system, shell, and database tools.
|
|
89
1152
|
* In production, provides only the default system prompt.
|
|
90
1153
|
*/
|