vellum 0.2.1 → 0.2.2
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 +15 -2
- package/bun.lock +5 -2
- package/package.json +4 -2
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +299 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +62 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +246 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +163 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +28 -4
- package/src/daemon/ipc-contract.ts +133 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/http-server.ts +108 -20
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +5 -10
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +220 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +77 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
package/src/subagent/manager.ts
CHANGED
|
@@ -52,10 +52,18 @@ interface ManagedSubagent {
|
|
|
52
52
|
parentSendToClient: (msg: ServerMessage) => void;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
export interface SubagentNotificationInfo {
|
|
56
|
+
subagentId: string;
|
|
57
|
+
label: string;
|
|
58
|
+
status: 'completed' | 'failed' | 'aborted';
|
|
59
|
+
error?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
export type ParentNotifyCallback = (
|
|
56
63
|
parentSessionId: string,
|
|
57
64
|
message: string,
|
|
58
65
|
sendToClient: (msg: ServerMessage) => void,
|
|
66
|
+
notification: SubagentNotificationInfo,
|
|
59
67
|
) => void;
|
|
60
68
|
|
|
61
69
|
export class SubagentManager {
|
|
@@ -221,6 +229,8 @@ export class SubagentManager {
|
|
|
221
229
|
await managed.session.runAgentLoop(objective, messageId, onEvent);
|
|
222
230
|
|
|
223
231
|
// Agent loop completed successfully.
|
|
232
|
+
// Copy usage stats from the session before sending status (which includes usage).
|
|
233
|
+
managed.state.usage = { ...managed.session.usageStats };
|
|
224
234
|
// Only update state + notify if still non-terminal (guards against abort race).
|
|
225
235
|
if (!TERMINAL_STATUSES.has(managed.state.status)) {
|
|
226
236
|
managed.state.completedAt = Date.now();
|
|
@@ -235,6 +245,7 @@ export class SubagentManager {
|
|
|
235
245
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
236
246
|
managed.state.error = errorMsg;
|
|
237
247
|
managed.state.completedAt = Date.now();
|
|
248
|
+
managed.state.usage = { ...managed.session.usageStats };
|
|
238
249
|
|
|
239
250
|
// Only update status if not already terminal (e.g. aborted).
|
|
240
251
|
if (!TERMINAL_STATUSES.has(managed.state.status)) {
|
|
@@ -267,16 +278,28 @@ export class SubagentManager {
|
|
|
267
278
|
managed.session.abort();
|
|
268
279
|
managed.state.completedAt = Date.now();
|
|
269
280
|
if (parentSendToClient) {
|
|
270
|
-
|
|
271
|
-
//
|
|
272
|
-
//
|
|
281
|
+
// Route the status update through the stored parent sender so the
|
|
282
|
+
// owning session's UI chip updates, even when the abort comes from a
|
|
283
|
+
// different socket (e.g. after thread switching). Fall back to the
|
|
284
|
+
// caller-provided sender if no stored sender exists.
|
|
285
|
+
const statusSender = managed.parentSendToClient ?? parentSendToClient;
|
|
286
|
+
this.setStatus(subagentId, 'aborted', statusSender);
|
|
287
|
+
// Notify parent that the subagent was explicitly aborted — tell it NOT to re-spawn.
|
|
288
|
+
// Skip when the parent LLM itself called subagent_abort (it already has the tool result).
|
|
273
289
|
if (this.onSubagentFinished && !options?.suppressNotification) {
|
|
274
290
|
const label = managed.state.config.label;
|
|
291
|
+
const message =
|
|
292
|
+
`[Subagent "${label}" was explicitly aborted]\n\n` +
|
|
293
|
+
`This subagent was cancelled on purpose. Do NOT re-spawn or retry it.`;
|
|
275
294
|
try {
|
|
295
|
+
// Use the managed subagent's stored parentSendToClient so the
|
|
296
|
+
// notification routes to the parent session's socket, not the
|
|
297
|
+
// aborting socket (which may be a different thread after switching).
|
|
276
298
|
this.onSubagentFinished(
|
|
277
299
|
managed.state.config.parentSessionId,
|
|
278
|
-
|
|
279
|
-
parentSendToClient,
|
|
300
|
+
message,
|
|
301
|
+
managed.parentSendToClient,
|
|
302
|
+
{ subagentId, label, status: 'aborted' },
|
|
280
303
|
);
|
|
281
304
|
} catch (err) {
|
|
282
305
|
log.error({ subagentId, err }, 'Failed to notify parent about abort');
|
|
@@ -460,16 +483,25 @@ export class SubagentManager {
|
|
|
460
483
|
if (outcome === 'completed') {
|
|
461
484
|
message =
|
|
462
485
|
`[Subagent "${config.label}" completed]\n\n` +
|
|
463
|
-
`Use subagent_read with subagent_id "${config.id}" to retrieve the full output
|
|
486
|
+
`Use subagent_read with subagent_id "${config.id}" to retrieve the full output.\n` +
|
|
487
|
+
`Do NOT re-spawn this subagent — just read and share the results.`;
|
|
464
488
|
} else {
|
|
465
489
|
const error = managed.state.error ?? 'Unknown error';
|
|
466
490
|
message =
|
|
467
491
|
`[Subagent "${config.label}" failed]\n\n` +
|
|
468
|
-
`Error: ${error}
|
|
492
|
+
`Error: ${error}\n` +
|
|
493
|
+
`Do NOT re-spawn or retry this subagent unless the user explicitly asks.`;
|
|
469
494
|
}
|
|
470
495
|
|
|
496
|
+
const notification: SubagentNotificationInfo = {
|
|
497
|
+
subagentId: config.id,
|
|
498
|
+
label: config.label,
|
|
499
|
+
status: outcome,
|
|
500
|
+
...(outcome === 'failed' ? { error: managed.state.error ?? 'Unknown error' } : {}),
|
|
501
|
+
};
|
|
502
|
+
|
|
471
503
|
try {
|
|
472
|
-
this.onSubagentFinished(config.parentSessionId, message, parentSendToClient);
|
|
504
|
+
this.onSubagentFinished(config.parentSessionId, message, parentSendToClient, notification);
|
|
473
505
|
} catch (err) {
|
|
474
506
|
log.error({ subagentId: config.id, err }, 'Failed to notify parent session');
|
|
475
507
|
}
|
|
@@ -12,6 +12,9 @@ import { getProfilePolicy } from './worker-backend.js';
|
|
|
12
12
|
|
|
13
13
|
const log = getLogger('swarm-backend-claude-code');
|
|
14
14
|
|
|
15
|
+
const MAX_CLAUDE_CODE_DEPTH = 1;
|
|
16
|
+
const DEPTH_ENV_VAR = 'VELLUM_CLAUDE_CODE_DEPTH';
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Create a Claude Code worker backend that enforces profile-based tool policies.
|
|
17
20
|
* Uses the Claude Agent SDK to run autonomous worker tasks.
|
|
@@ -28,6 +31,7 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
|
|
|
28
31
|
|
|
29
32
|
async runTask(input: SwarmWorkerBackendInput) {
|
|
30
33
|
const start = Date.now();
|
|
34
|
+
const stderrLines: string[] = [];
|
|
31
35
|
try {
|
|
32
36
|
const { query } = await import('@anthropic-ai/claude-agent-sdk');
|
|
33
37
|
const config = getConfig();
|
|
@@ -49,6 +53,22 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
|
|
|
49
53
|
return { behavior: 'allow' as const };
|
|
50
54
|
};
|
|
51
55
|
|
|
56
|
+
// Enforce nesting depth limit
|
|
57
|
+
const currentDepth = parseInt(process.env[DEPTH_ENV_VAR] ?? '0', 10);
|
|
58
|
+
if (currentDepth >= MAX_CLAUDE_CODE_DEPTH) {
|
|
59
|
+
log.warn({ currentDepth, max: MAX_CLAUDE_CODE_DEPTH }, 'Swarm worker nesting depth exceeded');
|
|
60
|
+
return { success: false, output: `Nesting depth exceeded (depth ${currentDepth}, max ${MAX_CLAUDE_CODE_DEPTH})`, failureReason: 'backend_unavailable' as const, durationMs: Date.now() - start };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Strip the SDK's nesting guard but set our own depth counter.
|
|
64
|
+
const subprocessEnv: Record<string, string | undefined> = {
|
|
65
|
+
...process.env,
|
|
66
|
+
ANTHROPIC_API_KEY: apiKey,
|
|
67
|
+
[DEPTH_ENV_VAR]: String(currentDepth + 1),
|
|
68
|
+
};
|
|
69
|
+
delete subprocessEnv.CLAUDECODE;
|
|
70
|
+
delete subprocessEnv.CLAUDE_CODE_ENTRYPOINT;
|
|
71
|
+
|
|
52
72
|
const conversation = query({
|
|
53
73
|
prompt: input.prompt,
|
|
54
74
|
options: {
|
|
@@ -57,20 +77,48 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
|
|
|
57
77
|
canUseTool,
|
|
58
78
|
permissionMode: 'default',
|
|
59
79
|
maxTurns: 30,
|
|
60
|
-
env:
|
|
80
|
+
env: subprocessEnv,
|
|
81
|
+
stderr: (data: string) => {
|
|
82
|
+
const trimmed = data.trimEnd();
|
|
83
|
+
if (trimmed) {
|
|
84
|
+
stderrLines.push(trimmed);
|
|
85
|
+
log.debug({ stderr: trimmed }, 'Swarm worker subprocess stderr');
|
|
86
|
+
}
|
|
87
|
+
},
|
|
61
88
|
},
|
|
62
89
|
});
|
|
63
90
|
|
|
64
91
|
let resultText = '';
|
|
92
|
+
let hasError = false;
|
|
65
93
|
for await (const message of conversation) {
|
|
66
94
|
if (input.signal?.aborted) break;
|
|
67
|
-
if (message.type === 'assistant'
|
|
68
|
-
|
|
69
|
-
|
|
95
|
+
if (message.type === 'assistant') {
|
|
96
|
+
if (message.error) {
|
|
97
|
+
log.error({ error: message.error, sessionId: message.session_id }, 'Swarm worker assistant message error');
|
|
98
|
+
hasError = true;
|
|
99
|
+
resultText += `\n[Claude Code error: ${message.error}]`;
|
|
100
|
+
}
|
|
101
|
+
if (message.message?.content) {
|
|
102
|
+
for (const block of message.message.content) {
|
|
103
|
+
if (block.type === 'text') resultText += block.text;
|
|
104
|
+
}
|
|
70
105
|
}
|
|
71
106
|
} else if (message.type === 'result') {
|
|
72
|
-
if (message.subtype === 'success'
|
|
73
|
-
|
|
107
|
+
if (message.subtype === 'success') {
|
|
108
|
+
log.info({ numTurns: message.num_turns, durationMs: message.duration_ms, costUsd: message.total_cost_usd }, 'Swarm worker completed');
|
|
109
|
+
if (message.result && !resultText) {
|
|
110
|
+
resultText = message.result;
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
hasError = true;
|
|
114
|
+
const errors = message.errors ?? [];
|
|
115
|
+
const denials = message.permission_denials ?? [];
|
|
116
|
+
log.error({ subtype: message.subtype, errors, permissionDenials: denials.length, numTurns: message.num_turns, durationMs: message.duration_ms }, 'Swarm worker session failed');
|
|
117
|
+
|
|
118
|
+
const parts: string[] = [`[${message.subtype}] (${message.num_turns} turns, ${(message.duration_ms / 1000).toFixed(1)}s)`];
|
|
119
|
+
if (errors.length > 0) parts.push(`Errors: ${errors.join('; ')}`);
|
|
120
|
+
if (denials.length > 0) parts.push(`Permission denied: ${denials.map(d => d.tool_name).join(', ')}`);
|
|
121
|
+
resultText += `\n${parts.join('\n')}`;
|
|
74
122
|
}
|
|
75
123
|
}
|
|
76
124
|
}
|
|
@@ -80,10 +128,17 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
|
|
|
80
128
|
return { success: false, output: 'Cancelled (aborted)', failureReason: 'cancelled' as const, durationMs: Date.now() - start };
|
|
81
129
|
}
|
|
82
130
|
|
|
83
|
-
return { success:
|
|
131
|
+
return { success: !hasError, output: resultText || 'Completed', durationMs: Date.now() - start };
|
|
84
132
|
} catch (err) {
|
|
85
|
-
const
|
|
86
|
-
|
|
133
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
134
|
+
const recentStderr = stderrLines.slice(-20);
|
|
135
|
+
log.error({ err, stderrTail: recentStderr }, 'Swarm worker execution failed');
|
|
136
|
+
|
|
137
|
+
const parts = [errMessage];
|
|
138
|
+
if (recentStderr.length > 0) {
|
|
139
|
+
parts.push(`\nSubprocess stderr (last ${recentStderr.length} lines):\n${recentStderr.join('\n')}`);
|
|
140
|
+
}
|
|
141
|
+
return { success: false, output: parts.join(''), failureReason: 'backend_unavailable' as const, durationMs: Date.now() - start };
|
|
87
142
|
}
|
|
88
143
|
},
|
|
89
144
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SwarmRole, SwarmTaskResult } from './types.js';
|
|
2
|
+
import { truncate } from '../util/truncate.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Build a role-specific worker prompt for a swarm task.
|
|
@@ -70,7 +71,7 @@ export function parseWorkerOutput(raw: string): Pick<SwarmTaskResult, 'summary'
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
return {
|
|
73
|
-
summary: raw
|
|
74
|
+
summary: truncate(raw, 500, ''),
|
|
74
75
|
artifacts: [],
|
|
75
76
|
issues: [],
|
|
76
77
|
nextSteps: [],
|
package/src/tasks/SPEC.md
CHANGED
|
@@ -87,18 +87,23 @@ Each task run creates a new conversation thread with `threadType: 'background'`.
|
|
|
87
87
|
|
|
88
88
|
### Lifecycle
|
|
89
89
|
|
|
90
|
-
1. **
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
2. **
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
1. **Preflight**: The client requests a permission preflight for the work item.
|
|
91
|
+
The daemon classifies risk for each required tool and returns the permission
|
|
92
|
+
set. The client displays an approval dialog; approved tools are stored on
|
|
93
|
+
the work item.
|
|
94
|
+
2. **Start**: The daemon creates a `background` conversation, substitutes
|
|
95
|
+
template placeholders, sets up ephemeral permission rules for the approved
|
|
96
|
+
tools, and processes the rendered prompt through a daemon `Session`. Status
|
|
97
|
+
updates are broadcast to all connected clients via `work_item_status_changed`
|
|
98
|
+
and `tasks_changed` IPC messages.
|
|
99
|
+
3. **Completion**: When the session finishes, the work item transitions to
|
|
100
|
+
`awaiting_review` (on success) or `failed` (on error). The daemon broadcasts
|
|
101
|
+
the final status to all clients.
|
|
102
|
+
4. **Visibility**: Background conversations are excluded from the default thread
|
|
103
|
+
list (existing behavior in `conversation-store.ts`). Clients can query for
|
|
104
|
+
them explicitly to surface task results in a dedicated UI.
|
|
105
|
+
|
|
106
|
+
**Why background conversations:** Reuses the existing `threadType: 'background'`
|
|
102
107
|
infrastructure. Task runs don't interrupt the user's current conversation, and
|
|
103
108
|
clients can choose how and when to display results (toast, panel, separate
|
|
104
109
|
tab).
|
|
@@ -107,27 +112,28 @@ tab).
|
|
|
107
112
|
|
|
108
113
|
## 4. Safety Invariants
|
|
109
114
|
|
|
110
|
-
- **
|
|
111
|
-
|
|
115
|
+
- **Explicit trigger required**: Task runs are triggered either by an explicit
|
|
116
|
+
user action (UI button press, API call) or by a user-configured schedule
|
|
117
|
+
(`run_task:<task_id>` via the scheduler).
|
|
112
118
|
- **Ephemeral permission bundles**: If a task is configured with tool access,
|
|
113
119
|
the permission grants are scoped to the single run and discarded afterward.
|
|
114
120
|
No persistent allowlist entries are created on behalf of a task.
|
|
115
|
-
- **High-risk tools
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
- **High-risk tools require upfront approval**: Tools classified as
|
|
122
|
+
`RiskLevel.High` (destructive shell commands, private-network fetches, etc.)
|
|
123
|
+
are surfaced in the preflight dialog so the user can explicitly approve or
|
|
124
|
+
deny them before execution begins. During the run itself, approved tools
|
|
125
|
+
(including high-risk ones) execute without further prompting.
|
|
119
126
|
|
|
120
127
|
---
|
|
121
128
|
|
|
122
|
-
## 5.
|
|
129
|
+
## 5. Implementation Notes
|
|
123
130
|
|
|
124
|
-
|
|
131
|
+
The implementation is complete. Key modules:
|
|
125
132
|
|
|
126
|
-
|
|
|
127
|
-
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
|
133
|
-
| 5 | IPC + macOS integration | Wire up IPC message types; macOS client displays task run results. |
|
|
133
|
+
| Module | What it delivers |
|
|
134
|
+
|--------|------------------|
|
|
135
|
+
| `task-store.ts` | `tasks` and `task_runs` tables, CRUD functions. |
|
|
136
|
+
| `task-runner.ts` | `runTask()` — creates background conversation, renders template, processes through daemon Session. |
|
|
137
|
+
| `ephemeral-permissions.ts` | Scoped permission rules for the duration of a single task run. |
|
|
138
|
+
| `work-items.ts` (daemon handler) | IPC handlers for preflight, run, cancel, and status queries. |
|
|
139
|
+
| Bundled skill (`tasks/`) | Tool definitions (`task_save`, `task_run`, `task_list`, `task_delete`, `task_list_*`) for the LLM. |
|
|
@@ -21,19 +21,28 @@ export function clearTaskRunRules(taskRunId: string): void {
|
|
|
21
21
|
/**
|
|
22
22
|
* Build ephemeral TrustRule entries from a task's required_tools list.
|
|
23
23
|
*
|
|
24
|
-
* Each rule allows the specified tool with a wildcard pattern scoped
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* Each rule allows the specified tool with a wildcard pattern scoped
|
|
25
|
+
* globally ('everywhere'). The scope is intentionally broad because the
|
|
26
|
+
* session's workingDir (sandbox path like ~/.vellum/workspace) differs
|
|
27
|
+
* from process.cwd() — using a directory-scoped rule would fail
|
|
28
|
+
* matchesScope() and silently miss. Priority is set to 75 — above
|
|
29
|
+
* default rules (50) so pre-approved tools aren't shadowed by default
|
|
30
|
+
* `ask` rules (which would trigger prompting and auto-deny in
|
|
31
|
+
* non-interactive task runs), but below user rules (100) so user deny
|
|
32
|
+
* rules still take precedence. `allowHighRisk` is set because task runs
|
|
33
|
+
* execute asynchronously without interactive confirmation — the client
|
|
34
|
+
* pre-approves tools via the preflight flow before execution begins,
|
|
35
|
+
* so there is no interactive prompt during the run itself.
|
|
28
36
|
*/
|
|
29
|
-
export function buildTaskRules(taskRunId: string, requiredTools: string[],
|
|
37
|
+
export function buildTaskRules(taskRunId: string, requiredTools: string[], _workingDir: string): TrustRule[] {
|
|
30
38
|
return requiredTools.map((tool) => ({
|
|
31
39
|
id: `ephemeral:${taskRunId}:${tool}`,
|
|
32
40
|
tool,
|
|
33
41
|
pattern: '**',
|
|
34
|
-
scope:
|
|
42
|
+
scope: 'everywhere',
|
|
35
43
|
decision: 'allow' as const,
|
|
36
|
-
|
|
44
|
+
allowHighRisk: true,
|
|
45
|
+
priority: 75,
|
|
37
46
|
createdAt: Date.now(),
|
|
38
47
|
principalKind: 'task',
|
|
39
48
|
principalId: taskRunId,
|
|
@@ -3,6 +3,8 @@ import { getDb } from '../memory/db.js';
|
|
|
3
3
|
import { messages as messagesTable } from '../memory/schema.js';
|
|
4
4
|
import { createTask } from './task-store.js';
|
|
5
5
|
import type { Task } from './task-store.js';
|
|
6
|
+
import { truncate } from '../util/truncate.js';
|
|
7
|
+
import { sanitizeToolList } from './tool-sanitizer.js';
|
|
6
8
|
|
|
7
9
|
/** Output schema for the task compiler. */
|
|
8
10
|
export interface CompiledTask {
|
|
@@ -44,8 +46,8 @@ export function compileTaskFromConversation(conversationId: string): CompiledTas
|
|
|
44
46
|
// Extract user message text content
|
|
45
47
|
const userText = extractTextContent(firstUserMsg.content);
|
|
46
48
|
|
|
47
|
-
// Extract unique tool names from assistant messages
|
|
48
|
-
const requiredTools = extractToolNames(msgs);
|
|
49
|
+
// Extract unique tool names from assistant messages.
|
|
50
|
+
const requiredTools = sanitizeToolList(extractToolNames(msgs));
|
|
49
51
|
|
|
50
52
|
// Build template with placeholder substitutions
|
|
51
53
|
const { template, properties } = buildTemplate(userText);
|
|
@@ -193,6 +195,5 @@ function buildTemplate(text: string): {
|
|
|
193
195
|
function deriveTitle(text: string): string {
|
|
194
196
|
// Take the first line and trim whitespace
|
|
195
197
|
const firstLine = text.split('\n')[0].trim();
|
|
196
|
-
|
|
197
|
-
return firstLine.slice(0, 57) + '...';
|
|
198
|
+
return truncate(firstLine, 60);
|
|
198
199
|
}
|
package/src/tasks/task-runner.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { getLogger } from '../util/logger.js';
|
|
|
2
2
|
import { createConversation } from '../memory/conversation-store.js';
|
|
3
3
|
import { getTask, createTaskRun, updateTaskRun } from './task-store.js';
|
|
4
4
|
import { buildTaskRules, setTaskRunRules, clearTaskRunRules } from './ephemeral-permissions.js';
|
|
5
|
+
import { sanitizeToolList } from './tool-sanitizer.js';
|
|
5
6
|
|
|
6
7
|
const log = getLogger('task-runner');
|
|
7
8
|
|
|
@@ -9,6 +10,8 @@ export interface TaskRunOptions {
|
|
|
9
10
|
taskId: string;
|
|
10
11
|
inputs?: Record<string, string>;
|
|
11
12
|
workingDir: string;
|
|
13
|
+
/** Pre-approved tools from the permission preflight flow. When set, only these tools get ephemeral allow rules. */
|
|
14
|
+
approvedTools?: string[];
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export interface TaskRunResult {
|
|
@@ -32,7 +35,7 @@ export function renderTemplate(template: string, inputs: Record<string, string>)
|
|
|
32
35
|
*/
|
|
33
36
|
export async function runTask(
|
|
34
37
|
opts: TaskRunOptions,
|
|
35
|
-
processMessage: (conversationId: string, message: string) => Promise<void>,
|
|
38
|
+
processMessage: (conversationId: string, message: string, taskRunId: string) => Promise<void>,
|
|
36
39
|
): Promise<TaskRunResult> {
|
|
37
40
|
const task = getTask(opts.taskId);
|
|
38
41
|
if (!task) {
|
|
@@ -47,9 +50,11 @@ export async function runTask(
|
|
|
47
50
|
memoryScopeId: `task:${task.id}`,
|
|
48
51
|
});
|
|
49
52
|
|
|
50
|
-
// Build and register ephemeral permission rules
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
+
// Build and register ephemeral permission rules. If the user pre-approved
|
|
54
|
+
// specific tools via the preflight flow, use those instead of all requiredTools.
|
|
55
|
+
const requiredTools = sanitizeToolList(task.requiredTools ? JSON.parse(task.requiredTools) : []);
|
|
56
|
+
const toolsForRules = opts.approvedTools ? sanitizeToolList(opts.approvedTools) : requiredTools;
|
|
57
|
+
const rules = buildTaskRules(run.id, toolsForRules, opts.workingDir);
|
|
53
58
|
setTaskRunRules(run.id, rules);
|
|
54
59
|
|
|
55
60
|
try {
|
|
@@ -58,7 +63,7 @@ export async function runTask(
|
|
|
58
63
|
updateTaskRun(run.id, { status: 'running', startedAt: Date.now() });
|
|
59
64
|
|
|
60
65
|
log.info({ taskId: task.id, taskRunId: run.id, conversationId: conversation.id }, 'Executing task');
|
|
61
|
-
await processMessage(conversation.id, renderedTemplate);
|
|
66
|
+
await processMessage(conversation.id, renderedTemplate, run.id);
|
|
62
67
|
|
|
63
68
|
updateTaskRun(run.id, { status: 'completed', finishedAt: Date.now() });
|
|
64
69
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSchedule } from '../schedule/schedule-store.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Create a schedule that runs a task on a cron expression.
|
|
4
|
+
* Create a recurrence schedule that runs a task on a cron or RRULE expression.
|
|
5
5
|
* The scheduler detects the `run_task:<taskId>` message format
|
|
6
6
|
* and delegates to runTask() instead of processMessage().
|
|
7
7
|
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getTool, getAllTools } from '../tools/registry.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deduplicate and sort a list of tool names, validating against the live
|
|
5
|
+
* tool registry. Unknown tool names are logged at warn level but kept —
|
|
6
|
+
* they may refer to skill tools that will be loaded at runtime.
|
|
7
|
+
*
|
|
8
|
+
* The returned array is deterministic: sorted alphabetically with no duplicates.
|
|
9
|
+
*/
|
|
10
|
+
export function sanitizeToolList(tools: string[]): string[] {
|
|
11
|
+
const seen = new Set<string>();
|
|
12
|
+
|
|
13
|
+
for (const tool of tools) {
|
|
14
|
+
if (!tool || typeof tool !== 'string') continue;
|
|
15
|
+
seen.add(tool);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return [...seen].sort();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get all registered tool names from the live tool registry.
|
|
23
|
+
* Used as the fallback when a task/work-item has no explicit requiredTools.
|
|
24
|
+
*/
|
|
25
|
+
export function getRegisteredToolNames(): string[] {
|
|
26
|
+
return getAllTools()
|
|
27
|
+
.filter((t) => t.executionMode !== 'proxy' && t.origin !== 'skill')
|
|
28
|
+
.map((t) => t.name)
|
|
29
|
+
.sort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Look up the human-readable description for a tool from the registry. */
|
|
33
|
+
export function getToolDescription(tool: string): string {
|
|
34
|
+
const registered = getTool(tool);
|
|
35
|
+
return registered?.description ?? tool;
|
|
36
|
+
}
|
|
@@ -214,7 +214,7 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
214
214
|
|
|
215
215
|
const limit = Math.min(params.limit ?? DEFAULT_LIMIT, MAX_RESULTS);
|
|
216
216
|
const stmt = raw.prepare(
|
|
217
|
-
`SELECT a.id, a.
|
|
217
|
+
`SELECT a.id, a.original_filename, a.mime_type, a.size_bytes, a.kind, a.thumbnail_base64, a.created_at
|
|
218
218
|
FROM attachments a
|
|
219
219
|
WHERE ${whereParts.join(' AND ')}
|
|
220
220
|
ORDER BY a.created_at DESC
|
|
@@ -223,21 +223,21 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
223
223
|
|
|
224
224
|
const rows = stmt.all(...bindValues, limit) as Array<{
|
|
225
225
|
id: string;
|
|
226
|
-
assistant_id: string;
|
|
227
226
|
original_filename: string;
|
|
228
227
|
mime_type: string;
|
|
229
228
|
size_bytes: number;
|
|
230
229
|
kind: string;
|
|
230
|
+
thumbnail_base64: string | null;
|
|
231
231
|
created_at: number;
|
|
232
232
|
}>;
|
|
233
233
|
|
|
234
234
|
return rows.map((r) => ({
|
|
235
235
|
id: r.id,
|
|
236
|
-
assistantId: r.assistant_id,
|
|
237
236
|
originalFilename: r.original_filename,
|
|
238
237
|
mimeType: r.mime_type,
|
|
239
238
|
sizeBytes: r.size_bytes,
|
|
240
239
|
kind: r.kind,
|
|
240
|
+
thumbnailBase64: r.thumbnail_base64,
|
|
241
241
|
createdAt: r.created_at,
|
|
242
242
|
}));
|
|
243
243
|
}
|
|
@@ -249,11 +249,11 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
249
249
|
const query = db
|
|
250
250
|
.select({
|
|
251
251
|
id: attachments.id,
|
|
252
|
-
assistantId: attachments.assistantId,
|
|
253
252
|
originalFilename: attachments.originalFilename,
|
|
254
253
|
mimeType: attachments.mimeType,
|
|
255
254
|
sizeBytes: attachments.sizeBytes,
|
|
256
255
|
kind: attachments.kind,
|
|
256
|
+
thumbnailBase64: attachments.thumbnailBase64,
|
|
257
257
|
createdAt: attachments.createdAt,
|
|
258
258
|
})
|
|
259
259
|
.from(attachments)
|