@agent-native/core 0.7.4 → 0.7.7
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 +6 -5
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +8 -4
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/types.d.ts +1 -1
- package/dist/agent/engine/types.d.ts.map +1 -1
- package/dist/agent/production-agent.d.ts +7 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +153 -118
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +4 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +46 -25
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +12 -3
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +25 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +13 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +66 -10
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +8 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/info.d.ts +2 -0
- package/dist/cli/info.d.ts.map +1 -0
- package/dist/cli/info.js +103 -0
- package/dist/cli/info.js.map +1 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +249 -85
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +12 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +46 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts +21 -0
- package/dist/client/composer/VoiceButton.d.ts.map +1 -0
- package/dist/client/composer/VoiceButton.js +51 -0
- package/dist/client/composer/VoiceButton.js.map +1 -0
- package/dist/client/composer/useVoiceDictation.d.ts +38 -0
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -0
- package/dist/client/composer/useVoiceDictation.js +398 -0
- package/dist/client/composer/useVoiceDictation.js.map +1 -0
- package/dist/client/onboarding/OnboardingPanel.js +2 -2
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +5 -4
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +90 -24
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/resources/McpServerDetail.d.ts +15 -0
- package/dist/client/resources/McpServerDetail.d.ts.map +1 -0
- package/dist/client/resources/McpServerDetail.js +65 -0
- package/dist/client/resources/McpServerDetail.js.map +1 -0
- package/dist/client/resources/ResourceEditor.js +1 -1
- package/dist/client/resources/ResourceEditor.js.map +1 -1
- package/dist/client/resources/ResourceTree.d.ts +6 -1
- package/dist/client/resources/ResourceTree.d.ts.map +1 -1
- package/dist/client/resources/ResourceTree.js +18 -7
- package/dist/client/resources/ResourceTree.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +191 -20
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +68 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -0
- package/dist/client/resources/use-mcp-servers.js +83 -0
- package/dist/client/resources/use-mcp-servers.js.map +1 -0
- package/dist/client/resources/use-resources.d.ts +39 -1
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +102 -0
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +3 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts +14 -0
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -0
- package/dist/client/settings/VoiceTranscriptionSection.js +111 -0
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -0
- package/dist/client/sharing/ShareButton.d.ts +6 -4
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +299 -34
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareDialog.d.ts +22 -4
- package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
- package/dist/client/sharing/ShareDialog.js +170 -148
- package/dist/client/sharing/ShareDialog.js.map +1 -1
- package/dist/client/sharing/VisibilityBadge.d.ts.map +1 -1
- package/dist/client/sharing/VisibilityBadge.js +1 -2
- package/dist/client/sharing/VisibilityBadge.js.map +1 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +20 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/db/migrations.d.ts +18 -3
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +25 -3
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/workspace-core.js +2 -2
- package/dist/mcp-client/config.d.ts +20 -1
- package/dist/mcp-client/config.d.ts.map +1 -1
- package/dist/mcp-client/config.js +28 -11
- package/dist/mcp-client/config.js.map +1 -1
- package/dist/mcp-client/hub-client.d.ts +38 -0
- package/dist/mcp-client/hub-client.d.ts.map +1 -0
- package/dist/mcp-client/hub-client.js +147 -0
- package/dist/mcp-client/hub-client.js.map +1 -0
- package/dist/mcp-client/hub-routes.d.ts +42 -0
- package/dist/mcp-client/hub-routes.d.ts.map +1 -0
- package/dist/mcp-client/hub-routes.js +114 -0
- package/dist/mcp-client/hub-routes.js.map +1 -0
- package/dist/mcp-client/index.d.ts +15 -0
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +35 -0
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/manager.d.ts +54 -8
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +276 -59
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/remote-store.d.ts +102 -0
- package/dist/mcp-client/remote-store.d.ts.map +1 -0
- package/dist/mcp-client/remote-store.js +200 -0
- package/dist/mcp-client/remote-store.js.map +1 -0
- package/dist/mcp-client/routes.d.ts +55 -0
- package/dist/mcp-client/routes.d.ts.map +1 -0
- package/dist/mcp-client/routes.js +384 -0
- package/dist/mcp-client/routes.js.map +1 -0
- package/dist/mcp-client/visibility.d.ts +16 -0
- package/dist/mcp-client/visibility.d.ts.map +1 -0
- package/dist/mcp-client/visibility.js +45 -0
- package/dist/mcp-client/visibility.js.map +1 -0
- package/dist/org/context.js +2 -2
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.js +2 -2
- package/dist/org/handlers.js.map +1 -1
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +30 -0
- package/dist/resources/handlers.js.map +1 -1
- package/dist/secrets/register-framework-secrets.d.ts +13 -0
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -0
- package/dist/secrets/register-framework-secrets.js +59 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -0
- package/dist/secrets/register.d.ts.map +1 -1
- package/dist/secrets/register.js +8 -1
- package/dist/secrets/register.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +22 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +16 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +237 -70
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/app-url.d.ts.map +1 -1
- package/dist/server/app-url.js +11 -3
- package/dist/server/app-url.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +50 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +99 -4
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +44 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +6 -0
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/date-utils.d.ts +15 -0
- package/dist/server/date-utils.d.ts.map +1 -0
- package/dist/server/date-utils.js +41 -0
- package/dist/server/date-utils.js.map +1 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts +3 -0
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +13 -3
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/request-context.d.ts +9 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +10 -0
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +26 -0
- package/dist/server/transcribe-voice.d.ts.map +1 -0
- package/dist/server/transcribe-voice.js +143 -0
- package/dist/server/transcribe-voice.js.map +1 -0
- package/dist/styles/agent-native.css +111 -0
- package/dist/tailwind.preset.d.ts +2 -2
- package/dist/tailwind.preset.d.ts.map +1 -1
- package/dist/tailwind.preset.js +27 -7
- package/dist/tailwind.preset.js.map +1 -1
- package/dist/templates/default/app/global.css +65 -68
- package/dist/templates/default/components.json +1 -1
- package/dist/templates/default/package.json +2 -4
- package/dist/templates/default/vite.config.ts +3 -0
- package/dist/templates/workspace-core/package.json +1 -4
- package/dist/templates/workspace-core/src/index.ts +1 -1
- package/dist/templates/workspace-core/styles/tokens.css +22 -0
- package/dist/templates/workspace-core/tsconfig.json +1 -1
- package/dist/vite/client.d.ts +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +18 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/actions.md +169 -74
- package/docs/content/agent-teams.md +139 -0
- package/docs/content/cloneable-saas.md +98 -0
- package/docs/content/creating-templates.md +9 -11
- package/docs/content/deployment.md +2 -9
- package/docs/content/drop-in-agent.md +200 -0
- package/docs/content/enterprise-workspace.md +22 -10
- package/docs/content/getting-started.md +34 -19
- package/docs/content/integrations.md +3 -3
- package/docs/content/key-concepts.md +50 -23
- package/docs/content/mcp-clients.md +71 -0
- package/docs/content/pure-agent-apps.md +69 -0
- package/docs/content/recurring-jobs.md +123 -0
- package/docs/content/skills-guide.md +8 -0
- package/docs/content/template-analytics.md +190 -0
- package/docs/content/template-calendar.md +151 -0
- package/docs/content/template-clips.md +55 -0
- package/docs/content/template-content.md +141 -0
- package/docs/content/template-dispatch.md +58 -0
- package/docs/content/template-forms.md +51 -0
- package/docs/content/template-mail.md +169 -0
- package/docs/content/template-slides.md +218 -0
- package/docs/content/template-starter.md +68 -0
- package/docs/content/template-video.md +162 -0
- package/docs/content/voice-input.md +59 -0
- package/docs/content/what-is-agent-native.md +142 -45
- package/docs/content/workspace-management.md +1 -0
- package/docs/content/{resources.md → workspace.md} +94 -42
- package/package.json +9 -16
- package/src/templates/default/app/global.css +65 -68
- package/src/templates/default/components.json +1 -1
- package/src/templates/default/package.json +2 -4
- package/src/templates/default/vite.config.ts +3 -0
- package/src/templates/workspace-core/package.json +1 -4
- package/src/templates/workspace-core/src/index.ts +1 -1
- package/src/templates/workspace-core/styles/tokens.css +22 -0
- package/src/templates/workspace-core/tsconfig.json +1 -1
- package/dist/templates/default/postcss.config.js +0 -6
- package/dist/templates/default/tailwind.config.ts +0 -7
- package/dist/templates/workspace-core/tailwind.preset.ts +0 -34
- package/src/templates/default/postcss.config.js +0 -6
- package/src/templates/default/tailwind.config.ts +0 -7
- package/src/templates/workspace-core/tailwind.preset.ts +0 -34
|
@@ -3,14 +3,14 @@ import { getSetting, putSetting } from "../settings/store.js";
|
|
|
3
3
|
import { getH3App } from "./framework-request-handler.js";
|
|
4
4
|
import { createProductionAgentHandler, runAgentLoop, actionsToEngineTools, getActiveRunForThreadAsync, abortRun, subscribeToRun, } from "../agent/production-agent.js";
|
|
5
5
|
import { resolveEngine, createAnthropicEngine } from "../agent/engine/index.js";
|
|
6
|
-
import { McpClientManager, loadMcpConfig, autoDetectMcpConfig, mcpToolsToActionEntries, } from "../mcp-client/index.js";
|
|
6
|
+
import { McpClientManager, loadMcpConfig, autoDetectMcpConfig, mcpToolsToActionEntries, syncMcpActionEntries, mountMcpServersRoutes, mountMcpHubRoutes, buildMergedConfig, getHubStatus, isHubServeEnabled, } from "../mcp-client/index.js";
|
|
7
7
|
import { discoverAgents } from "./agent-discovery.js";
|
|
8
8
|
import { loadSchemaPromptBlock } from "./schema-prompt.js";
|
|
9
9
|
import { buildAssistantMessage, extractThreadMeta, } from "../agent/thread-data-builder.js";
|
|
10
|
-
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, } from "h3";
|
|
10
|
+
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, getHeader, } from "h3";
|
|
11
11
|
import { getSession } from "./auth.js";
|
|
12
12
|
import { getOrigin } from "./google-oauth.js";
|
|
13
|
-
import { createThread, getThread, listThreads, searchThreads, updateThreadData, deleteThread, } from "../chat-threads/store.js";
|
|
13
|
+
import { createThread, getThread, listThreads, searchThreads, updateThreadData, withThreadDataLock, deleteThread, setThreadQueuedMessages, } from "../chat-threads/store.js";
|
|
14
14
|
import { resourceListAccessible, resourceList, resourceGet, resourceGetByPath, ensurePersonalDefaults, SHARED_OWNER, } from "../resources/store.js";
|
|
15
15
|
import nodePath from "node:path";
|
|
16
16
|
import { readBody } from "./h3-helpers.js";
|
|
@@ -907,6 +907,8 @@ Resources can be personal (per-user) or shared (team-wide). By default, resource
|
|
|
907
907
|
|
|
908
908
|
When the user gives instructions that should apply to all users/sessions, update the shared "AGENTS.md" resource.
|
|
909
909
|
|
|
910
|
+
**Resources are NOT an agent scratchpad.** Never use \`resource-write\` to store executable scripts, task plans, retry notes, or work-in-progress files you're writing to yourself. Specifically, do NOT create resources under \`scripts/\` or \`tasks/\` unless the user explicitly asked for a file at that path, or a tool (like \`create-job\` or \`spawn-task\`) writes there as part of its contract. If you can't complete a task with the tools you have, say so — don't improvise by leaving behind \`FINAL-*.md\`, \`EXECUTE-NOW-*.js\`, or similar artifacts. Resources are visible to the user in the workspace sidebar; every file you write is something they'll see and have to clean up.
|
|
911
|
+
|
|
910
912
|
### Navigation Rule
|
|
911
913
|
|
|
912
914
|
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.
|
|
@@ -1357,6 +1359,22 @@ export function createAgentChatPlugin(options) {
|
|
|
1357
1359
|
// this, requests can race ahead of the bootstrap and hit the SSR catch-all.
|
|
1358
1360
|
const { awaitBootstrap } = await import("./framework-request-handler.js");
|
|
1359
1361
|
await awaitBootstrap(nitroApp);
|
|
1362
|
+
// Reap phantom runs left over from the previous process (HMR restart,
|
|
1363
|
+
// process crash, isolate eviction). Any run whose heartbeat is already
|
|
1364
|
+
// stale by startup time had a dead producer; mark it errored so the
|
|
1365
|
+
// next /runs/active check returns a terminal status and reconnecting
|
|
1366
|
+
// clients don't spin on "Thinking...". Runs owned by OTHER live
|
|
1367
|
+
// isolates are protected by their fresh heartbeats.
|
|
1368
|
+
try {
|
|
1369
|
+
const { reapAllStaleRuns } = await import("../agent/run-store.js");
|
|
1370
|
+
const reaped = await reapAllStaleRuns();
|
|
1371
|
+
if (reaped > 0) {
|
|
1372
|
+
console.log(`[agent-chat] reaped ${reaped} stale run(s) on startup`);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
catch {
|
|
1376
|
+
// Best effort — don't block plugin init if SQL isn't ready yet.
|
|
1377
|
+
}
|
|
1360
1378
|
const env = process.env.NODE_ENV;
|
|
1361
1379
|
// AGENT_MODE=production forces production agent constraints even in dev
|
|
1362
1380
|
const canToggle = (env === "development" || env === "test") &&
|
|
@@ -1385,22 +1403,27 @@ export function createAgentChatPlugin(options) {
|
|
|
1385
1403
|
// `canToggle` means "this environment allows toggling" (static); this
|
|
1386
1404
|
// function means "the user currently has dev mode ON" (live).
|
|
1387
1405
|
const isDevMode = () => currentDevMode;
|
|
1388
|
-
// Initialize MCP client
|
|
1389
|
-
//
|
|
1390
|
-
//
|
|
1391
|
-
|
|
1406
|
+
// Initialize MCP client. Merges file/env config + auto-detected binaries
|
|
1407
|
+
// + any remote servers users have added through the settings UI (persisted
|
|
1408
|
+
// in the settings table, scanned across all scopes so we never drop
|
|
1409
|
+
// another user's entries). Graceful-degrade: any failure yields zero MCP
|
|
1410
|
+
// tools and agent-chat keeps working as before.
|
|
1411
|
+
let mcpConfig = await buildMergedConfig().catch((err) => {
|
|
1412
|
+
console.warn(`[mcp-client] buildMergedConfig failed: ${err?.message ?? err}`);
|
|
1413
|
+
return null;
|
|
1414
|
+
});
|
|
1392
1415
|
if (!mcpConfig) {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
console.log(`[mcp-client]
|
|
1416
|
+
const fileOrEnv = loadMcpConfig() ?? autoDetectMcpConfig();
|
|
1417
|
+
mcpConfig = fileOrEnv;
|
|
1418
|
+
if (mcpConfig?.source) {
|
|
1419
|
+
console.log(`[mcp-client] loaded config from ${mcpConfig.source} (${Object.keys(mcpConfig.servers).length} server(s))`);
|
|
1397
1420
|
}
|
|
1398
1421
|
else {
|
|
1399
|
-
console.log("[mcp-client] no
|
|
1422
|
+
console.log("[mcp-client] no configured MCP servers — skipping MCP tools");
|
|
1400
1423
|
}
|
|
1401
1424
|
}
|
|
1402
1425
|
else if (mcpConfig.source) {
|
|
1403
|
-
console.log(`[mcp-client]
|
|
1426
|
+
console.log(`[mcp-client] merged config (${Object.keys(mcpConfig.servers).length} server(s), source: ${mcpConfig.source})`);
|
|
1404
1427
|
}
|
|
1405
1428
|
const mcpManager = new McpClientManager(mcpConfig);
|
|
1406
1429
|
try {
|
|
@@ -1411,8 +1434,22 @@ export function createAgentChatPlugin(options) {
|
|
|
1411
1434
|
}
|
|
1412
1435
|
setGlobalMcpManager(mcpManager);
|
|
1413
1436
|
const mcpActionEntries = mcpToolsToActionEntries(mcpManager);
|
|
1414
|
-
// Mount status
|
|
1437
|
+
// Mount status + management routes so the settings UI can list / add /
|
|
1438
|
+
// remove remote MCP servers and hot-reload the running manager.
|
|
1415
1439
|
mountMcpStatusRoute(nitroApp, mcpManager);
|
|
1440
|
+
mountMcpServersRoutes(nitroApp, mcpManager);
|
|
1441
|
+
// Hub-serve: expose org-scope servers to other agent-native apps in the
|
|
1442
|
+
// workspace when `AGENT_NATIVE_MCP_HUB_TOKEN` is set (dispatch, by
|
|
1443
|
+
// convention). Gated by the env var so mounting is a no-op otherwise.
|
|
1444
|
+
if (isHubServeEnabled()) {
|
|
1445
|
+
mountMcpHubRoutes(nitroApp);
|
|
1446
|
+
console.log("[mcp-client] hub serve enabled — other apps can pull org servers via /_agent-native/mcp/hub/servers");
|
|
1447
|
+
}
|
|
1448
|
+
const hubStatus = getHubStatus();
|
|
1449
|
+
if (hubStatus.consuming) {
|
|
1450
|
+
console.log(`[mcp-client] hub consume enabled — pulling from ${hubStatus.hubUrl}`);
|
|
1451
|
+
}
|
|
1452
|
+
mountMcpHubStatusRoute(nitroApp);
|
|
1416
1453
|
// Ensure we tear down child processes if the host shuts down cleanly.
|
|
1417
1454
|
if (typeof process !== "undefined" &&
|
|
1418
1455
|
typeof process.once === "function" &&
|
|
@@ -1872,53 +1909,71 @@ export function createAgentChatPlugin(options) {
|
|
|
1872
1909
|
const onRunComplete = async (run, threadId) => {
|
|
1873
1910
|
if (!threadId)
|
|
1874
1911
|
return;
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
// No content produced — just bump timestamp
|
|
1882
|
-
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
1883
|
-
return;
|
|
1884
|
-
}
|
|
1885
|
-
// Parse existing thread_data, append assistant message only if
|
|
1886
|
-
// the frontend hasn't already saved it (avoids duplicates when
|
|
1887
|
-
// the client is still connected during a normal flow).
|
|
1888
|
-
let repo;
|
|
1912
|
+
// Serialize the read-modify-write against the same thread's other
|
|
1913
|
+
// `thread_data` writers (setThreadQueuedMessages, setThreadEngineMeta,
|
|
1914
|
+
// the frontend-triggered saves below). Without the lock, a concurrent
|
|
1915
|
+
// queued-message save can clobber the assistant message we just
|
|
1916
|
+
// appended here, or vice versa.
|
|
1917
|
+
await withThreadDataLock(threadId, async () => {
|
|
1889
1918
|
try {
|
|
1890
|
-
|
|
1919
|
+
const thread = await getThread(threadId);
|
|
1920
|
+
if (!thread)
|
|
1921
|
+
return;
|
|
1922
|
+
const assistantMsg = buildAssistantMessage(run.events ?? [], run.runId);
|
|
1923
|
+
if (!assistantMsg) {
|
|
1924
|
+
// No content produced — just bump timestamp
|
|
1925
|
+
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
// Parse existing thread_data, append assistant message only if
|
|
1929
|
+
// the frontend hasn't already saved it (avoids duplicates when
|
|
1930
|
+
// the client is still connected during a normal flow).
|
|
1931
|
+
let repo;
|
|
1932
|
+
try {
|
|
1933
|
+
repo = JSON.parse(thread.threadData || "{}");
|
|
1934
|
+
}
|
|
1935
|
+
catch {
|
|
1936
|
+
repo = {};
|
|
1937
|
+
}
|
|
1938
|
+
if (!Array.isArray(repo.messages))
|
|
1939
|
+
repo.messages = [];
|
|
1940
|
+
const lastMsg = repo.messages[repo.messages.length - 1];
|
|
1941
|
+
// Check both wrapped ({ message: { role } }) and unwrapped ({ role }) formats
|
|
1942
|
+
const lastRole = lastMsg?.message?.role ?? lastMsg?.role;
|
|
1943
|
+
const lastContent = lastMsg?.message?.content ?? lastMsg?.content;
|
|
1944
|
+
const lastContentIsEmpty = Array.isArray(lastContent)
|
|
1945
|
+
? lastContent.length === 0
|
|
1946
|
+
: lastContent == null || lastContent === "";
|
|
1947
|
+
if (lastRole === "assistant" && !lastContentIsEmpty) {
|
|
1948
|
+
// Frontend already saved the assistant response — just bump timestamp
|
|
1949
|
+
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
if (lastRole === "assistant" && lastContentIsEmpty) {
|
|
1953
|
+
// The frontend wrote an empty assistant placeholder before the stream
|
|
1954
|
+
// had any content (common when the user reloads mid-run, and the 5s
|
|
1955
|
+
// periodic save raced with the first text chunk). Replace it with
|
|
1956
|
+
// the server's reconstructed message so the turn isn't lost.
|
|
1957
|
+
repo.messages.pop();
|
|
1958
|
+
}
|
|
1959
|
+
// Determine if repo uses wrapped format ({ message, parentId }) or flat format
|
|
1960
|
+
const isWrapped = lastMsg && "message" in lastMsg;
|
|
1961
|
+
if (isWrapped) {
|
|
1962
|
+
const parentId = repo.messages.length > 0
|
|
1963
|
+
? (repo.messages[repo.messages.length - 1].message?.id ?? null)
|
|
1964
|
+
: null;
|
|
1965
|
+
repo.messages.push({ message: assistantMsg, parentId });
|
|
1966
|
+
}
|
|
1967
|
+
else {
|
|
1968
|
+
repo.messages.push(assistantMsg);
|
|
1969
|
+
}
|
|
1970
|
+
const meta = extractThreadMeta(repo);
|
|
1971
|
+
await updateThreadData(threadId, JSON.stringify(repo), meta.title || thread.title, meta.preview || thread.preview, repo.messages.length);
|
|
1891
1972
|
}
|
|
1892
1973
|
catch {
|
|
1893
|
-
|
|
1894
|
-
}
|
|
1895
|
-
if (!Array.isArray(repo.messages))
|
|
1896
|
-
repo.messages = [];
|
|
1897
|
-
const lastMsg = repo.messages[repo.messages.length - 1];
|
|
1898
|
-
// Check both wrapped ({ message: { role } }) and unwrapped ({ role }) formats
|
|
1899
|
-
const lastRole = lastMsg?.message?.role ?? lastMsg?.role;
|
|
1900
|
-
if (lastRole === "assistant") {
|
|
1901
|
-
// Frontend already saved the assistant response — just bump timestamp
|
|
1902
|
-
await updateThreadData(threadId, thread.threadData, thread.title, thread.preview, thread.messageCount);
|
|
1903
|
-
return;
|
|
1904
|
-
}
|
|
1905
|
-
// Determine if repo uses wrapped format ({ message, parentId }) or flat format
|
|
1906
|
-
const isWrapped = lastMsg && "message" in lastMsg;
|
|
1907
|
-
if (isWrapped) {
|
|
1908
|
-
const parentId = repo.messages.length > 0
|
|
1909
|
-
? (repo.messages[repo.messages.length - 1].message?.id ?? null)
|
|
1910
|
-
: null;
|
|
1911
|
-
repo.messages.push({ message: assistantMsg, parentId });
|
|
1912
|
-
}
|
|
1913
|
-
else {
|
|
1914
|
-
repo.messages.push(assistantMsg);
|
|
1974
|
+
// Best-effort — don't break cleanup
|
|
1915
1975
|
}
|
|
1916
|
-
|
|
1917
|
-
await updateThreadData(threadId, JSON.stringify(repo), meta.title || thread.title, meta.preview || thread.preview, repo.messages.length);
|
|
1918
|
-
}
|
|
1919
|
-
catch {
|
|
1920
|
-
// Best-effort — don't break cleanup
|
|
1921
|
-
}
|
|
1976
|
+
});
|
|
1922
1977
|
};
|
|
1923
1978
|
// ─── Agent Teams: per-run send reference ─────────────────────────
|
|
1924
1979
|
// Team tools need to emit events to the parent chat's SSE stream.
|
|
@@ -1991,6 +2046,13 @@ export function createAgentChatPlugin(options) {
|
|
|
1991
2046
|
...browserTools,
|
|
1992
2047
|
...mcpActionEntries,
|
|
1993
2048
|
};
|
|
2049
|
+
// Keep the prod action dict's MCP entries in sync when the manager's
|
|
2050
|
+
// server set changes at runtime (e.g. a user adds a remote MCP server
|
|
2051
|
+
// through the settings UI). getEngineTools() in production-agent re-reads
|
|
2052
|
+
// the registry per request, so updates here propagate without restart.
|
|
2053
|
+
mcpManager.onChange(() => {
|
|
2054
|
+
syncMcpActionEntries(mcpManager, prodActions);
|
|
2055
|
+
});
|
|
1994
2056
|
// Always build the production handler (includes resource tools + call-agent + team tools)
|
|
1995
2057
|
// In production mode (!canToggle), enable usage tracking and limits
|
|
1996
2058
|
const isHostedProd = !canToggle;
|
|
@@ -2006,6 +2068,11 @@ export function createAgentChatPlugin(options) {
|
|
|
2006
2068
|
return "";
|
|
2007
2069
|
}
|
|
2008
2070
|
};
|
|
2071
|
+
const leanPrompt = options?.leanPrompt === true;
|
|
2072
|
+
// Lean mode: use only the template's systemPrompt + actions list.
|
|
2073
|
+
// Skip resource loading, schema block, and extraContext — those add
|
|
2074
|
+
// DB round-trips and tokens that minimal/voice apps don't need.
|
|
2075
|
+
const leanBasePrompt = (options?.systemPrompt ?? "") + prodActionsPrompt;
|
|
2009
2076
|
const prodHandler = createProductionAgentHandler({
|
|
2010
2077
|
actions: prodActions,
|
|
2011
2078
|
systemPrompt: async (event) => {
|
|
@@ -2014,6 +2081,10 @@ export function createAgentChatPlugin(options) {
|
|
|
2014
2081
|
_currentRunOwner = owner;
|
|
2015
2082
|
const { getOwnerAnthropicApiKey } = await import("../agent/production-agent.js");
|
|
2016
2083
|
_currentRunUserApiKey = await getOwnerAnthropicApiKey(owner);
|
|
2084
|
+
if (leanPrompt) {
|
|
2085
|
+
_currentRunSystemPrompt = leanBasePrompt;
|
|
2086
|
+
return _currentRunSystemPrompt;
|
|
2087
|
+
}
|
|
2017
2088
|
const resources = await loadResourcesForPrompt(owner);
|
|
2018
2089
|
const schemaBlock = await buildSchemaBlock(owner, false);
|
|
2019
2090
|
const extra = await resolveExtraContext(event, owner);
|
|
@@ -2023,6 +2094,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2023
2094
|
model: options?.model ??
|
|
2024
2095
|
(isHostedProd ? "claude-haiku-4-5-20251001" : undefined),
|
|
2025
2096
|
apiKey: options?.apiKey,
|
|
2097
|
+
skipFilesContext: leanPrompt,
|
|
2026
2098
|
onRunStart: (send, threadId) => {
|
|
2027
2099
|
_runSendByThread.set(threadId, send);
|
|
2028
2100
|
_currentRunThreadId = threadId;
|
|
@@ -2046,17 +2118,30 @@ export function createAgentChatPlugin(options) {
|
|
|
2046
2118
|
// how Claude Code works locally and dramatically reduces the rate of
|
|
2047
2119
|
// degenerate empty-object tool calls. The CLI syntax for each action is
|
|
2048
2120
|
// listed in the dev system prompt's "Available Actions" section.
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2121
|
+
// In lean mode, expose the template's actions directly as native tools
|
|
2122
|
+
// instead of routing through shell — the lean system prompt has no
|
|
2123
|
+
// shell-usage guidance, so shell-based action invocation would break.
|
|
2124
|
+
const devActions = leanPrompt
|
|
2125
|
+
? prodActions
|
|
2126
|
+
: {
|
|
2127
|
+
...resourceScripts,
|
|
2128
|
+
...docsScripts,
|
|
2129
|
+
...chatScripts,
|
|
2130
|
+
...callAgentScript,
|
|
2131
|
+
...teamTools,
|
|
2132
|
+
...jobTools,
|
|
2133
|
+
...browserTools,
|
|
2134
|
+
...mcpActionEntries,
|
|
2135
|
+
...(await createDevScriptRegistry()),
|
|
2136
|
+
};
|
|
2137
|
+
// Keep dev action dict in sync with runtime MCP additions. When
|
|
2138
|
+
// leanPrompt is true, devActions === prodActions so the prod listener
|
|
2139
|
+
// already covers it.
|
|
2140
|
+
if (devActions !== prodActions) {
|
|
2141
|
+
mcpManager.onChange(() => {
|
|
2142
|
+
syncMcpActionEntries(mcpManager, devActions);
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2060
2145
|
devHandler = createProductionAgentHandler({
|
|
2061
2146
|
actions: devActions,
|
|
2062
2147
|
systemPrompt: async (event) => {
|
|
@@ -2065,6 +2150,10 @@ export function createAgentChatPlugin(options) {
|
|
|
2065
2150
|
_currentRunOwner = owner;
|
|
2066
2151
|
const { getOwnerAnthropicApiKey } = await import("../agent/production-agent.js");
|
|
2067
2152
|
_currentRunUserApiKey = await getOwnerAnthropicApiKey(owner);
|
|
2153
|
+
if (leanPrompt) {
|
|
2154
|
+
_currentRunSystemPrompt = leanBasePrompt;
|
|
2155
|
+
return _currentRunSystemPrompt;
|
|
2156
|
+
}
|
|
2068
2157
|
const resources = await loadResourcesForPrompt(owner);
|
|
2069
2158
|
const schemaBlock = await buildSchemaBlock(owner, true);
|
|
2070
2159
|
const extra = await resolveExtraContext(event, owner);
|
|
@@ -2073,6 +2162,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2073
2162
|
},
|
|
2074
2163
|
model: options?.model,
|
|
2075
2164
|
apiKey: options?.apiKey,
|
|
2165
|
+
skipFilesContext: leanPrompt,
|
|
2076
2166
|
onRunStart: (send, threadId) => {
|
|
2077
2167
|
_runSendByThread.set(threadId, send);
|
|
2078
2168
|
_currentRunThreadId = threadId;
|
|
@@ -2624,6 +2714,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2624
2714
|
runId: run.runId,
|
|
2625
2715
|
threadId: run.threadId,
|
|
2626
2716
|
status: run.status,
|
|
2717
|
+
heartbeatAt: run.heartbeatAt,
|
|
2627
2718
|
};
|
|
2628
2719
|
}
|
|
2629
2720
|
setResponseStatus(event, 405);
|
|
@@ -2657,13 +2748,60 @@ export function createAgentChatPlugin(options) {
|
|
|
2657
2748
|
return thread;
|
|
2658
2749
|
}
|
|
2659
2750
|
if (method === "PUT") {
|
|
2751
|
+
// Hold the thread_data lock for the full read-modify-write so
|
|
2752
|
+
// periodic saves from the frontend don't race with
|
|
2753
|
+
// onRunComplete / setThreadQueuedMessages / setThreadEngineMeta.
|
|
2754
|
+
// Without the lock, a client save that lands during an agent
|
|
2755
|
+
// run could clobber the assistant message the server just
|
|
2756
|
+
// appended (and vice versa).
|
|
2757
|
+
return await withThreadDataLock(threadId, async () => {
|
|
2758
|
+
const thread = await getThread(threadId);
|
|
2759
|
+
if (!thread || thread.ownerEmail !== owner) {
|
|
2760
|
+
setResponseStatus(event, 404);
|
|
2761
|
+
return { error: "Thread not found" };
|
|
2762
|
+
}
|
|
2763
|
+
const body = await readBody(event);
|
|
2764
|
+
let newThreadData = body.threadData || thread.threadData;
|
|
2765
|
+
// Preserve queuedMessages from the existing thread_data when the
|
|
2766
|
+
// incoming blob doesn't include it. Periodic full-thread saves
|
|
2767
|
+
// (exported via threadRuntime.export) don't carry the queue, and
|
|
2768
|
+
// we don't want them to clobber queued-message state persisted
|
|
2769
|
+
// via POST /threads/:id/queued.
|
|
2770
|
+
if (body.threadData) {
|
|
2771
|
+
try {
|
|
2772
|
+
const existing = JSON.parse(thread.threadData);
|
|
2773
|
+
if (existing.queuedMessages !== undefined) {
|
|
2774
|
+
const incoming = JSON.parse(newThreadData);
|
|
2775
|
+
if (incoming.queuedMessages === undefined) {
|
|
2776
|
+
incoming.queuedMessages = existing.queuedMessages;
|
|
2777
|
+
newThreadData = JSON.stringify(incoming);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
catch {
|
|
2782
|
+
// Invalid JSON in either side — fall back to raw body blob.
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
await updateThreadData(threadId, newThreadData, body.title ?? thread.title, body.preview ?? thread.preview, body.messageCount || thread.messageCount);
|
|
2786
|
+
return { ok: true };
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
// POST /threads/:id/queued — debounced writes from the client
|
|
2790
|
+
// when the user adds/removes/dequeues a queued message. Keeps
|
|
2791
|
+
// queued messages durable across reloads without piggybacking
|
|
2792
|
+
// on full-thread saves.
|
|
2793
|
+
if (method === "POST" &&
|
|
2794
|
+
/\/threads\/[^/?]+\/queued/.test(event.node?.req?.url || event.path || "")) {
|
|
2660
2795
|
const thread = await getThread(threadId);
|
|
2661
2796
|
if (!thread || thread.ownerEmail !== owner) {
|
|
2662
2797
|
setResponseStatus(event, 404);
|
|
2663
2798
|
return { error: "Thread not found" };
|
|
2664
2799
|
}
|
|
2665
2800
|
const body = await readBody(event);
|
|
2666
|
-
|
|
2801
|
+
const queued = Array.isArray(body?.queuedMessages)
|
|
2802
|
+
? body.queuedMessages
|
|
2803
|
+
: [];
|
|
2804
|
+
await setThreadQueuedMessages(threadId, queued);
|
|
2667
2805
|
return { ok: true };
|
|
2668
2806
|
}
|
|
2669
2807
|
if (method === "DELETE") {
|
|
@@ -2739,7 +2877,18 @@ export function createAgentChatPlugin(options) {
|
|
|
2739
2877
|
else {
|
|
2740
2878
|
delete process.env.AGENT_ORG_ID;
|
|
2741
2879
|
}
|
|
2742
|
-
|
|
2880
|
+
// Propagate the caller's IANA timezone from `x-user-timezone` so that
|
|
2881
|
+
// tool calls made by the agent (e.g. log-meal with no explicit date)
|
|
2882
|
+
// resolve "today" in the user's local timezone instead of server UTC.
|
|
2883
|
+
const tzRaw = getHeader(event, "x-user-timezone");
|
|
2884
|
+
const timezone = typeof tzRaw === "string" &&
|
|
2885
|
+
tzRaw.trim().length > 0 &&
|
|
2886
|
+
tzRaw.trim().length < 64
|
|
2887
|
+
? tzRaw.trim()
|
|
2888
|
+
: undefined;
|
|
2889
|
+
if (timezone)
|
|
2890
|
+
process.env.AGENT_USER_TIMEZONE = timezone;
|
|
2891
|
+
return runWithRequestContext({ userEmail: owner, orgId: resolvedOrgId, timezone }, () => {
|
|
2743
2892
|
const handler = currentDevMode && devHandler ? devHandler : prodHandler;
|
|
2744
2893
|
return handler(event);
|
|
2745
2894
|
});
|
|
@@ -2800,6 +2949,24 @@ function setGlobalMcpManager(manager) {
|
|
|
2800
2949
|
export function getGlobalMcpManager() {
|
|
2801
2950
|
return _globalMcpManager;
|
|
2802
2951
|
}
|
|
2952
|
+
function mountMcpHubStatusRoute(nitroApp) {
|
|
2953
|
+
if (globalThis.__agentNativeMcpHubStatusMounted)
|
|
2954
|
+
return;
|
|
2955
|
+
globalThis.__agentNativeMcpHubStatusMounted = true;
|
|
2956
|
+
try {
|
|
2957
|
+
getH3App(nitroApp).use("/_agent-native/mcp/hub/status", defineEventHandler(async (event) => {
|
|
2958
|
+
if (getMethod(event) !== "GET") {
|
|
2959
|
+
setResponseStatus(event, 405);
|
|
2960
|
+
return { error: "Method not allowed" };
|
|
2961
|
+
}
|
|
2962
|
+
setResponseHeader(event, "Content-Type", "application/json");
|
|
2963
|
+
return getHubStatus();
|
|
2964
|
+
}));
|
|
2965
|
+
}
|
|
2966
|
+
catch (err) {
|
|
2967
|
+
console.warn(`[mcp-client] Failed to mount /_agent-native/mcp/hub/status: ${err?.message ?? err}`);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2803
2970
|
function mountMcpStatusRoute(nitroApp, manager) {
|
|
2804
2971
|
// Idempotent — agent-chat-plugin can be invoked once per process; guard anyway.
|
|
2805
2972
|
if (globalThis.__agentNativeMcpStatusMounted)
|