stagent 0.5.0 → 0.6.1
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 +8 -8
- package/dist/cli.js +146 -2
- package/docs/.coverage-gaps.json +21 -0
- package/docs/.last-generated +1 -1
- package/docs/features/agent-intelligence.md +36 -14
- package/docs/features/chat.md +33 -56
- package/docs/features/cost-usage.md +14 -10
- package/docs/features/dashboard-kanban.md +30 -13
- package/docs/features/delivery-channels.md +198 -0
- package/docs/features/design-system.md +10 -10
- package/docs/features/documents.md +8 -8
- package/docs/features/home-workspace.md +20 -15
- package/docs/features/inbox-notifications.md +22 -10
- package/docs/features/keyboard-navigation.md +11 -11
- package/docs/features/monitoring.md +1 -1
- package/docs/features/playbook.md +30 -32
- package/docs/features/profiles.md +33 -11
- package/docs/features/projects.md +2 -2
- package/docs/features/provider-runtimes.md +58 -14
- package/docs/features/schedules.md +70 -40
- package/docs/features/settings.md +74 -46
- package/docs/features/shared-components.md +7 -15
- package/docs/features/tool-permissions.md +9 -9
- package/docs/features/workflows.md +32 -21
- package/docs/getting-started.md +33 -9
- package/docs/index.md +25 -16
- package/docs/journeys/developer.md +124 -207
- package/docs/journeys/personal-use.md +70 -79
- package/docs/journeys/power-user.md +107 -151
- package/docs/journeys/work-use.md +81 -113
- package/docs/manifest.json +77 -45
- package/docs/superpowers/plans/2026-03-30-finish-in-progress-features.md +547 -0
- package/docs/use-cases/agency-operator.md +84 -0
- package/docs/use-cases/solo-founder.md +75 -0
- package/docs/why-stagent.md +59 -0
- package/package.json +10 -3
- package/src/app/api/channels/[id]/route.ts +104 -0
- package/src/app/api/channels/[id]/test/route.ts +52 -0
- package/src/app/api/channels/inbound/slack/route.ts +116 -0
- package/src/app/api/channels/inbound/telegram/poll/route.ts +140 -0
- package/src/app/api/channels/inbound/telegram/route.ts +87 -0
- package/src/app/api/channels/route.ts +72 -0
- package/src/app/api/chat/conversations/route.ts +15 -0
- package/src/app/api/chat/entities/search/route.ts +46 -31
- package/src/app/api/data/clear/route.ts +4 -0
- package/src/app/api/data/seed/route.ts +4 -0
- package/src/app/api/documents/route.ts +36 -6
- package/src/app/api/environment/profiles/suggest/route.ts +19 -3
- package/src/app/api/environment/scan/route.ts +8 -1
- package/src/app/api/handoffs/[id]/route.ts +76 -0
- package/src/app/api/handoffs/route.ts +89 -0
- package/src/app/api/memory/route.ts +181 -0
- package/src/app/api/profiles/[id]/route.ts +16 -1
- package/src/app/api/profiles/[id]/test/route.ts +4 -0
- package/src/app/api/profiles/[id]/test-results/route.ts +22 -0
- package/src/app/api/profiles/[id]/test-single/route.ts +64 -0
- package/src/app/api/profiles/assist/route.ts +35 -0
- package/src/app/api/profiles/import-repo/apply-updates/route.ts +123 -0
- package/src/app/api/profiles/import-repo/check-updates/route.ts +163 -0
- package/src/app/api/profiles/import-repo/confirm/route.ts +118 -0
- package/src/app/api/profiles/import-repo/preview/route.ts +107 -0
- package/src/app/api/profiles/import-repo/route.ts +29 -0
- package/src/app/api/profiles/import-repo/scan/route.ts +25 -0
- package/src/app/api/profiles/route.ts +73 -22
- package/src/app/api/runtimes/ollama/route.ts +86 -0
- package/src/app/api/runtimes/suggest/route.ts +29 -0
- package/src/app/api/schedules/[id]/heartbeat-history/route.ts +77 -0
- package/src/app/api/schedules/[id]/route.ts +41 -3
- package/src/app/api/schedules/parse/route.ts +66 -0
- package/src/app/api/schedules/route.ts +71 -12
- package/src/app/api/settings/author-default/route.ts +7 -0
- package/src/app/api/settings/learning/route.ts +41 -0
- package/src/app/api/settings/ollama/route.ts +34 -0
- package/src/app/api/settings/providers/route.ts +57 -0
- package/src/app/api/settings/routing/route.ts +24 -0
- package/src/app/api/settings/web-search/route.ts +28 -0
- package/src/app/api/tasks/[id]/execute/route.ts +13 -1
- package/src/app/api/tasks/[id]/respond/route.ts +23 -1
- package/src/app/documents/page.tsx +3 -0
- package/src/app/environment/page.tsx +8 -1
- package/src/app/settings/page.tsx +10 -4
- package/src/app/workflows/[id]/edit/page.tsx +2 -0
- package/src/app/workflows/new/page.tsx +2 -0
- package/src/components/chat/chat-command-popover.tsx +22 -19
- package/src/components/chat/chat-input.tsx +5 -0
- package/src/components/chat/chat-model-selector.tsx +42 -1
- package/src/components/chat/chat-shell.tsx +2 -0
- package/src/components/dashboard/welcome-landing.tsx +9 -9
- package/src/components/environment/artifact-card.tsx +27 -1
- package/src/components/environment/environment-dashboard.tsx +50 -2
- package/src/components/environment/environment-summary-card.tsx +5 -2
- package/src/components/environment/suggested-profiles.tsx +117 -52
- package/src/components/handoffs/handoff-approval-card.tsx +159 -0
- package/src/components/memory/memory-browser.tsx +315 -0
- package/src/components/profiles/learned-context-panel.tsx +4 -4
- package/src/components/profiles/profile-assist-panel.tsx +512 -0
- package/src/components/profiles/profile-browser.tsx +109 -8
- package/src/components/profiles/profile-card.tsx +29 -1
- package/src/components/profiles/profile-detail-view.tsx +200 -28
- package/src/components/profiles/profile-form-view.tsx +220 -82
- package/src/components/profiles/repo-import-wizard.tsx +648 -0
- package/src/components/profiles/smoke-test-editor.tsx +106 -0
- package/src/components/schedules/schedule-create-sheet.tsx +9 -1
- package/src/components/schedules/schedule-form.tsx +348 -9
- package/src/components/schedules/schedule-list.tsx +15 -2
- package/src/components/settings/auth-method-selector.tsx +7 -1
- package/src/components/settings/budget-guardrails-section.tsx +111 -48
- package/src/components/settings/channels-section.tsx +526 -0
- package/src/components/settings/chat-settings-section.tsx +27 -1
- package/src/components/settings/data-management-section.tsx +8 -6
- package/src/components/settings/learning-context-section.tsx +124 -0
- package/src/components/settings/ollama-section.tsx +270 -0
- package/src/components/settings/providers-runtimes-section.tsx +499 -0
- package/src/components/settings/web-search-section.tsx +101 -0
- package/src/components/shared/tag-input.tsx +156 -0
- package/src/components/tasks/kanban-board.tsx +32 -0
- package/src/components/tasks/kanban-column.tsx +4 -2
- package/src/components/tasks/task-card.tsx +1 -0
- package/src/components/tasks/task-chip-bar.tsx +6 -1
- package/src/components/tasks/task-create-panel.tsx +55 -5
- package/src/components/workflows/workflow-form-view.tsx +38 -3
- package/src/hooks/use-chat-autocomplete.ts +24 -26
- package/src/hooks/use-project-skills.ts +66 -0
- package/src/hooks/use-tag-suggestions.ts +31 -0
- package/src/instrumentation.ts +4 -1
- package/src/lib/agents/__tests__/claude-agent.test.ts +3 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +10 -0
- package/src/lib/agents/agentic-loop.ts +235 -0
- package/src/lib/agents/browser-mcp.ts +59 -4
- package/src/lib/agents/claude-agent.ts +27 -200
- package/src/lib/agents/handoff/bus.ts +164 -0
- package/src/lib/agents/handoff/governance.ts +47 -0
- package/src/lib/agents/handoff/types.ts +16 -0
- package/src/lib/agents/learned-context.ts +27 -7
- package/src/lib/agents/memory/decay.ts +61 -0
- package/src/lib/agents/memory/extractor.ts +181 -0
- package/src/lib/agents/memory/retrieval.ts +96 -0
- package/src/lib/agents/memory/types.ts +6 -0
- package/src/lib/agents/profiles/__tests__/project-profiles.test.ts +119 -0
- package/src/lib/agents/profiles/__tests__/registry.test.ts +11 -3
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/content-creator/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/content-creator/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/customer-support-agent/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/customer-support-agent/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/financial-analyst/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/financial-analyst/profile.yaml +24 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/marketing-strategist/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/marketing-strategist/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/operations-coordinator/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/operations-coordinator/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +1 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/sales-researcher/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/sales-researcher/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +1 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/sweep/profile.yaml +1 -1
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +2 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +2 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +2 -2
- package/src/lib/agents/profiles/project-profiles.ts +193 -0
- package/src/lib/agents/profiles/registry.ts +130 -6
- package/src/lib/agents/profiles/types.ts +28 -0
- package/src/lib/agents/router.ts +174 -2
- package/src/lib/agents/runtime/__tests__/catalog.test.ts +15 -4
- package/src/lib/agents/runtime/anthropic-direct.ts +644 -0
- package/src/lib/agents/runtime/catalog.ts +57 -2
- package/src/lib/agents/runtime/claude.ts +205 -1
- package/src/lib/agents/runtime/index.ts +22 -0
- package/src/lib/agents/runtime/ollama-adapter.ts +409 -0
- package/src/lib/agents/runtime/openai-direct.ts +514 -0
- package/src/lib/agents/runtime/profile-assist-types.ts +30 -0
- package/src/lib/agents/runtime/types.ts +2 -0
- package/src/lib/agents/tool-permissions.ts +203 -0
- package/src/lib/channels/gateway.ts +321 -0
- package/src/lib/channels/poller.ts +268 -0
- package/src/lib/channels/registry.ts +90 -0
- package/src/lib/channels/slack-adapter.ts +188 -0
- package/src/lib/channels/telegram-adapter.ts +218 -0
- package/src/lib/channels/types.ts +75 -0
- package/src/lib/channels/webhook-adapter.ts +74 -0
- package/src/lib/chat/context-builder.ts +22 -2
- package/src/lib/chat/engine.ts +95 -13
- package/src/lib/chat/ollama-engine.ts +198 -0
- package/src/lib/chat/stagent-tools.ts +106 -20
- package/src/lib/chat/tool-catalog.ts +24 -0
- package/src/lib/chat/tool-registry.ts +90 -0
- package/src/lib/chat/tools/chat-history-tools.ts +4 -4
- package/src/lib/chat/tools/document-tools.ts +7 -7
- package/src/lib/chat/tools/handoff-tools.ts +70 -0
- package/src/lib/chat/tools/notification-tools.ts +4 -4
- package/src/lib/chat/tools/profile-tools.ts +3 -3
- package/src/lib/chat/tools/project-tools.ts +3 -3
- package/src/lib/chat/tools/schedule-tools.ts +29 -13
- package/src/lib/chat/tools/settings-tools.ts +2 -2
- package/src/lib/chat/tools/task-tools.ts +66 -11
- package/src/lib/chat/tools/usage-tools.ts +2 -2
- package/src/lib/chat/tools/workflow-tools.ts +8 -8
- package/src/lib/chat/types.ts +11 -5
- package/src/lib/constants/known-tools.ts +19 -0
- package/src/lib/constants/prose-styles.ts +1 -1
- package/src/lib/constants/settings.ts +7 -0
- package/src/lib/data/channel-bindings.ts +85 -0
- package/src/lib/data/clear.ts +22 -0
- package/src/lib/data/profile-test-results.ts +48 -0
- package/src/lib/data/seed-data/conversations.ts +196 -0
- package/src/lib/data/seed-data/learned-context.ts +99 -0
- package/src/lib/data/seed-data/notifications.ts +54 -1
- package/src/lib/data/seed-data/profile-test-results.ts +96 -0
- package/src/lib/data/seed-data/repo-imports.ts +51 -0
- package/src/lib/data/seed-data/views.ts +60 -0
- package/src/lib/data/seed.ts +51 -0
- package/src/lib/db/bootstrap.ts +162 -0
- package/src/lib/db/migrations/0013_add_repo_imports.sql +15 -0
- package/src/lib/db/migrations/0014_add_linked_profile_id.sql +3 -0
- package/src/lib/db/migrations/0015_add_channel_bindings.sql +23 -0
- package/src/lib/db/schema.ts +190 -1
- package/src/lib/environment/__tests__/auto-scan.test.ts +86 -0
- package/src/lib/environment/__tests__/profile-linker.test.ts +187 -0
- package/src/lib/environment/auto-scan.ts +48 -0
- package/src/lib/environment/data.ts +25 -0
- package/src/lib/environment/profile-generator.ts +40 -10
- package/src/lib/environment/profile-linker.ts +143 -0
- package/src/lib/environment/profile-rules.ts +96 -0
- package/src/lib/import/dedup.ts +149 -0
- package/src/lib/import/format-adapter.ts +631 -0
- package/src/lib/import/github-api.ts +219 -0
- package/src/lib/import/repo-scanner.ts +251 -0
- package/src/lib/schedules/__tests__/nlp-parser.test.ts +330 -0
- package/src/lib/schedules/active-hours.ts +120 -0
- package/src/lib/schedules/heartbeat-parser.ts +224 -0
- package/src/lib/schedules/heartbeat-prompt.ts +153 -0
- package/src/lib/schedules/nlp-parser.ts +357 -0
- package/src/lib/schedules/scheduler.ts +218 -3
- package/src/lib/settings/__tests__/budget-guardrails.test.ts +39 -1
- package/src/lib/settings/helpers.ts +6 -0
- package/src/lib/settings/routing.ts +24 -0
- package/src/lib/settings/runtime-setup.ts +28 -1
- package/src/lib/usage/ledger.ts +2 -1
- package/src/lib/validators/__tests__/settings.test.ts +9 -0
- package/src/lib/validators/profile.ts +39 -0
- package/src/lib/workflows/blueprints/builtins/business-daily-briefing.yaml +102 -0
- package/src/lib/workflows/blueprints/builtins/content-marketing-pipeline.yaml +90 -0
- package/src/lib/workflows/blueprints/builtins/customer-support-triage.yaml +107 -0
- package/src/lib/workflows/blueprints/builtins/financial-reporting.yaml +104 -0
- package/src/lib/workflows/blueprints/builtins/lead-research-pipeline.yaml +82 -0
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Direct API runtime adapter.
|
|
3
|
+
*
|
|
4
|
+
* Calls the Anthropic Messages API directly via `@anthropic-ai/sdk`
|
|
5
|
+
* (no subprocess spawning). Supports streaming, tool use, session
|
|
6
|
+
* resume, and budget enforcement.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { db } from "@/lib/db";
|
|
10
|
+
import { tasks, agentLogs, notifications } from "@/lib/db/schema";
|
|
11
|
+
import { eq } from "drizzle-orm";
|
|
12
|
+
import { setExecution, removeExecution, getExecution } from "../execution-manager";
|
|
13
|
+
import { DEFAULT_MAX_TURNS, DEFAULT_MAX_BUDGET_USD } from "@/lib/constants/task-status";
|
|
14
|
+
import {
|
|
15
|
+
buildTaskQueryContext,
|
|
16
|
+
createTaskUsageState,
|
|
17
|
+
} from "../claude-agent";
|
|
18
|
+
import { createToolServer } from "@/lib/chat/stagent-tools";
|
|
19
|
+
import type { AnthropicToolDef } from "@/lib/chat/tool-registry";
|
|
20
|
+
import { handleToolPermission, clearPermissionCache } from "../tool-permissions";
|
|
21
|
+
import {
|
|
22
|
+
runAgenticLoop,
|
|
23
|
+
type LoopMessage,
|
|
24
|
+
type ModelTurnResult,
|
|
25
|
+
type TurnUsage,
|
|
26
|
+
type AgentStreamEvent,
|
|
27
|
+
} from "../agentic-loop";
|
|
28
|
+
import { getRuntimeCatalogEntry } from "./catalog";
|
|
29
|
+
import type {
|
|
30
|
+
AgentRuntimeAdapter,
|
|
31
|
+
RuntimeConnectionResult,
|
|
32
|
+
TaskAssistInput,
|
|
33
|
+
} from "./types";
|
|
34
|
+
import type { TaskAssistResponse } from "./task-assist-types";
|
|
35
|
+
import type { ProfileAssistRequest, ProfileAssistResponse } from "./profile-assist-types";
|
|
36
|
+
import type { ProfileTestReport } from "../profiles/test-types";
|
|
37
|
+
import { getProfile, listProfiles } from "../profiles/registry";
|
|
38
|
+
import {
|
|
39
|
+
recordUsageLedgerEntry,
|
|
40
|
+
resolveUsageActivityType,
|
|
41
|
+
} from "@/lib/usage/ledger";
|
|
42
|
+
|
|
43
|
+
// ── SDK lazy import ──────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
type AnthropicSDK = typeof import("@anthropic-ai/sdk");
|
|
46
|
+
let _sdk: AnthropicSDK | null = null;
|
|
47
|
+
|
|
48
|
+
async function getAnthropicSDK(): Promise<AnthropicSDK> {
|
|
49
|
+
if (!_sdk) {
|
|
50
|
+
_sdk = await import("@anthropic-ai/sdk");
|
|
51
|
+
}
|
|
52
|
+
return _sdk;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── API key resolution ───────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
async function getAnthropicApiKey(): Promise<string> {
|
|
58
|
+
// Try DB-stored key first
|
|
59
|
+
const { getSetting } = await import("@/lib/settings/helpers");
|
|
60
|
+
const { SETTINGS_KEYS } = await import("@/lib/constants/settings");
|
|
61
|
+
const { decrypt } = await import("@/lib/utils/crypto");
|
|
62
|
+
|
|
63
|
+
const encryptedKey = await getSetting(SETTINGS_KEYS.AUTH_API_KEY);
|
|
64
|
+
if (encryptedKey) {
|
|
65
|
+
try {
|
|
66
|
+
return decrypt(encryptedKey);
|
|
67
|
+
} catch {
|
|
68
|
+
// Fall through to env
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Fall back to env var
|
|
73
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
74
|
+
return process.env.ANTHROPIC_API_KEY;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new Error("No Anthropic API key configured. Set one in Settings > Authentication.");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Streaming model call ─────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/** Options for prompt caching and advanced capabilities. */
|
|
83
|
+
interface AnthropicCallOptions {
|
|
84
|
+
modelId?: string;
|
|
85
|
+
maxTokens?: number;
|
|
86
|
+
/** Enable prompt caching — splits system prompt into cacheable blocks. */
|
|
87
|
+
enableCaching?: boolean;
|
|
88
|
+
/** Profile instructions block (cached separately from base system prompt). */
|
|
89
|
+
profileInstructions?: string;
|
|
90
|
+
/** Extended thinking config (Anthropic only). */
|
|
91
|
+
extendedThinking?: { enabled: boolean; budgetTokens?: number };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build system content blocks with optional prompt caching.
|
|
96
|
+
*
|
|
97
|
+
* When caching is enabled, stable content (base prompt, profile instructions)
|
|
98
|
+
* gets `cache_control: { type: "ephemeral" }` so repeated calls reuse cached
|
|
99
|
+
* token processing at 90% lower cost.
|
|
100
|
+
*/
|
|
101
|
+
function buildSystemBlocks(
|
|
102
|
+
basePrompt: string,
|
|
103
|
+
profileInstructions: string | undefined,
|
|
104
|
+
enableCaching: boolean,
|
|
105
|
+
): unknown {
|
|
106
|
+
if (!enableCaching) return basePrompt;
|
|
107
|
+
|
|
108
|
+
const blocks: unknown[] = [];
|
|
109
|
+
|
|
110
|
+
if (profileInstructions) {
|
|
111
|
+
// Cache the base prompt separately from profile instructions
|
|
112
|
+
blocks.push({
|
|
113
|
+
type: "text",
|
|
114
|
+
text: basePrompt,
|
|
115
|
+
cache_control: { type: "ephemeral" },
|
|
116
|
+
});
|
|
117
|
+
blocks.push({
|
|
118
|
+
type: "text",
|
|
119
|
+
text: profileInstructions,
|
|
120
|
+
cache_control: { type: "ephemeral" },
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
// Single cached block
|
|
124
|
+
blocks.push({
|
|
125
|
+
type: "text",
|
|
126
|
+
text: basePrompt,
|
|
127
|
+
cache_control: { type: "ephemeral" },
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return blocks;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function callAnthropicModel(
|
|
135
|
+
client: InstanceType<AnthropicSDK["default"]>,
|
|
136
|
+
systemPrompt: string,
|
|
137
|
+
messages: LoopMessage[],
|
|
138
|
+
tools: AnthropicToolDef[],
|
|
139
|
+
signal: AbortSignal,
|
|
140
|
+
emitEvent: (event: AgentStreamEvent) => void,
|
|
141
|
+
options: AnthropicCallOptions = {},
|
|
142
|
+
): Promise<ModelTurnResult> {
|
|
143
|
+
const modelId = options.modelId ?? "claude-sonnet-4-20250514";
|
|
144
|
+
const maxTokens = options.maxTokens ?? 8192;
|
|
145
|
+
|
|
146
|
+
// Build system content with optional caching
|
|
147
|
+
const systemContent = buildSystemBlocks(
|
|
148
|
+
systemPrompt,
|
|
149
|
+
options.profileInstructions,
|
|
150
|
+
options.enableCaching ?? false,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Build request params
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
+
const params: any = {
|
|
156
|
+
model: modelId,
|
|
157
|
+
system: systemContent,
|
|
158
|
+
messages: messages as any,
|
|
159
|
+
tools: tools as any,
|
|
160
|
+
max_tokens: maxTokens,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Add extended thinking if enabled
|
|
164
|
+
if (options.extendedThinking?.enabled) {
|
|
165
|
+
params.thinking = {
|
|
166
|
+
type: "enabled",
|
|
167
|
+
budget_tokens: options.extendedThinking.budgetTokens ?? 10000,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const stream = client.messages.stream(params, { signal });
|
|
172
|
+
|
|
173
|
+
let text = "";
|
|
174
|
+
let thinkingText = "";
|
|
175
|
+
const toolCalls: ModelTurnResult["toolCalls"] = [];
|
|
176
|
+
let stopReason = "";
|
|
177
|
+
const usage: TurnUsage = { modelId };
|
|
178
|
+
|
|
179
|
+
emitEvent({ type: "status", phase: "running" });
|
|
180
|
+
|
|
181
|
+
const response = await stream.finalMessage();
|
|
182
|
+
|
|
183
|
+
// Extract usage (including cache metrics)
|
|
184
|
+
if (response.usage) {
|
|
185
|
+
usage.inputTokens = response.usage.input_tokens;
|
|
186
|
+
usage.outputTokens = response.usage.output_tokens;
|
|
187
|
+
usage.totalTokens = response.usage.input_tokens + response.usage.output_tokens;
|
|
188
|
+
// Cache metrics are available as cache_creation_input_tokens / cache_read_input_tokens
|
|
189
|
+
const usageAny = response.usage as unknown as Record<string, unknown>;
|
|
190
|
+
if (usageAny.cache_creation_input_tokens || usageAny.cache_read_input_tokens) {
|
|
191
|
+
(usage as Record<string, unknown>).cacheCreationTokens = usageAny.cache_creation_input_tokens ?? 0;
|
|
192
|
+
(usage as Record<string, unknown>).cacheReadTokens = usageAny.cache_read_input_tokens ?? 0;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
stopReason = response.stop_reason ?? "";
|
|
197
|
+
|
|
198
|
+
// Process content blocks
|
|
199
|
+
for (const block of response.content) {
|
|
200
|
+
if (block.type === "text") {
|
|
201
|
+
text += block.text;
|
|
202
|
+
emitEvent({ type: "delta", content: block.text });
|
|
203
|
+
} else if (block.type === "thinking") {
|
|
204
|
+
thinkingText += (block as { thinking: string }).thinking ?? "";
|
|
205
|
+
emitEvent({ type: "status", phase: "thinking" });
|
|
206
|
+
} else if (block.type === "tool_use") {
|
|
207
|
+
toolCalls.push({
|
|
208
|
+
id: block.id,
|
|
209
|
+
name: block.name,
|
|
210
|
+
arguments: block.input as Record<string, unknown>,
|
|
211
|
+
});
|
|
212
|
+
emitEvent({ type: "status", phase: "tool_use", message: block.name });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
text: text || thinkingText, // Fall back to thinking if no text
|
|
218
|
+
toolCalls,
|
|
219
|
+
isComplete: stopReason === "end_turn",
|
|
220
|
+
needsContinuation: stopReason === "max_tokens",
|
|
221
|
+
usage,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Session persistence ──────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
async function saveSessionSnapshot(
|
|
228
|
+
taskId: string,
|
|
229
|
+
profileId: string,
|
|
230
|
+
messages: LoopMessage[],
|
|
231
|
+
) {
|
|
232
|
+
await db.insert(agentLogs).values({
|
|
233
|
+
id: crypto.randomUUID(),
|
|
234
|
+
taskId,
|
|
235
|
+
agentType: profileId,
|
|
236
|
+
event: "session_snapshot",
|
|
237
|
+
payload: JSON.stringify({ messages }),
|
|
238
|
+
timestamp: new Date(),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function loadSessionSnapshot(taskId: string): Promise<LoopMessage[] | null> {
|
|
243
|
+
const [log] = await db
|
|
244
|
+
.select()
|
|
245
|
+
.from(agentLogs)
|
|
246
|
+
.where(eq(agentLogs.taskId, taskId))
|
|
247
|
+
.orderBy(agentLogs.timestamp)
|
|
248
|
+
.limit(1);
|
|
249
|
+
|
|
250
|
+
if (!log?.payload) return null;
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const data = JSON.parse(log.payload);
|
|
254
|
+
if (Array.isArray(data.messages)) return data.messages;
|
|
255
|
+
} catch {
|
|
256
|
+
// Corrupted snapshot
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ── Core task execution ──────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
async function executeAnthropicDirectTask(taskId: string, isResume = false): Promise<void> {
|
|
264
|
+
const [task] = await db.select().from(tasks).where(eq(tasks.id, taskId));
|
|
265
|
+
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
266
|
+
|
|
267
|
+
const agentProfileId = task.agentProfile ?? "general";
|
|
268
|
+
const usageState = createTaskUsageState(task, isResume);
|
|
269
|
+
const abortController = new AbortController();
|
|
270
|
+
|
|
271
|
+
setExecution(taskId, {
|
|
272
|
+
abortController,
|
|
273
|
+
sessionId: null,
|
|
274
|
+
taskId,
|
|
275
|
+
startedAt: new Date(),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// Mark as running
|
|
280
|
+
await db
|
|
281
|
+
.update(tasks)
|
|
282
|
+
.set({ status: "running", updatedAt: new Date() })
|
|
283
|
+
.where(eq(tasks.id, taskId));
|
|
284
|
+
|
|
285
|
+
const ctx = await buildTaskQueryContext(task, agentProfileId);
|
|
286
|
+
const apiKey = await getAnthropicApiKey();
|
|
287
|
+
const sdk = await getAnthropicSDK();
|
|
288
|
+
const client = new sdk.default({ apiKey });
|
|
289
|
+
|
|
290
|
+
// Get tools in Anthropic format
|
|
291
|
+
const toolServer = createToolServer(task.projectId);
|
|
292
|
+
const { tools, executeHandler } = toolServer.forProvider("anthropic");
|
|
293
|
+
|
|
294
|
+
// Build initial messages or restore from snapshot
|
|
295
|
+
let initialMessages: LoopMessage[];
|
|
296
|
+
if (isResume) {
|
|
297
|
+
const snapshot = await loadSessionSnapshot(taskId);
|
|
298
|
+
initialMessages = snapshot ?? [{ role: "user", content: ctx.userPrompt }];
|
|
299
|
+
} else {
|
|
300
|
+
initialMessages = [{ role: "user", content: ctx.userPrompt }];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Resolve model from settings
|
|
304
|
+
const { getSetting } = await import("@/lib/settings/helpers");
|
|
305
|
+
const modelId = (await getSetting("anthropic_direct_model")) ?? "claude-sonnet-4-20250514";
|
|
306
|
+
|
|
307
|
+
const maxTurns = ctx.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
308
|
+
|
|
309
|
+
// Log start
|
|
310
|
+
await db.insert(agentLogs).values({
|
|
311
|
+
id: crypto.randomUUID(),
|
|
312
|
+
taskId,
|
|
313
|
+
agentType: agentProfileId,
|
|
314
|
+
event: isResume ? "resumed" : "started",
|
|
315
|
+
payload: JSON.stringify({
|
|
316
|
+
runtime: "anthropic-direct",
|
|
317
|
+
model: modelId,
|
|
318
|
+
maxTurns,
|
|
319
|
+
}),
|
|
320
|
+
timestamp: new Date(),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Run the agentic loop
|
|
324
|
+
const result = await runAgenticLoop(initialMessages, {
|
|
325
|
+
async callModel(messages, signal) {
|
|
326
|
+
// Resolve capability overrides from profile
|
|
327
|
+
const profile = getProfile(agentProfileId);
|
|
328
|
+
const capOverrides = profile?.capabilityOverrides?.["anthropic-direct"];
|
|
329
|
+
|
|
330
|
+
const turnResult = await callAnthropicModel(
|
|
331
|
+
client,
|
|
332
|
+
ctx.systemInstructions,
|
|
333
|
+
messages,
|
|
334
|
+
tools as AnthropicToolDef[],
|
|
335
|
+
signal,
|
|
336
|
+
(evt) => {
|
|
337
|
+
void db.insert(agentLogs).values({
|
|
338
|
+
id: crypto.randomUUID(),
|
|
339
|
+
taskId,
|
|
340
|
+
agentType: agentProfileId,
|
|
341
|
+
event: "stream",
|
|
342
|
+
payload: JSON.stringify(evt),
|
|
343
|
+
timestamp: new Date(),
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
modelId: capOverrides?.modelId ?? modelId,
|
|
348
|
+
enableCaching: true,
|
|
349
|
+
profileInstructions: profile?.skillMd,
|
|
350
|
+
extendedThinking: capOverrides?.extendedThinking,
|
|
351
|
+
},
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// Save session snapshot after each model turn
|
|
355
|
+
await saveSessionSnapshot(taskId, agentProfileId, [
|
|
356
|
+
...messages,
|
|
357
|
+
{ role: "assistant", content: turnResult.text },
|
|
358
|
+
]);
|
|
359
|
+
|
|
360
|
+
return turnResult;
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
formatToolResult(toolCallId, _toolName, result) {
|
|
364
|
+
return {
|
|
365
|
+
role: "user",
|
|
366
|
+
content: [
|
|
367
|
+
{
|
|
368
|
+
type: "tool_result",
|
|
369
|
+
tool_use_id: toolCallId,
|
|
370
|
+
content: result.content.map((c) => ({
|
|
371
|
+
type: "text",
|
|
372
|
+
text: c.text,
|
|
373
|
+
})),
|
|
374
|
+
is_error: result.isError ?? false,
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
formatContinuation() {
|
|
381
|
+
return { role: "user", content: "Please continue." };
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
async executeTool(name, args) {
|
|
385
|
+
return executeHandler(name, args);
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
async checkPermission(toolName, args) {
|
|
389
|
+
return handleToolPermission(taskId, toolName, args, ctx.canUseToolPolicy);
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
emitEvent(event) {
|
|
393
|
+
// Events are logged in callModel; no-op here
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
maxTurns,
|
|
397
|
+
maxBudgetUsd: DEFAULT_MAX_BUDGET_USD,
|
|
398
|
+
signal: abortController.signal,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Finalize task based on result
|
|
402
|
+
const finalStatus = result.stopReason === "complete" ? "completed" : "failed";
|
|
403
|
+
const resultText = result.stopReason === "complete"
|
|
404
|
+
? result.finalText
|
|
405
|
+
: `Task stopped: ${result.stopReason}`;
|
|
406
|
+
|
|
407
|
+
await db
|
|
408
|
+
.update(tasks)
|
|
409
|
+
.set({ status: finalStatus, result: resultText, updatedAt: new Date() })
|
|
410
|
+
.where(eq(tasks.id, taskId));
|
|
411
|
+
|
|
412
|
+
await db.insert(notifications).values({
|
|
413
|
+
id: crypto.randomUUID(),
|
|
414
|
+
taskId,
|
|
415
|
+
type: finalStatus === "completed" ? "task_completed" : "task_failed",
|
|
416
|
+
title: `Task ${finalStatus}: ${task.title}`,
|
|
417
|
+
body: resultText.slice(0, 500),
|
|
418
|
+
createdAt: new Date(),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await db.insert(agentLogs).values({
|
|
422
|
+
id: crypto.randomUUID(),
|
|
423
|
+
taskId,
|
|
424
|
+
agentType: agentProfileId,
|
|
425
|
+
event: finalStatus,
|
|
426
|
+
payload: JSON.stringify({
|
|
427
|
+
result: resultText.slice(0, 1000),
|
|
428
|
+
turns: result.turnCount,
|
|
429
|
+
usage: result.totalUsage,
|
|
430
|
+
}),
|
|
431
|
+
timestamp: new Date(),
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Record usage
|
|
435
|
+
await recordUsageLedgerEntry({
|
|
436
|
+
taskId,
|
|
437
|
+
workflowId: task.workflowId ?? null,
|
|
438
|
+
scheduleId: task.scheduleId ?? null,
|
|
439
|
+
projectId: task.projectId ?? null,
|
|
440
|
+
activityType: resolveUsageActivityType({
|
|
441
|
+
workflowId: task.workflowId,
|
|
442
|
+
scheduleId: task.scheduleId,
|
|
443
|
+
isResume,
|
|
444
|
+
}),
|
|
445
|
+
runtimeId: "anthropic-direct",
|
|
446
|
+
providerId: "anthropic",
|
|
447
|
+
modelId: result.totalUsage.modelId ?? modelId,
|
|
448
|
+
inputTokens: result.totalUsage.inputTokens ?? null,
|
|
449
|
+
outputTokens: result.totalUsage.outputTokens ?? null,
|
|
450
|
+
totalTokens: result.totalUsage.totalTokens ?? null,
|
|
451
|
+
status: finalStatus,
|
|
452
|
+
startedAt: usageState.startedAt,
|
|
453
|
+
finishedAt: new Date(),
|
|
454
|
+
});
|
|
455
|
+
} catch (err) {
|
|
456
|
+
if (!abortController.signal.aborted) {
|
|
457
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
458
|
+
await db
|
|
459
|
+
.update(tasks)
|
|
460
|
+
.set({ status: "failed", result: errorMsg, updatedAt: new Date() })
|
|
461
|
+
.where(eq(tasks.id, taskId));
|
|
462
|
+
|
|
463
|
+
await db.insert(notifications).values({
|
|
464
|
+
id: crypto.randomUUID(),
|
|
465
|
+
taskId,
|
|
466
|
+
type: "task_failed",
|
|
467
|
+
title: `Task failed: ${task.title}`,
|
|
468
|
+
body: errorMsg.slice(0, 500),
|
|
469
|
+
createdAt: new Date(),
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
} finally {
|
|
473
|
+
clearPermissionCache(taskId);
|
|
474
|
+
removeExecution(taskId);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ── Task Assist ──────────────────────────────────────────────────────
|
|
479
|
+
|
|
480
|
+
async function runAnthropicTaskAssist(input: TaskAssistInput): Promise<TaskAssistResponse> {
|
|
481
|
+
const apiKey = await getAnthropicApiKey();
|
|
482
|
+
const sdk = await getAnthropicSDK();
|
|
483
|
+
const client = new sdk.default({ apiKey });
|
|
484
|
+
|
|
485
|
+
const profileIds = listProfiles().map((p) => p.id);
|
|
486
|
+
const profileList = profileIds.length > 0
|
|
487
|
+
? `Available agent profiles: ${profileIds.join(", ")}`
|
|
488
|
+
: "No explicit profiles available.";
|
|
489
|
+
|
|
490
|
+
const systemPrompt = `You are an AI task definition assistant. Analyze the given task and return ONLY a JSON object (no markdown) with:
|
|
491
|
+
- "improvedDescription": A clearer version of the task
|
|
492
|
+
- "breakdown": Array of step objects if complex (empty array if simple)
|
|
493
|
+
- "recommendedPattern": one of "single", "sequence", "planner-executor", "checkpoint", "parallel", "loop", "swarm"
|
|
494
|
+
- "complexity": "simple", "moderate", or "complex"
|
|
495
|
+
- "needsCheckpoint": boolean
|
|
496
|
+
- "reasoning": Brief explanation
|
|
497
|
+
|
|
498
|
+
${profileList}`;
|
|
499
|
+
|
|
500
|
+
const userContent = [
|
|
501
|
+
input.title ? `Task title: ${input.title}` : "",
|
|
502
|
+
input.description ? `Description: ${input.description}` : "",
|
|
503
|
+
].filter(Boolean).join("\n");
|
|
504
|
+
|
|
505
|
+
const response = await client.messages.create({
|
|
506
|
+
model: "claude-sonnet-4-20250514",
|
|
507
|
+
system: systemPrompt,
|
|
508
|
+
messages: [{ role: "user", content: userContent || "Analyze this task" }],
|
|
509
|
+
max_tokens: 2048,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const text = response.content
|
|
513
|
+
.filter((b) => b.type === "text")
|
|
514
|
+
.map((b) => (b as { type: "text"; text: string }).text)
|
|
515
|
+
.join("");
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
return JSON.parse(text);
|
|
519
|
+
} catch {
|
|
520
|
+
return {
|
|
521
|
+
improvedDescription: text,
|
|
522
|
+
breakdown: [],
|
|
523
|
+
recommendedPattern: "single",
|
|
524
|
+
complexity: "simple",
|
|
525
|
+
needsCheckpoint: false,
|
|
526
|
+
reasoning: "Failed to parse structured response",
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ── Profile Assist ───────────────────────────────────────────────────
|
|
532
|
+
|
|
533
|
+
async function runAnthropicProfileAssist(input: ProfileAssistRequest): Promise<ProfileAssistResponse> {
|
|
534
|
+
const apiKey = await getAnthropicApiKey();
|
|
535
|
+
const sdk = await getAnthropicSDK();
|
|
536
|
+
const client = new sdk.default({ apiKey });
|
|
537
|
+
|
|
538
|
+
const response = await client.messages.create({
|
|
539
|
+
model: "claude-sonnet-4-20250514",
|
|
540
|
+
system: `You are an AI assistant that helps configure agent profiles. Return ONLY a JSON object with the requested fields.`,
|
|
541
|
+
messages: [{ role: "user", content: JSON.stringify(input) }],
|
|
542
|
+
max_tokens: 2048,
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
const text = response.content
|
|
546
|
+
.filter((b) => b.type === "text")
|
|
547
|
+
.map((b) => (b as { type: "text"; text: string }).text)
|
|
548
|
+
.join("");
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
return JSON.parse(text);
|
|
552
|
+
} catch {
|
|
553
|
+
return {
|
|
554
|
+
name: "unknown",
|
|
555
|
+
description: text,
|
|
556
|
+
domain: "work" as const,
|
|
557
|
+
tags: [],
|
|
558
|
+
skillMd: "",
|
|
559
|
+
allowedTools: [],
|
|
560
|
+
canUseToolPolicy: { autoApprove: [], autoDeny: [] },
|
|
561
|
+
maxTurns: 10,
|
|
562
|
+
outputFormat: "",
|
|
563
|
+
supportedRuntimes: ["anthropic-direct"],
|
|
564
|
+
tests: [],
|
|
565
|
+
reasoning: "Failed to parse structured response",
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ── Connection Test ──────────────────────────────────────────────────
|
|
571
|
+
|
|
572
|
+
async function testAnthropicConnection(): Promise<RuntimeConnectionResult> {
|
|
573
|
+
try {
|
|
574
|
+
const apiKey = await getAnthropicApiKey();
|
|
575
|
+
const sdk = await getAnthropicSDK();
|
|
576
|
+
const client = new sdk.default({ apiKey });
|
|
577
|
+
|
|
578
|
+
// Simple validation: create a minimal request
|
|
579
|
+
await client.messages.create({
|
|
580
|
+
model: "claude-sonnet-4-20250514",
|
|
581
|
+
messages: [{ role: "user", content: "ping" }],
|
|
582
|
+
max_tokens: 1,
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
return { connected: true, apiKeySource: "db" };
|
|
586
|
+
} catch (err) {
|
|
587
|
+
return {
|
|
588
|
+
connected: false,
|
|
589
|
+
error: err instanceof Error ? err.message : "Connection failed",
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// ── Adapter export ───────────────────────────────────────────────────
|
|
595
|
+
|
|
596
|
+
export const anthropicDirectRuntimeAdapter: AgentRuntimeAdapter = {
|
|
597
|
+
metadata: getRuntimeCatalogEntry("anthropic-direct" as never), // Registered in catalog.ts
|
|
598
|
+
|
|
599
|
+
async executeTask(taskId: string) {
|
|
600
|
+
return executeAnthropicDirectTask(taskId, false);
|
|
601
|
+
},
|
|
602
|
+
|
|
603
|
+
async resumeTask(taskId: string) {
|
|
604
|
+
return executeAnthropicDirectTask(taskId, true);
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
async cancelTask(taskId: string) {
|
|
608
|
+
const execution = getExecution(taskId);
|
|
609
|
+
if (execution?.abortController) {
|
|
610
|
+
execution.abortController.abort();
|
|
611
|
+
}
|
|
612
|
+
await db
|
|
613
|
+
.update(tasks)
|
|
614
|
+
.set({ status: "cancelled", updatedAt: new Date() })
|
|
615
|
+
.where(eq(tasks.id, taskId));
|
|
616
|
+
removeExecution(taskId);
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
async runTaskAssist(input: TaskAssistInput) {
|
|
620
|
+
return runAnthropicTaskAssist(input);
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
async runProfileAssist(input: ProfileAssistRequest) {
|
|
624
|
+
return runAnthropicProfileAssist(input);
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
async runProfileTests(profileId: string): Promise<ProfileTestReport> {
|
|
628
|
+
const profile = getProfile(profileId);
|
|
629
|
+
return {
|
|
630
|
+
profileId,
|
|
631
|
+
profileName: profile?.name ?? profileId,
|
|
632
|
+
runtimeId: "anthropic-direct",
|
|
633
|
+
results: [],
|
|
634
|
+
totalPassed: 0,
|
|
635
|
+
totalFailed: 0,
|
|
636
|
+
unsupported: true,
|
|
637
|
+
unsupportedReason: "Profile smoke tests not yet implemented for Anthropic Direct runtime",
|
|
638
|
+
};
|
|
639
|
+
},
|
|
640
|
+
|
|
641
|
+
async testConnection() {
|
|
642
|
+
return testAnthropicConnection();
|
|
643
|
+
},
|
|
644
|
+
};
|