@vellumai/assistant 0.5.6 → 0.5.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/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +1 -1
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +8 -14
- package/src/config/feature-flag-registry.json +48 -8
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +227 -51
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +12 -0
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/jobs-store.ts +30 -5
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +27 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.ts +24 -6
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +3 -50
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getCCCommand,
|
|
3
|
-
loadCCCommandTemplate,
|
|
4
|
-
} from "../../commands/cc-command-registry.js";
|
|
5
|
-
import { RiskLevel } from "../../permissions/types.js";
|
|
6
|
-
import type { ToolDefinition } from "../../providers/types.js";
|
|
7
|
-
import { getProviderKeyAsync } from "../../security/secure-keys.js";
|
|
8
|
-
import type { WorkerProfile } from "../../swarm/worker-backend.js";
|
|
9
|
-
import { getProfilePolicy } from "../../swarm/worker-backend.js";
|
|
10
|
-
import { getLogger } from "../../util/logger.js";
|
|
11
|
-
import { truncate } from "../../util/truncate.js";
|
|
12
|
-
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
13
|
-
|
|
14
|
-
const log = getLogger("claude-code-tool");
|
|
15
|
-
|
|
16
|
-
// Tools that CC can use without user approval
|
|
17
|
-
const AUTO_APPROVE_TOOLS = new Set([
|
|
18
|
-
"Read",
|
|
19
|
-
"Glob",
|
|
20
|
-
"Grep",
|
|
21
|
-
"WebSearch",
|
|
22
|
-
"WebFetch",
|
|
23
|
-
"LS",
|
|
24
|
-
"Bash(grep *)",
|
|
25
|
-
"Bash(rg *)",
|
|
26
|
-
"Bash(find *)",
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
// Tools that always require user approval via confirmation prompt
|
|
30
|
-
const APPROVAL_REQUIRED_TOOLS = new Set([
|
|
31
|
-
"Bash",
|
|
32
|
-
"Edit",
|
|
33
|
-
"Write",
|
|
34
|
-
"MultiEdit",
|
|
35
|
-
"NotebookEdit",
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
const VALID_PROFILES: readonly WorkerProfile[] = [
|
|
39
|
-
"general",
|
|
40
|
-
"researcher",
|
|
41
|
-
"coder",
|
|
42
|
-
"reviewer",
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
// Maximum nesting depth for Claude Code subprocesses.
|
|
46
|
-
// Depth 0 = top-level assistant, depth 1 = first subprocess, etc.
|
|
47
|
-
const MAX_CLAUDE_CODE_DEPTH = 1;
|
|
48
|
-
const DEPTH_ENV_VAR = "VELLUM_CLAUDE_CODE_DEPTH";
|
|
49
|
-
|
|
50
|
-
function summarizeToolInput(
|
|
51
|
-
toolName: string,
|
|
52
|
-
input: Record<string, unknown>,
|
|
53
|
-
): string {
|
|
54
|
-
// Extract the most relevant field for each tool type
|
|
55
|
-
const name = toolName.toLowerCase();
|
|
56
|
-
if (name === "bash") return String(input.command ?? "");
|
|
57
|
-
if (name === "read" || name === "file_read")
|
|
58
|
-
return String(input.file_path ?? input.path ?? "");
|
|
59
|
-
if (name === "edit" || name === "file_edit")
|
|
60
|
-
return String(input.file_path ?? input.path ?? "");
|
|
61
|
-
if (name === "write" || name === "file_write")
|
|
62
|
-
return String(input.file_path ?? input.path ?? "");
|
|
63
|
-
if (name === "glob") return String(input.pattern ?? "");
|
|
64
|
-
if (name === "grep") return String(input.pattern ?? "");
|
|
65
|
-
if (name === "websearch" || name === "web_search")
|
|
66
|
-
return String(input.query ?? "");
|
|
67
|
-
if (name === "webfetch" || name === "web_fetch")
|
|
68
|
-
return String(input.url ?? "");
|
|
69
|
-
if (name === "task") return String(input.description ?? "");
|
|
70
|
-
// Fallback: first string value
|
|
71
|
-
for (const val of Object.values(input)) {
|
|
72
|
-
if (typeof val === "string" && val.length > 0 && val.length < 200)
|
|
73
|
-
return val;
|
|
74
|
-
}
|
|
75
|
-
return "";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export const claudeCodeTool: Tool = {
|
|
79
|
-
name: "claude_code",
|
|
80
|
-
description:
|
|
81
|
-
"Delegate a coding task to Claude Code, an AI-powered coding agent that can read, write, and edit files, run shell commands, and perform complex multi-step software engineering tasks autonomously.",
|
|
82
|
-
category: "coding",
|
|
83
|
-
defaultRiskLevel: RiskLevel.Medium,
|
|
84
|
-
|
|
85
|
-
getDefinition(): ToolDefinition {
|
|
86
|
-
return {
|
|
87
|
-
name: "claude_code",
|
|
88
|
-
description: this.description,
|
|
89
|
-
input_schema: {
|
|
90
|
-
type: "object",
|
|
91
|
-
properties: {
|
|
92
|
-
prompt: {
|
|
93
|
-
type: "string",
|
|
94
|
-
description:
|
|
95
|
-
"The coding task or question for Claude Code to work on. Use this for free-form tasks. Mutually exclusive with command.",
|
|
96
|
-
},
|
|
97
|
-
command: {
|
|
98
|
-
type: "string",
|
|
99
|
-
description:
|
|
100
|
-
"Name of a .claude/commands/*.md command template to execute. The template will be loaded and $ARGUMENTS substituted before execution. Use this instead of prompt when invoking a named CC command.",
|
|
101
|
-
},
|
|
102
|
-
arguments: {
|
|
103
|
-
type: "string",
|
|
104
|
-
description:
|
|
105
|
-
"Arguments to substitute into the command template ($ARGUMENTS placeholder). Only used with the command input.",
|
|
106
|
-
},
|
|
107
|
-
working_dir: {
|
|
108
|
-
type: "string",
|
|
109
|
-
description:
|
|
110
|
-
"Working directory for Claude Code (defaults to conversation working directory)",
|
|
111
|
-
},
|
|
112
|
-
resume: {
|
|
113
|
-
type: "string",
|
|
114
|
-
description: "Claude Code session ID to resume a previous session",
|
|
115
|
-
},
|
|
116
|
-
model: {
|
|
117
|
-
type: "string",
|
|
118
|
-
description: "Model to use (defaults to claude-sonnet-4-6)",
|
|
119
|
-
},
|
|
120
|
-
profile: {
|
|
121
|
-
type: "string",
|
|
122
|
-
enum: ["general", "researcher", "coder", "reviewer"],
|
|
123
|
-
description:
|
|
124
|
-
"Worker profile that scopes tool access. Defaults to general.",
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
async execute(
|
|
132
|
-
input: Record<string, unknown>,
|
|
133
|
-
context: ToolContext,
|
|
134
|
-
): Promise<ToolExecutionResult> {
|
|
135
|
-
if (context.signal?.aborted) {
|
|
136
|
-
return { content: "Cancelled", isError: true };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const workingDir = (input.working_dir as string) || context.workingDir;
|
|
140
|
-
|
|
141
|
-
// Resolve prompt: either from direct prompt input or by loading a CC command template
|
|
142
|
-
let prompt: string;
|
|
143
|
-
if (input.command != null && typeof input.command !== "string") {
|
|
144
|
-
return {
|
|
145
|
-
content: `Error: "command" must be a string, got ${typeof input.command}`,
|
|
146
|
-
isError: true,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
const commandName = input.command as string | undefined;
|
|
150
|
-
if (commandName) {
|
|
151
|
-
// Command-template execution path: load .claude/commands/<command>.md,
|
|
152
|
-
// apply $ARGUMENTS substitution, and use the result as the prompt.
|
|
153
|
-
const entry = getCCCommand(workingDir, commandName);
|
|
154
|
-
if (!entry) {
|
|
155
|
-
return {
|
|
156
|
-
content: `Error: CC command "${commandName}" not found. Looked for .claude/commands/${commandName}.md in ${workingDir} and parent directories.`,
|
|
157
|
-
isError: true,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let template: string;
|
|
162
|
-
try {
|
|
163
|
-
template = loadCCCommandTemplate(entry);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
-
return {
|
|
167
|
-
content: `Error: Failed to load CC command template "${commandName}": ${message}`,
|
|
168
|
-
isError: true,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Substitute $ARGUMENTS placeholder with the provided arguments
|
|
173
|
-
const args = (input.arguments as string) ?? "";
|
|
174
|
-
prompt = template.replace(/\$ARGUMENTS/g, args);
|
|
175
|
-
|
|
176
|
-
log.info(
|
|
177
|
-
{ command: commandName, templatePath: entry.filePath, hasArgs: !!args },
|
|
178
|
-
"Loaded CC command template",
|
|
179
|
-
);
|
|
180
|
-
} else if (typeof input.prompt === "string") {
|
|
181
|
-
prompt = input.prompt;
|
|
182
|
-
} else {
|
|
183
|
-
return {
|
|
184
|
-
content: 'Error: Either "prompt" or "command" must be provided.',
|
|
185
|
-
isError: true,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
const resumeSessionId = input.resume as string | undefined;
|
|
189
|
-
const model = (input.model as string) || "claude-sonnet-4-6";
|
|
190
|
-
const profileName =
|
|
191
|
-
(input.profile as WorkerProfile | undefined) ?? "general";
|
|
192
|
-
|
|
193
|
-
// Validate profile
|
|
194
|
-
if (!VALID_PROFILES.includes(profileName)) {
|
|
195
|
-
return {
|
|
196
|
-
content: `Error: Invalid profile "${profileName}". Valid profiles: ${VALID_PROFILES.join(
|
|
197
|
-
", ",
|
|
198
|
-
)}.`,
|
|
199
|
-
isError: true,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const profilePolicy = getProfilePolicy(profileName);
|
|
204
|
-
|
|
205
|
-
// Validate API key
|
|
206
|
-
const apiKey = await getProviderKeyAsync("anthropic");
|
|
207
|
-
if (!apiKey) {
|
|
208
|
-
return {
|
|
209
|
-
content:
|
|
210
|
-
"Error: No Anthropic API key configured. Set it via `keys set anthropic <key>` or configure it from the Settings page under API Keys.",
|
|
211
|
-
isError: true,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Dynamic import of the Agent SDK
|
|
216
|
-
let sdkModule: typeof import("@anthropic-ai/claude-agent-sdk");
|
|
217
|
-
try {
|
|
218
|
-
sdkModule = await import("@anthropic-ai/claude-agent-sdk");
|
|
219
|
-
} catch (err) {
|
|
220
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
221
|
-
log.error({ err }, "Failed to load Claude Agent SDK");
|
|
222
|
-
return {
|
|
223
|
-
content: `Error: Failed to load Claude Agent SDK: ${message}`,
|
|
224
|
-
isError: true,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const { query } = sdkModule;
|
|
229
|
-
|
|
230
|
-
// Collect stderr output from the Claude Code subprocess for debugging
|
|
231
|
-
const stderrLines: string[] = [];
|
|
232
|
-
|
|
233
|
-
log.info(
|
|
234
|
-
{
|
|
235
|
-
prompt: truncate(prompt, 100, ""),
|
|
236
|
-
workingDir,
|
|
237
|
-
model,
|
|
238
|
-
resume: !!resumeSessionId,
|
|
239
|
-
},
|
|
240
|
-
"Starting Claude Code session",
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
// Build the canUseTool callback, enforcing profile-based restrictions
|
|
244
|
-
const canUseTool: import("@anthropic-ai/claude-agent-sdk").CanUseTool =
|
|
245
|
-
async (toolName, toolInput, _options) => {
|
|
246
|
-
// Profile hard-deny check first
|
|
247
|
-
if (profilePolicy.deny.has(toolName)) {
|
|
248
|
-
log.debug(
|
|
249
|
-
{ toolName, profile: profileName },
|
|
250
|
-
"Tool denied by profile policy",
|
|
251
|
-
);
|
|
252
|
-
return {
|
|
253
|
-
behavior: "deny" as const,
|
|
254
|
-
message: `Tool "${toolName}" is denied by profile "${profileName}"`,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Profile explicit allow (auto-approve)
|
|
259
|
-
if (profilePolicy.allow.has(toolName)) {
|
|
260
|
-
return { behavior: "allow" as const };
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Auto-approve safe read-only tools (general profile default)
|
|
264
|
-
if (AUTO_APPROVE_TOOLS.has(toolName)) {
|
|
265
|
-
return { behavior: "allow" as const };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// For tools that need approval, bridge to Vellum's confirmation flow
|
|
269
|
-
if (!context.requestConfirmation) {
|
|
270
|
-
log.warn(
|
|
271
|
-
{ toolName },
|
|
272
|
-
"Claude Code tool requires approval but no requestConfirmation callback available",
|
|
273
|
-
);
|
|
274
|
-
return {
|
|
275
|
-
behavior: "deny" as const,
|
|
276
|
-
message: "Tool approval not available in this context",
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
try {
|
|
281
|
-
const result = await context.requestConfirmation({
|
|
282
|
-
toolName,
|
|
283
|
-
input: toolInput,
|
|
284
|
-
riskLevel: APPROVAL_REQUIRED_TOOLS.has(toolName) ? "Medium" : "Low",
|
|
285
|
-
principal: context.principal,
|
|
286
|
-
});
|
|
287
|
-
if (result.decision === "allow") {
|
|
288
|
-
return { behavior: "allow" as const };
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
behavior: "deny" as const,
|
|
292
|
-
message: `User denied ${toolName}`,
|
|
293
|
-
};
|
|
294
|
-
} catch (err) {
|
|
295
|
-
log.debug(
|
|
296
|
-
{ err, toolName },
|
|
297
|
-
"requestConfirmation rejected (likely abort)",
|
|
298
|
-
);
|
|
299
|
-
return {
|
|
300
|
-
behavior: "deny" as const,
|
|
301
|
-
message: "Approval request cancelled",
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// Enforce nesting depth limit to prevent infinite recursion.
|
|
307
|
-
const currentDepth = parseInt(process.env[DEPTH_ENV_VAR] ?? "0", 10);
|
|
308
|
-
if (currentDepth >= MAX_CLAUDE_CODE_DEPTH) {
|
|
309
|
-
log.warn(
|
|
310
|
-
{ currentDepth, max: MAX_CLAUDE_CODE_DEPTH },
|
|
311
|
-
"Claude Code nesting depth exceeded",
|
|
312
|
-
);
|
|
313
|
-
return {
|
|
314
|
-
content: `Error: Claude Code nesting depth exceeded (depth ${currentDepth}, max ${MAX_CLAUDE_CODE_DEPTH}). Cannot spawn another Claude Code subprocess.`,
|
|
315
|
-
isError: true,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Build a clean env for the subprocess. Strip the SDK's own nesting guard
|
|
320
|
-
// (CLAUDECODE) so it can launch, but set our depth counter to enforce our limit.
|
|
321
|
-
const subprocessEnv: Record<string, string | undefined> = {
|
|
322
|
-
...process.env,
|
|
323
|
-
ANTHROPIC_API_KEY: apiKey,
|
|
324
|
-
[DEPTH_ENV_VAR]: String(currentDepth + 1),
|
|
325
|
-
};
|
|
326
|
-
delete subprocessEnv.CLAUDECODE;
|
|
327
|
-
delete subprocessEnv.CLAUDE_CODE_ENTRYPOINT;
|
|
328
|
-
|
|
329
|
-
// Build query options
|
|
330
|
-
const queryOptions: import("@anthropic-ai/claude-agent-sdk").Options = {
|
|
331
|
-
cwd: workingDir,
|
|
332
|
-
model,
|
|
333
|
-
canUseTool,
|
|
334
|
-
permissionMode: "default",
|
|
335
|
-
allowedTools: [...AUTO_APPROVE_TOOLS],
|
|
336
|
-
env: subprocessEnv,
|
|
337
|
-
maxTurns: 50,
|
|
338
|
-
persistSession: true,
|
|
339
|
-
stderr: (data: string) => {
|
|
340
|
-
const trimmed = data.trimEnd();
|
|
341
|
-
if (trimmed) {
|
|
342
|
-
stderrLines.push(trimmed);
|
|
343
|
-
log.debug({ stderr: trimmed }, "Claude Code subprocess stderr");
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
if (resumeSessionId) {
|
|
349
|
-
queryOptions.resume = resumeSessionId;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Declared outside try so the catch block can emit a final tool_complete on error.
|
|
353
|
-
let lastSubToolName: string | null = null;
|
|
354
|
-
let activeToolUseId: string | null = null;
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
const conversation = query({ prompt, options: queryOptions });
|
|
358
|
-
let resultText = "";
|
|
359
|
-
let conversationId = "";
|
|
360
|
-
let hasError = false;
|
|
361
|
-
|
|
362
|
-
// Track tool_use_id → {name, inputSummary} for enriching progress events.
|
|
363
|
-
const toolUseIdInfo = new Map<
|
|
364
|
-
string,
|
|
365
|
-
{ name: string; inputSummary: string }
|
|
366
|
-
>();
|
|
367
|
-
// Track tool_use_ids that we've already emitted tool_start for (to avoid duplicates).
|
|
368
|
-
const emittedToolUseIds = new Set<string>();
|
|
369
|
-
|
|
370
|
-
for await (const message of conversation) {
|
|
371
|
-
switch (message.type) {
|
|
372
|
-
case "assistant": {
|
|
373
|
-
// Check for SDK-level errors on the assistant message
|
|
374
|
-
if (message.error) {
|
|
375
|
-
log.error(
|
|
376
|
-
{ error: message.error, conversationId: message.session_id },
|
|
377
|
-
"Claude Code assistant message error",
|
|
378
|
-
);
|
|
379
|
-
hasError = true;
|
|
380
|
-
resultText += `\n\n[Claude Code error: ${message.error}]`;
|
|
381
|
-
}
|
|
382
|
-
// Extract text from assistant messages
|
|
383
|
-
if (message.message?.content) {
|
|
384
|
-
for (const block of message.message.content) {
|
|
385
|
-
if (block.type === "text") {
|
|
386
|
-
context.onOutput?.(block.text);
|
|
387
|
-
resultText += block.text;
|
|
388
|
-
}
|
|
389
|
-
if (block.type === "tool_use") {
|
|
390
|
-
// Capture info keyed by tool_use_id for enriching tool_progress events.
|
|
391
|
-
const inputSummary = summarizeToolInput(
|
|
392
|
-
block.name,
|
|
393
|
-
block.input as Record<string, unknown>,
|
|
394
|
-
);
|
|
395
|
-
toolUseIdInfo.set(block.id, {
|
|
396
|
-
name: block.name,
|
|
397
|
-
inputSummary,
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// Emit tool_start if we haven't already (tool_progress may have fired first).
|
|
401
|
-
// NOTE: Do NOT emit tool_complete for the previous tool here. An assistant
|
|
402
|
-
// message may contain multiple tool_use blocks (parallel tool use) and none
|
|
403
|
-
// of them have executed yet at this point. Completions are handled by
|
|
404
|
-
// tool_use_summary and tool_progress events.
|
|
405
|
-
if (!emittedToolUseIds.has(block.id)) {
|
|
406
|
-
context.onOutput?.(
|
|
407
|
-
JSON.stringify({
|
|
408
|
-
subType: "tool_start",
|
|
409
|
-
subToolName: block.name,
|
|
410
|
-
subToolInput: inputSummary,
|
|
411
|
-
subToolId: block.id,
|
|
412
|
-
}),
|
|
413
|
-
);
|
|
414
|
-
emittedToolUseIds.add(block.id);
|
|
415
|
-
lastSubToolName = block.name;
|
|
416
|
-
activeToolUseId = block.id;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
conversationId = message.session_id;
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
case "tool_progress": {
|
|
425
|
-
// The SDK fires tool_progress periodically DURING tool execution.
|
|
426
|
-
// This is our primary signal for live sub-tool progress.
|
|
427
|
-
const toolUseId = message.tool_use_id;
|
|
428
|
-
const toolName = message.tool_name;
|
|
429
|
-
conversationId = message.session_id;
|
|
430
|
-
|
|
431
|
-
// Record tool name if we don't have it yet (tool_progress fires before assistant sometimes).
|
|
432
|
-
if (!toolUseIdInfo.has(toolUseId)) {
|
|
433
|
-
toolUseIdInfo.set(toolUseId, {
|
|
434
|
-
name: toolName,
|
|
435
|
-
inputSummary: "",
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (!emittedToolUseIds.has(toolUseId)) {
|
|
440
|
-
// New tool - mark previous as complete and emit tool_start.
|
|
441
|
-
if (lastSubToolName && activeToolUseId !== toolUseId) {
|
|
442
|
-
context.onOutput?.(
|
|
443
|
-
JSON.stringify({
|
|
444
|
-
subType: "tool_complete",
|
|
445
|
-
subToolName: lastSubToolName,
|
|
446
|
-
subToolId: activeToolUseId,
|
|
447
|
-
}),
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
const inputSummary =
|
|
451
|
-
toolUseIdInfo.get(toolUseId)?.inputSummary ?? "";
|
|
452
|
-
context.onOutput?.(
|
|
453
|
-
JSON.stringify({
|
|
454
|
-
subType: "tool_start",
|
|
455
|
-
subToolName: toolName,
|
|
456
|
-
subToolInput: inputSummary,
|
|
457
|
-
subToolId: toolUseId,
|
|
458
|
-
}),
|
|
459
|
-
);
|
|
460
|
-
emittedToolUseIds.add(toolUseId);
|
|
461
|
-
lastSubToolName = toolName;
|
|
462
|
-
}
|
|
463
|
-
activeToolUseId = toolUseId;
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
case "tool_use_summary": {
|
|
467
|
-
// The SDK fires tool_use_summary after tool execution with a summary
|
|
468
|
-
// and the IDs of tools that were executed.
|
|
469
|
-
conversationId = message.session_id;
|
|
470
|
-
for (const completedId of message.preceding_tool_use_ids) {
|
|
471
|
-
const info = toolUseIdInfo.get(completedId);
|
|
472
|
-
const completedName: string | null =
|
|
473
|
-
info?.name ?? lastSubToolName;
|
|
474
|
-
if (completedName && emittedToolUseIds.has(completedId)) {
|
|
475
|
-
context.onOutput?.(
|
|
476
|
-
JSON.stringify({
|
|
477
|
-
subType: "tool_complete",
|
|
478
|
-
subToolName: completedName,
|
|
479
|
-
subToolId: completedId,
|
|
480
|
-
}),
|
|
481
|
-
);
|
|
482
|
-
if (lastSubToolName === completedName) {
|
|
483
|
-
lastSubToolName = null;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// Prune completed entries to keep memory flat across long sessions.
|
|
487
|
-
toolUseIdInfo.delete(completedId);
|
|
488
|
-
emittedToolUseIds.delete(completedId);
|
|
489
|
-
}
|
|
490
|
-
activeToolUseId = null;
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
case "result": {
|
|
494
|
-
// Mark the final sub-tool as complete (flag error if the session failed).
|
|
495
|
-
if (lastSubToolName) {
|
|
496
|
-
const isFailure = message.subtype !== "success";
|
|
497
|
-
context.onOutput?.(
|
|
498
|
-
JSON.stringify({
|
|
499
|
-
subType: "tool_complete",
|
|
500
|
-
subToolName: lastSubToolName,
|
|
501
|
-
subToolId: activeToolUseId,
|
|
502
|
-
...(isFailure && { subToolIsError: true }),
|
|
503
|
-
}),
|
|
504
|
-
);
|
|
505
|
-
lastSubToolName = null;
|
|
506
|
-
}
|
|
507
|
-
conversationId = message.session_id;
|
|
508
|
-
const resultMeta = {
|
|
509
|
-
subtype: message.subtype,
|
|
510
|
-
numTurns: message.num_turns,
|
|
511
|
-
durationMs: message.duration_ms,
|
|
512
|
-
costUsd: message.total_cost_usd,
|
|
513
|
-
stopReason: message.stop_reason,
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
if (message.subtype === "success") {
|
|
517
|
-
log.info(
|
|
518
|
-
resultMeta,
|
|
519
|
-
"Claude Code session completed successfully",
|
|
520
|
-
);
|
|
521
|
-
if (message.result && !resultText) {
|
|
522
|
-
resultText = message.result;
|
|
523
|
-
}
|
|
524
|
-
} else {
|
|
525
|
-
// Error result - surface the subtype and details
|
|
526
|
-
hasError = true;
|
|
527
|
-
const errors = message.errors ?? [];
|
|
528
|
-
const denials = message.permission_denials ?? [];
|
|
529
|
-
|
|
530
|
-
log.error(
|
|
531
|
-
{ ...resultMeta, errors, permissionDenials: denials.length },
|
|
532
|
-
"Claude Code session failed",
|
|
533
|
-
);
|
|
534
|
-
|
|
535
|
-
const parts: string[] = [];
|
|
536
|
-
parts.push(
|
|
537
|
-
`[${message.subtype}] (${message.num_turns} turns, ${(
|
|
538
|
-
message.duration_ms / 1000
|
|
539
|
-
).toFixed(1)}s)`,
|
|
540
|
-
);
|
|
541
|
-
if (errors.length > 0) {
|
|
542
|
-
parts.push(`Errors: ${errors.join("; ")}`);
|
|
543
|
-
}
|
|
544
|
-
if (denials.length > 0) {
|
|
545
|
-
const denialSummary = denials
|
|
546
|
-
.map((d: { tool_name: string }) => `${d.tool_name}`)
|
|
547
|
-
.join(", ");
|
|
548
|
-
parts.push(`Permission denied: ${denialSummary}`);
|
|
549
|
-
}
|
|
550
|
-
resultText += `\n\n${parts.join("\n")}`;
|
|
551
|
-
}
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
default:
|
|
555
|
-
// Log unhandled message types at debug level for diagnostics
|
|
556
|
-
log.debug(
|
|
557
|
-
{ messageType: message.type },
|
|
558
|
-
"Claude Code unhandled message type",
|
|
559
|
-
);
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const output =
|
|
565
|
-
resultText.trim() ||
|
|
566
|
-
"Claude Code completed without producing text output.";
|
|
567
|
-
const sessionInfo = conversationId
|
|
568
|
-
? `\n\n[Claude Code session: ${conversationId}]`
|
|
569
|
-
: "";
|
|
570
|
-
|
|
571
|
-
return {
|
|
572
|
-
content: output + sessionInfo,
|
|
573
|
-
isError: hasError,
|
|
574
|
-
};
|
|
575
|
-
} catch (err) {
|
|
576
|
-
// Mark the last sub-tool as failed so the UI shows an error icon.
|
|
577
|
-
if (lastSubToolName) {
|
|
578
|
-
context.onOutput?.(
|
|
579
|
-
JSON.stringify({
|
|
580
|
-
subType: "tool_complete",
|
|
581
|
-
subToolName: lastSubToolName,
|
|
582
|
-
subToolId: activeToolUseId,
|
|
583
|
-
subToolIsError: true,
|
|
584
|
-
}),
|
|
585
|
-
);
|
|
586
|
-
lastSubToolName = null;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const errMessage = err instanceof Error ? err.message : String(err);
|
|
590
|
-
const recentStderr = stderrLines.slice(-20);
|
|
591
|
-
log.error(
|
|
592
|
-
{ err, stderrTail: recentStderr },
|
|
593
|
-
"Claude Code execution failed",
|
|
594
|
-
);
|
|
595
|
-
|
|
596
|
-
const parts = [`Claude Code error: ${errMessage}`];
|
|
597
|
-
if (recentStderr.length > 0) {
|
|
598
|
-
parts.push(
|
|
599
|
-
`\nSubprocess stderr (last ${
|
|
600
|
-
recentStderr.length
|
|
601
|
-
} lines):\n${recentStderr.join("\n")}`,
|
|
602
|
-
);
|
|
603
|
-
}
|
|
604
|
-
return {
|
|
605
|
-
content: parts.join(""),
|
|
606
|
-
isError: true,
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
},
|
|
610
|
-
};
|