@vellumai/assistant 0.6.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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/openapi.yaml +538 -3
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/checker.test.ts +38 -6
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +72 -105
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/agent/loop.ts +6 -0
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/connect.ts +14 -5
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/gmail/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +158 -65
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +300 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +18 -0
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +63 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +199 -157
- package/src/daemon/message-types/conversations.ts +25 -6
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +89 -9
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +136 -107
- package/src/memory/conversation-group-migration.ts +1 -1
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -15
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/permissions/checker.ts +12 -1
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -0
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { RiskLevel } from "../../permissions/types.js";
|
|
2
|
+
import type { ToolDefinition } from "../../providers/types.js";
|
|
3
|
+
import { registerTool } from "../registry.js";
|
|
4
|
+
import { FileSystemOps } from "../shared/filesystem/file-ops-service.js";
|
|
5
|
+
import { sandboxPolicy } from "../shared/filesystem/path-policy.js";
|
|
6
|
+
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
7
|
+
|
|
8
|
+
class FileListTool implements Tool {
|
|
9
|
+
name = "file_list";
|
|
10
|
+
description =
|
|
11
|
+
"List the contents of a directory. Returns file and subdirectory names with type indicators and sizes.";
|
|
12
|
+
category = "filesystem";
|
|
13
|
+
defaultRiskLevel = RiskLevel.Low;
|
|
14
|
+
|
|
15
|
+
getDefinition(): ToolDefinition {
|
|
16
|
+
return {
|
|
17
|
+
name: this.name,
|
|
18
|
+
description: this.description,
|
|
19
|
+
input_schema: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
path: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "The directory path to list",
|
|
25
|
+
},
|
|
26
|
+
glob: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Filter entries by glob pattern, e.g. '*.md'",
|
|
29
|
+
},
|
|
30
|
+
activity: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description:
|
|
33
|
+
"Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["path", "activity"],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async execute(
|
|
42
|
+
input: Record<string, unknown>,
|
|
43
|
+
context: ToolContext,
|
|
44
|
+
): Promise<ToolExecutionResult> {
|
|
45
|
+
const rawPath = input.path as string;
|
|
46
|
+
if (!rawPath || typeof rawPath !== "string") {
|
|
47
|
+
return {
|
|
48
|
+
content: "Error: path is required and must be a string",
|
|
49
|
+
isError: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ops = new FileSystemOps((path, opts) =>
|
|
54
|
+
sandboxPolicy(path, context.workingDir, opts),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const result = ops.listDirSafe({
|
|
58
|
+
path: rawPath,
|
|
59
|
+
glob: typeof input.glob === "string" ? input.glob : undefined,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!result.ok) {
|
|
63
|
+
const { error } = result;
|
|
64
|
+
switch (error.code) {
|
|
65
|
+
case "NOT_A_DIRECTORY":
|
|
66
|
+
return {
|
|
67
|
+
content: `Error: ${error.path} is not a directory`,
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
case "NOT_FOUND":
|
|
71
|
+
return {
|
|
72
|
+
content: `Error: directory not found: ${error.path}`,
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
default: {
|
|
76
|
+
const hint =
|
|
77
|
+
error.code === "PATH_OUT_OF_BOUNDS"
|
|
78
|
+
? ". To list files outside the workspace, use the host_bash tool instead."
|
|
79
|
+
: "";
|
|
80
|
+
return {
|
|
81
|
+
content: `Error: ${error.message}${hint}`,
|
|
82
|
+
isError: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { content: result.value.listing, isError: false };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const fileListTool = new FileListTool();
|
|
93
|
+
registerTool(fileListTool);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
1
2
|
import { getConfig } from "../config/loader.js";
|
|
2
3
|
import { getHookManager } from "../hooks/manager.js";
|
|
3
4
|
import {
|
|
@@ -6,9 +7,11 @@ import {
|
|
|
6
7
|
generateAllowlistOptions,
|
|
7
8
|
generateScopeOptions,
|
|
8
9
|
} from "../permissions/checker.js";
|
|
10
|
+
import { getMode } from "../permissions/permission-mode-store.js";
|
|
9
11
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
10
12
|
import { addRule } from "../permissions/trust-store.js";
|
|
11
13
|
import { RiskLevel } from "../permissions/types.js";
|
|
14
|
+
import { isHostTool } from "../permissions/workspace-policy.js";
|
|
12
15
|
import {
|
|
13
16
|
getEffectiveMode,
|
|
14
17
|
setConversationMode,
|
|
@@ -65,6 +68,52 @@ export class PermissionChecker {
|
|
|
65
68
|
}
|
|
66
69
|
| undefined,
|
|
67
70
|
): Promise<PermissionDecision> {
|
|
71
|
+
// ── permission-controls-v2 early gate ──────────────────────────────
|
|
72
|
+
// When the v2 flag is enabled, replace the entire risk-classification
|
|
73
|
+
// path with a simple binary check: is it a host tool + is host access
|
|
74
|
+
// enabled? Certain security gates (requireFreshApproval,
|
|
75
|
+
// forcePromptSideEffects, hostAccess=false) fall through to the v1
|
|
76
|
+
// prompt flow so the interactive prompter is engaged.
|
|
77
|
+
const cfg = getConfig();
|
|
78
|
+
let v2ForcePrompt = false;
|
|
79
|
+
if (isAssistantFeatureFlagEnabled("permission-controls-v2", cfg)) {
|
|
80
|
+
// requireFreshApproval demands an interactive prompt every time —
|
|
81
|
+
// fall through to v1 so the prompter is engaged.
|
|
82
|
+
const needsFreshApproval = !!context.requireFreshApproval;
|
|
83
|
+
|
|
84
|
+
// forcePromptSideEffects (private conversations, untrusted actors)
|
|
85
|
+
// requires explicit approval for side-effect tools.
|
|
86
|
+
const needsSideEffectPrompt =
|
|
87
|
+
!!context.forcePromptSideEffects && isSideEffectTool(name, input);
|
|
88
|
+
|
|
89
|
+
if (!needsFreshApproval && !needsSideEffectPrompt) {
|
|
90
|
+
if (isHostTool(name)) {
|
|
91
|
+
const mode = getMode();
|
|
92
|
+
if (mode.hostAccess) {
|
|
93
|
+
return {
|
|
94
|
+
allowed: true,
|
|
95
|
+
decision: "allow",
|
|
96
|
+
riskLevel: RiskLevel.Low,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Host tool with hostAccess disabled — fall through to v1 so the
|
|
100
|
+
// interactive prompter is engaged (returning allowed:false here
|
|
101
|
+
// would surface an error string instead of a permission dialog).
|
|
102
|
+
// The v2ForcePrompt flag ensures check()'s allow decision is
|
|
103
|
+
// promoted to prompt so the user sees a permission dialog.
|
|
104
|
+
v2ForcePrompt = true;
|
|
105
|
+
} else {
|
|
106
|
+
// Non-host tools are auto-allowed when v2 is on
|
|
107
|
+
return {
|
|
108
|
+
allowed: true,
|
|
109
|
+
decision: "allow",
|
|
110
|
+
riskLevel: RiskLevel.Low,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Falls through to the v1 risk-classification + prompter path
|
|
115
|
+
}
|
|
116
|
+
|
|
68
117
|
const risk = await classifyRisk(
|
|
69
118
|
name,
|
|
70
119
|
input,
|
|
@@ -114,6 +163,14 @@ export class PermissionChecker {
|
|
|
114
163
|
"Fresh approval required: per-invocation human review enforced";
|
|
115
164
|
}
|
|
116
165
|
|
|
166
|
+
// v2 host-access-disabled: the v2 gate fell through because
|
|
167
|
+
// hostAccess is off. Promote allow → prompt so the user sees an
|
|
168
|
+
// interactive permission dialog instead of an error string.
|
|
169
|
+
if (v2ForcePrompt && result.decision === "allow") {
|
|
170
|
+
result.decision = "prompt";
|
|
171
|
+
result.reason = "Host access disabled: requires explicit approval";
|
|
172
|
+
}
|
|
173
|
+
|
|
117
174
|
if (result.decision === "deny") {
|
|
118
175
|
const durationMs = Date.now() - startTime;
|
|
119
176
|
emitLifecycleEvent({
|
|
@@ -137,6 +194,26 @@ export class PermissionChecker {
|
|
|
137
194
|
};
|
|
138
195
|
}
|
|
139
196
|
|
|
197
|
+
// Platform-hosted mode: auto-approve sandboxed bash for guardians.
|
|
198
|
+
// The sandbox provides the security boundary — prompting is unnecessary
|
|
199
|
+
// friction. host_bash is excluded because it runs unsandboxed on the
|
|
200
|
+
// user's machine and warrants explicit approval.
|
|
201
|
+
// Deny rules are still respected (checked above). requireFreshApproval
|
|
202
|
+
// is preserved as a belt-and-suspenders guard.
|
|
203
|
+
if (
|
|
204
|
+
result.decision === "prompt" &&
|
|
205
|
+
context.isPlatformHosted &&
|
|
206
|
+
name === "bash" &&
|
|
207
|
+
context.trustClass === "guardian" &&
|
|
208
|
+
!context.requireFreshApproval
|
|
209
|
+
) {
|
|
210
|
+
log.info(
|
|
211
|
+
{ toolName: name, riskLevel },
|
|
212
|
+
"Auto-approving bash tool for platform-hosted guardian session",
|
|
213
|
+
);
|
|
214
|
+
return { allowed: true, decision: "platform_auto_approve", riskLevel };
|
|
215
|
+
}
|
|
216
|
+
|
|
140
217
|
if (result.decision === "prompt") {
|
|
141
218
|
// Guardian-trust sessions (e.g. scheduled jobs, reminders) should be
|
|
142
219
|
// able to use bundled tools without interactive approval. The guardian
|
|
@@ -158,6 +235,7 @@ export class PermissionChecker {
|
|
|
158
235
|
context.trustClass === "guardian" &&
|
|
159
236
|
!context.requireFreshApproval &&
|
|
160
237
|
!isDynamicSkillLoad &&
|
|
238
|
+
!v2ForcePrompt &&
|
|
161
239
|
riskLevel !== RiskLevel.High
|
|
162
240
|
) {
|
|
163
241
|
log.info(
|
package/src/tools/registry.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { hostFileEditTool } from "./host-filesystem/edit.js";
|
|
|
7
7
|
import { hostFileReadTool } from "./host-filesystem/read.js";
|
|
8
8
|
import { hostFileWriteTool } from "./host-filesystem/write.js";
|
|
9
9
|
import { hostShellTool } from "./host-terminal/host-shell.js";
|
|
10
|
+
import { registerSystemTools } from "./system/register.js";
|
|
11
|
+
import { setPermissionModeTool } from "./system/set-permission-mode.js";
|
|
10
12
|
import type { Tool } from "./types.js";
|
|
11
13
|
import { allUiSurfaceTools } from "./ui-surface/definitions.js";
|
|
12
14
|
import { registerUiSurfaceTools } from "./ui-surface/registry.js";
|
|
@@ -264,6 +266,7 @@ export async function initializeTools(): Promise<void> {
|
|
|
264
266
|
|
|
265
267
|
registerUiSurfaceTools();
|
|
266
268
|
registerAppTools();
|
|
269
|
+
registerSystemTools();
|
|
267
270
|
|
|
268
271
|
// Snapshot core tools for __resetRegistryForTesting(). We include every
|
|
269
272
|
// non-skill tool that was registered by the manifest, while excluding
|
|
@@ -282,6 +285,7 @@ export async function initializeTools(): Promise<void> {
|
|
|
282
285
|
...allComputerUseTools.map((t: Tool) => t.name),
|
|
283
286
|
...allUiSurfaceTools.map((t: Tool) => t.name),
|
|
284
287
|
...coreAppProxyTools.map((t: Tool) => t.name),
|
|
288
|
+
setPermissionModeTool.name,
|
|
285
289
|
]);
|
|
286
290
|
|
|
287
291
|
coreToolsSnapshot = new Map<string, Tool>();
|
|
@@ -42,6 +42,7 @@ export async function executeScheduleCreate(
|
|
|
42
42
|
| Record<string, unknown>
|
|
43
43
|
| undefined;
|
|
44
44
|
const quiet = (input.quiet as boolean) ?? false;
|
|
45
|
+
const reuseConversation = (input.reuse_conversation as boolean) ?? false;
|
|
45
46
|
|
|
46
47
|
if (!name || typeof name !== "string") {
|
|
47
48
|
return {
|
|
@@ -114,6 +115,7 @@ export async function executeScheduleCreate(
|
|
|
114
115
|
routingIntent: routingIntent as RoutingIntent | undefined,
|
|
115
116
|
routingHints,
|
|
116
117
|
quiet,
|
|
118
|
+
reuseConversation,
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
const fireDate = formatLocalDate(job.nextRunAt);
|
|
@@ -190,6 +192,7 @@ export async function executeScheduleCreate(
|
|
|
190
192
|
routingIntent: routingIntent as RoutingIntent | undefined,
|
|
191
193
|
routingHints,
|
|
192
194
|
quiet,
|
|
195
|
+
reuseConversation,
|
|
193
196
|
});
|
|
194
197
|
|
|
195
198
|
const scheduleDescription =
|
|
@@ -102,6 +102,11 @@ export async function executeScheduleUpdate(
|
|
|
102
102
|
updates.quiet = input.quiet;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// Conversation reuse
|
|
106
|
+
if (input.reuse_conversation !== undefined) {
|
|
107
|
+
updates.reuseConversation = input.reuse_conversation;
|
|
108
|
+
}
|
|
109
|
+
|
|
105
110
|
// Auto-detect syntax when expression changes without explicit syntax
|
|
106
111
|
if (input.expression !== undefined || input.syntax !== undefined) {
|
|
107
112
|
const resolved = normalizeScheduleSyntax({
|
|
@@ -165,6 +170,7 @@ export async function executeScheduleUpdate(
|
|
|
165
170
|
routingIntent?: RoutingIntent;
|
|
166
171
|
routingHints?: Record<string, unknown>;
|
|
167
172
|
quiet?: boolean;
|
|
173
|
+
reuseConversation?: boolean;
|
|
168
174
|
},
|
|
169
175
|
);
|
|
170
176
|
|
|
@@ -8,6 +8,7 @@ export type FsErrorCode =
|
|
|
8
8
|
| "PATH_NOT_ABSOLUTE"
|
|
9
9
|
| "NOT_FOUND"
|
|
10
10
|
| "NOT_A_FILE"
|
|
11
|
+
| "NOT_A_DIRECTORY"
|
|
11
12
|
| "SIZE_LIMIT_EXCEEDED"
|
|
12
13
|
| "MATCH_NOT_FOUND"
|
|
13
14
|
| "MATCH_AMBIGUOUS"
|
|
@@ -56,6 +57,10 @@ export function notAFile(path: string): FsError {
|
|
|
56
57
|
return { code: "NOT_A_FILE", message: `Not a regular file: ${path}`, path };
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
export function notADirectory(path: string): FsError {
|
|
61
|
+
return { code: "NOT_A_DIRECTORY", message: `Not a directory: ${path}`, path };
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
export function sizeLimitExceeded(path: string, detail: string): FsError {
|
|
60
65
|
return {
|
|
61
66
|
code: "SIZE_LIMIT_EXCEEDED",
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
lstatSync,
|
|
3
|
+
readdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
statSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
|
|
10
|
+
import { minimatch } from "minimatch";
|
|
3
11
|
|
|
4
12
|
import { ensureDir, pathExists } from "../../../util/fs.js";
|
|
5
13
|
import { applyEdit } from "./edit-engine.js";
|
|
@@ -9,6 +17,8 @@ import { checkContentSize, checkFileSizeOnDisk } from "./size-guard.js";
|
|
|
9
17
|
import type {
|
|
10
18
|
EditInput,
|
|
11
19
|
EditResult,
|
|
20
|
+
ListInput,
|
|
21
|
+
ListResult,
|
|
12
22
|
ReadInput,
|
|
13
23
|
ReadResult,
|
|
14
24
|
WriteInput,
|
|
@@ -237,4 +247,82 @@ export class FileSystemOps {
|
|
|
237
247
|
},
|
|
238
248
|
};
|
|
239
249
|
}
|
|
250
|
+
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
// List
|
|
253
|
+
// -------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
listDirSafe(input: ListInput): ListResult {
|
|
256
|
+
const pathCheck = this.policy(input.path, { mustExist: true });
|
|
257
|
+
if (!pathCheck.ok) {
|
|
258
|
+
return {
|
|
259
|
+
ok: false,
|
|
260
|
+
error: pathError(input.path, pathCheck.reason, pathCheck.error),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const resolved = pathCheck.resolved;
|
|
264
|
+
|
|
265
|
+
if (!pathExists(resolved)) {
|
|
266
|
+
return { ok: false, error: Err.notFound(resolved) };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const stat = statSync(resolved);
|
|
270
|
+
if (!stat.isDirectory()) {
|
|
271
|
+
return { ok: false, error: Err.notADirectory(resolved) };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
let entries = readdirSync(resolved, { withFileTypes: true });
|
|
276
|
+
|
|
277
|
+
if (input.glob) {
|
|
278
|
+
const pattern = input.glob;
|
|
279
|
+
entries = entries.filter((e) => minimatch(e.name, pattern));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Sort: directories first (alphabetical), then files (alphabetical)
|
|
283
|
+
const dirs = entries
|
|
284
|
+
.filter((e) => e.isDirectory())
|
|
285
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
286
|
+
const files = entries
|
|
287
|
+
.filter((e) => !e.isDirectory())
|
|
288
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
289
|
+
const sorted = [...dirs, ...files];
|
|
290
|
+
|
|
291
|
+
const MAX_ENTRIES = 500;
|
|
292
|
+
const truncated = sorted.length > MAX_ENTRIES;
|
|
293
|
+
const visible = sorted.slice(0, MAX_ENTRIES);
|
|
294
|
+
|
|
295
|
+
const lines = visible.map((entry) => {
|
|
296
|
+
if (entry.isDirectory()) {
|
|
297
|
+
return `${entry.name}/`;
|
|
298
|
+
}
|
|
299
|
+
if (entry.isSymbolicLink()) {
|
|
300
|
+
return `${entry.name}@`;
|
|
301
|
+
}
|
|
302
|
+
const fileStat = lstatSync(join(resolved, entry.name));
|
|
303
|
+
return `${entry.name} ${formatSize(fileStat.size)}`;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (truncated) {
|
|
307
|
+
lines.push(
|
|
308
|
+
`\n... and ${sorted.length - MAX_ENTRIES} more entries (use glob to filter)`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return { ok: true, value: { listing: lines.join("\n") } };
|
|
313
|
+
} catch (err) {
|
|
314
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
315
|
+
return { ok: false, error: Err.ioError(resolved, msg) };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Helpers
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
function formatSize(bytes: number): string {
|
|
325
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
326
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
327
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
240
328
|
}
|
|
@@ -78,3 +78,20 @@ export interface EditOutput {
|
|
|
78
78
|
export type EditResult =
|
|
79
79
|
| { ok: true; value: EditOutput }
|
|
80
80
|
| { ok: false; error: FsError };
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// List
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
export interface ListInput {
|
|
87
|
+
path: string;
|
|
88
|
+
glob?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ListOutput {
|
|
92
|
+
listing: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type ListResult =
|
|
96
|
+
| { ok: true; value: ListOutput }
|
|
97
|
+
| { ok: false; error: FsError };
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export const MAX_OUTPUT_LENGTH = 20_000;
|
|
7
|
+
|
|
8
|
+
/** Tracks temp files created for truncated shell output so they can be cleaned up on shutdown. */
|
|
9
|
+
const trackedTempFiles = new Set<string>();
|
|
10
|
+
|
|
11
|
+
/** Remove all tracked truncated-output temp files. Safe to call multiple times. */
|
|
12
|
+
export function cleanupShellOutputTempFiles(): void {
|
|
13
|
+
for (const filePath of trackedTempFiles) {
|
|
14
|
+
try {
|
|
15
|
+
unlinkSync(filePath);
|
|
16
|
+
} catch {
|
|
17
|
+
// File may already be gone — ignore.
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
trackedTempFiles.clear();
|
|
21
|
+
}
|
|
2
22
|
|
|
3
23
|
export interface ShellOutputResult {
|
|
4
24
|
content: string;
|
|
@@ -33,7 +53,16 @@ export function formatShellOutput(
|
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
if (output.length > MAX_OUTPUT_LENGTH) {
|
|
36
|
-
|
|
56
|
+
let fullOutputPath: string | undefined;
|
|
57
|
+
try {
|
|
58
|
+
fullOutputPath = join(tmpdir(), `vellum-shell-output-${randomUUID()}.txt`);
|
|
59
|
+
writeFileSync(fullOutputPath, output, { encoding: "utf-8", mode: 0o600 });
|
|
60
|
+
trackedTempFiles.add(fullOutputPath);
|
|
61
|
+
} catch {
|
|
62
|
+
fullOutputPath = undefined;
|
|
63
|
+
}
|
|
64
|
+
const fileAttr = fullOutputPath ? ` file="${fullOutputPath}"` : "";
|
|
65
|
+
const msg = `<output_truncated limit="20K"${fileAttr} />`;
|
|
37
66
|
output = output.slice(0, MAX_OUTPUT_LENGTH) + `\n${msg}`;
|
|
38
67
|
statusParts.push(msg);
|
|
39
68
|
}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
2
2
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
3
|
+
import { resolveSubagentId } from "./resolve.js";
|
|
3
4
|
|
|
4
5
|
export async function executeSubagentAbort(
|
|
5
6
|
input: Record<string, unknown>,
|
|
6
7
|
context: ToolContext,
|
|
7
8
|
): Promise<ToolExecutionResult> {
|
|
8
|
-
const subagentId = input
|
|
9
|
+
const subagentId = resolveSubagentId(input, context);
|
|
10
|
+
if (!subagentId && input.label) {
|
|
11
|
+
return {
|
|
12
|
+
content: `No subagent found with label "${input.label as string}".`,
|
|
13
|
+
isError: true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
9
16
|
if (!subagentId) {
|
|
10
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
content: '"subagent_id" or "label" is required.',
|
|
19
|
+
isError: true,
|
|
20
|
+
};
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
const manager = getSubagentManager();
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
2
2
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
3
|
+
import { resolveSubagentId } from "./resolve.js";
|
|
3
4
|
|
|
4
5
|
export async function executeSubagentMessage(
|
|
5
6
|
input: Record<string, unknown>,
|
|
6
7
|
context: ToolContext,
|
|
7
8
|
): Promise<ToolExecutionResult> {
|
|
8
|
-
const subagentId = input
|
|
9
|
+
const subagentId = resolveSubagentId(input, context);
|
|
9
10
|
const content = input.content as string;
|
|
10
11
|
|
|
12
|
+
if (!subagentId && input.label) {
|
|
13
|
+
return {
|
|
14
|
+
content: `No subagent found with label "${input.label as string}".`,
|
|
15
|
+
isError: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
11
18
|
if (!subagentId || !content) {
|
|
12
19
|
return {
|
|
13
|
-
content: '
|
|
20
|
+
content: '"subagent_id" or "label", and "content" are required.',
|
|
14
21
|
isError: true,
|
|
15
22
|
};
|
|
16
23
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { RiskLevel } from "../../permissions/types.js";
|
|
2
|
+
import type { ToolDefinition } from "../../providers/types.js";
|
|
3
|
+
import { getSubagentManager } from "../../subagent/index.js";
|
|
4
|
+
import { registerTool } from "../registry.js";
|
|
5
|
+
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
6
|
+
|
|
7
|
+
export async function executeSubagentNotifyParent(
|
|
8
|
+
input: Record<string, unknown>,
|
|
9
|
+
context: ToolContext,
|
|
10
|
+
): Promise<ToolExecutionResult> {
|
|
11
|
+
const message = input.message as string;
|
|
12
|
+
const urgency = (input.urgency as string) || "info";
|
|
13
|
+
|
|
14
|
+
if (!message) {
|
|
15
|
+
return { content: '"message" is required.', isError: true };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const manager = getSubagentManager();
|
|
19
|
+
const sent = manager.notifyParent(context.conversationId, message, urgency);
|
|
20
|
+
|
|
21
|
+
if (!sent) {
|
|
22
|
+
return {
|
|
23
|
+
content:
|
|
24
|
+
"Could not notify parent. This tool is only available to subagents.",
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
content: JSON.stringify({ sent: true, urgency }),
|
|
31
|
+
isError: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class NotifyParentTool implements Tool {
|
|
36
|
+
name = "notify_parent";
|
|
37
|
+
description =
|
|
38
|
+
"Send a notification to the parent conversation. Use this for important findings, when you're blocked, or when you have preliminary results the parent should know about. Do not overuse — notify for significant findings, not after every tool call.";
|
|
39
|
+
category = "orchestration";
|
|
40
|
+
defaultRiskLevel = RiskLevel.Low;
|
|
41
|
+
|
|
42
|
+
getDefinition(): ToolDefinition {
|
|
43
|
+
return {
|
|
44
|
+
name: this.name,
|
|
45
|
+
description: this.description,
|
|
46
|
+
input_schema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
message: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "The notification content for the parent.",
|
|
52
|
+
},
|
|
53
|
+
urgency: {
|
|
54
|
+
type: "string",
|
|
55
|
+
enum: ["info", "important", "blocked"],
|
|
56
|
+
description:
|
|
57
|
+
"'info' for progress updates, 'important' for key findings, 'blocked' when you need guidance.",
|
|
58
|
+
},
|
|
59
|
+
activity: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description:
|
|
62
|
+
"Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ["message", "activity"],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async execute(
|
|
71
|
+
input: Record<string, unknown>,
|
|
72
|
+
context: ToolContext,
|
|
73
|
+
): Promise<ToolExecutionResult> {
|
|
74
|
+
return executeSubagentNotifyParent(input, context);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const notifyParentTool = new NotifyParentTool();
|
|
79
|
+
registerTool(notifyParentTool);
|