vellum 0.2.0 → 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 +161 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/app-bundler.test.ts +12 -33
- 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 +5 -8
- 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 +454 -0
- 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-state.test.ts +133 -0
- package/src/__tests__/call-store.test.ts +691 -0
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
- 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 +348 -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__/doordash-session.test.ts +9 -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 +96 -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 +17 -10
- 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 +222 -0
- package/src/__tests__/run-orchestrator.test.ts +7 -7
- package/src/__tests__/runtime-attachment-metadata.test.ts +19 -20
- package/src/__tests__/runtime-runs-http.test.ts +5 -23
- package/src/__tests__/runtime-runs.test.ts +11 -11
- 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 +89 -16
- 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 +273 -2
- 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 +403 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +141 -2
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +35 -14
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +48 -0
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +390 -0
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-state.ts +64 -0
- package/src/calls/call-store.ts +416 -0
- package/src/calls/relay-server.ts +335 -0
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +34 -0
- package/src/calls/twilio-provider.ts +173 -0
- package/src/calls/twilio-routes.ts +250 -0
- package/src/calls/types.ts +37 -0
- package/src/calls/voice-provider.ts +14 -0
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +256 -25
- 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 +163 -0
- 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.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -24
- 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 +44 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +218 -1
- package/src/config/system-prompt.ts +100 -6
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +5 -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 +192 -4
- 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 -271
- 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 +495 -39
- package/src/daemon/ipc-contract-inventory.json +40 -4
- package/src/daemon/ipc-contract.ts +185 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +48 -5
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +144 -29
- 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 +222 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1365
- 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 -1151
- package/src/media/gemini-image-service.ts +1 -1
- 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 +362 -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 +65 -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 +277 -25
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/routes/attachment-routes.ts +5 -6
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +12 -19
- package/src/runtime/routes/conversation-routes.ts +5 -9
- package/src/runtime/routes/run-routes.ts +4 -8
- package/src/runtime/run-orchestrator.ts +39 -6
- 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 +67 -0
- package/src/tools/calls/call-start.ts +73 -0
- package/src/tools/calls/call-status.ts +81 -0
- 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 +21 -5
- 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/registry.ts +2 -4
- 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 -6
- package/src/tools/tasks/task-delete.ts +69 -56
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +74 -102
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +192 -134
- package/src/tools/tasks/work-item-list.ts +33 -78
- package/src/tools/tasks/work-item-remove.ts +60 -0
- package/src/tools/tasks/work-item-update.ts +114 -0
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +20 -74
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +6 -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 +236 -2
- package/src/workspace/commit-message-enrichment-service.ts +284 -0
- package/src/workspace/commit-message-provider.ts +95 -0
- package/src/workspace/git-service.ts +272 -52
- package/src/workspace/heartbeat-service.ts +70 -13
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +100 -51
- 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
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
|
|
3
|
-
import type { ToolDefinition } from '../../providers/types.js';
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../types.js';
|
|
4
2
|
import { getTask, listTasks, createTask } from '../../tasks/task-store.js';
|
|
5
|
-
import {
|
|
3
|
+
import { createWorkItemWithPermissions, findActiveWorkItemsByTitle, updateWorkItem, identifyEntityById, buildWorkItemMismatchError } from '../../work-items/work-item-store.js';
|
|
4
|
+
import { sanitizeToolList } from '../../tasks/tool-sanitizer.js';
|
|
5
|
+
import { getLogger } from '../../util/logger.js';
|
|
6
|
+
|
|
7
|
+
const log = getLogger('task-list-add');
|
|
6
8
|
|
|
7
9
|
const PRIORITY_LABELS: Record<number, string> = {
|
|
8
10
|
0: 'high',
|
|
@@ -10,151 +12,114 @@ const PRIORITY_LABELS: Record<number, string> = {
|
|
|
10
12
|
2: 'low',
|
|
11
13
|
};
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type: 'string',
|
|
31
|
-
description:
|
|
32
|
-
'Title for the work item. When provided WITHOUT task_id or task_name, creates an ad-hoc work item directly (a lightweight task template is auto-created behind the scenes). When provided WITH task_id or task_name, overrides the task definition title.',
|
|
33
|
-
},
|
|
34
|
-
notes: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
description: 'Notes to attach to the work item.',
|
|
37
|
-
},
|
|
38
|
-
priority_tier: {
|
|
39
|
-
type: 'number',
|
|
40
|
-
description: '0 = high, 1 = medium (default), 2 = low.',
|
|
41
|
-
},
|
|
42
|
-
sort_index: {
|
|
43
|
-
type: 'number',
|
|
44
|
-
description: 'Manual sort order within the priority tier.',
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
class TaskListAddTool implements Tool {
|
|
51
|
-
name = 'task_list_add';
|
|
52
|
-
description = definition.description;
|
|
53
|
-
category = 'tasks';
|
|
54
|
-
defaultRiskLevel = RiskLevel.Low;
|
|
55
|
-
|
|
56
|
-
getDefinition(): ToolDefinition {
|
|
57
|
-
return definition;
|
|
15
|
+
function handleDuplicate(
|
|
16
|
+
title: string,
|
|
17
|
+
ifExists: string,
|
|
18
|
+
input: Record<string, unknown>,
|
|
19
|
+
): ToolExecutionResult | null {
|
|
20
|
+
const existing = findActiveWorkItemsByTitle(title);
|
|
21
|
+
if (existing.length === 0) return null;
|
|
22
|
+
|
|
23
|
+
const match = existing[0];
|
|
24
|
+
log.info({ title, existingId: match.id, ifExists }, 'task_list_add: duplicate detected');
|
|
25
|
+
|
|
26
|
+
if (ifExists === 'reuse_existing') {
|
|
27
|
+
log.info({ title, existingId: match.id }, 'task_list_add: reused existing item');
|
|
28
|
+
return {
|
|
29
|
+
content: `Task "${match.title}" already exists in the queue (ID: ${match.id}, status: ${match.status}). Use task_list_update to modify it.`,
|
|
30
|
+
isError: false,
|
|
31
|
+
};
|
|
58
32
|
}
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
34
|
+
if (ifExists === 'update_existing') {
|
|
35
|
+
const updates: Partial<{ title: string; notes: string; priorityTier: number; sortIndex: number }> = {};
|
|
36
|
+
if (input.priority_tier !== undefined) updates.priorityTier = input.priority_tier as number;
|
|
37
|
+
if (input.notes !== undefined) updates.notes = input.notes as string;
|
|
38
|
+
if (input.sort_index !== undefined) updates.sortIndex = input.sort_index as number;
|
|
39
|
+
if (Object.keys(updates).length > 0) {
|
|
40
|
+
updateWorkItem(match.id, updates);
|
|
41
|
+
}
|
|
42
|
+
log.info({ title, existingId: match.id, updates }, 'task_list_add: updated existing item');
|
|
43
|
+
return {
|
|
44
|
+
content: `Reused existing task "${match.title}" (ID: ${match.id}) instead of creating a duplicate.${
|
|
45
|
+
Object.keys(updates).length > 0 ? ` Updated: ${Object.keys(updates).join(', ')}.` : ''
|
|
46
|
+
}`,
|
|
47
|
+
isError: false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
77
50
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
title: titleOverride,
|
|
81
|
-
template: titleOverride,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const workItem = createWorkItem({
|
|
85
|
-
taskId: adHocTask.id,
|
|
86
|
-
title: titleOverride,
|
|
87
|
-
notes,
|
|
88
|
-
priorityTier: priorityTier ?? 1,
|
|
89
|
-
sortIndex,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const priority = PRIORITY_LABELS[workItem.priorityTier] ?? `tier ${workItem.priorityTier}`;
|
|
93
|
-
const lines = [
|
|
94
|
-
`Enqueued work item:`,
|
|
95
|
-
` Title: ${workItem.title}`,
|
|
96
|
-
` ID: ${workItem.id}`,
|
|
97
|
-
` Priority: ${priority}`,
|
|
98
|
-
` Status: ${workItem.status}`,
|
|
99
|
-
];
|
|
100
|
-
if (workItem.notes) {
|
|
101
|
-
lines.push(` Notes: ${workItem.notes}`);
|
|
102
|
-
}
|
|
103
|
-
if (workItem.sortIndex !== null) {
|
|
104
|
-
lines.push(` Sort index: ${workItem.sortIndex}`);
|
|
105
|
-
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
106
53
|
|
|
107
|
-
|
|
54
|
+
export async function executeTaskListAdd(
|
|
55
|
+
input: Record<string, unknown>,
|
|
56
|
+
_context: ToolContext,
|
|
57
|
+
): Promise<ToolExecutionResult> {
|
|
58
|
+
try {
|
|
59
|
+
const taskId = input.task_id as string | undefined;
|
|
60
|
+
const taskName = input.task_name as string | undefined;
|
|
61
|
+
const titleOverride = input.title as string | undefined;
|
|
62
|
+
const executionPrompt = input.execution_prompt as string | undefined;
|
|
63
|
+
const notes = input.notes as string | undefined;
|
|
64
|
+
const priorityTier = input.priority_tier as number | undefined;
|
|
65
|
+
const sortIndex = input.sort_index as number | undefined;
|
|
66
|
+
const rawRequiredTools = input.required_tools as string[] | undefined;
|
|
67
|
+
|
|
68
|
+
const ifExists = (input.if_exists as string) || 'reuse_existing';
|
|
69
|
+
|
|
70
|
+
// Sanitize explicit required_tools if provided (including empty array)
|
|
71
|
+
const hasExplicitTools = rawRequiredTools !== undefined;
|
|
72
|
+
const sanitizedTools = hasExplicitTools ? sanitizeToolList(rawRequiredTools) : undefined;
|
|
73
|
+
|
|
74
|
+
// Ad-hoc mode: title provided without task_id or task_name
|
|
75
|
+
if (!taskId && !taskName) {
|
|
76
|
+
if (!titleOverride) {
|
|
77
|
+
return {
|
|
78
|
+
content: 'Error: You must provide either task_id, task_name, or title to create a work item.',
|
|
79
|
+
isError: true,
|
|
80
|
+
};
|
|
108
81
|
}
|
|
109
82
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (!resolvedTask) {
|
|
115
|
-
return { content: `Error: No task definition found with ID "${taskId}".`, isError: true };
|
|
116
|
-
}
|
|
83
|
+
// Duplicate-prevention guard
|
|
84
|
+
if (ifExists !== 'create_duplicate') {
|
|
85
|
+
const duplicateResult = handleDuplicate(titleOverride, ifExists, input);
|
|
86
|
+
if (duplicateResult) return duplicateResult;
|
|
117
87
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const allTasks = listTasks();
|
|
121
|
-
const matches = allTasks.filter((t) => t.title.toLowerCase().includes(needle));
|
|
122
|
-
|
|
123
|
-
if (matches.length === 0) {
|
|
124
|
-
return {
|
|
125
|
-
content: `Error: No task definition found matching "${taskName}". Use task_list to see available tasks.`,
|
|
126
|
-
isError: true,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
88
|
+
log.info({ title: titleOverride }, 'task_list_add: creating duplicate (if_exists=create_duplicate)');
|
|
89
|
+
}
|
|
129
90
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
91
|
+
log.debug({ title: titleOverride }, 'task_list_add: creating new item');
|
|
92
|
+
|
|
93
|
+
// Auto-create a lightweight task template for the ad-hoc item.
|
|
94
|
+
// Use execution_prompt as the template (the actual instruction sent to the
|
|
95
|
+
// model at run time), falling back to notes then title. This preserves
|
|
96
|
+
// detailed instructions (e.g. full file paths) that may be shortened in
|
|
97
|
+
// the display title.
|
|
98
|
+
const adHocTemplate = executionPrompt ?? notes ?? titleOverride;
|
|
99
|
+
const adHocTask = createTask({
|
|
100
|
+
title: titleOverride,
|
|
101
|
+
template: adHocTemplate,
|
|
102
|
+
});
|
|
140
103
|
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
// For ad-hoc items: explicit tools → persist; omitted → null
|
|
105
|
+
const adHocRequiredTools = hasExplicitTools ? JSON.stringify(sanitizedTools) : undefined;
|
|
143
106
|
|
|
144
|
-
const workItem =
|
|
145
|
-
taskId:
|
|
146
|
-
title: titleOverride
|
|
107
|
+
const workItem = createWorkItemWithPermissions({
|
|
108
|
+
taskId: adHocTask.id,
|
|
109
|
+
title: titleOverride,
|
|
147
110
|
notes,
|
|
148
111
|
priorityTier: priorityTier ?? 1,
|
|
149
112
|
sortIndex,
|
|
113
|
+
requiredTools: adHocRequiredTools,
|
|
150
114
|
});
|
|
151
115
|
|
|
116
|
+
log.info({ selectorType: 'title', workItemId: workItem.id, title: workItem.title }, 'ad-hoc work item created');
|
|
117
|
+
|
|
152
118
|
const priority = PRIORITY_LABELS[workItem.priorityTier] ?? `tier ${workItem.priorityTier}`;
|
|
153
119
|
const lines = [
|
|
154
120
|
`Enqueued work item:`,
|
|
155
121
|
` Title: ${workItem.title}`,
|
|
156
122
|
` ID: ${workItem.id}`,
|
|
157
|
-
` Task definition: ${resolvedTask.title} (${resolvedTask.id})`,
|
|
158
123
|
` Priority: ${priority}`,
|
|
159
124
|
` Status: ${workItem.status}`,
|
|
160
125
|
];
|
|
@@ -166,11 +131,104 @@ class TaskListAddTool implements Tool {
|
|
|
166
131
|
}
|
|
167
132
|
|
|
168
133
|
return { content: lines.join('\n'), isError: false };
|
|
169
|
-
} catch (err) {
|
|
170
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
171
|
-
return { content: `Error: ${msg}`, isError: true };
|
|
172
134
|
}
|
|
135
|
+
|
|
136
|
+
let resolvedTask;
|
|
137
|
+
|
|
138
|
+
if (taskId) {
|
|
139
|
+
resolvedTask = getTask(taskId);
|
|
140
|
+
if (!resolvedTask) {
|
|
141
|
+
const entity = identifyEntityById(taskId);
|
|
142
|
+
if (entity.type === 'work_item') {
|
|
143
|
+
return {
|
|
144
|
+
content: `Error: ${buildWorkItemMismatchError(taskId, entity.title!, 'task_list_update to modify the existing work item, or task_list_add with just a title for a new ad-hoc item')}`,
|
|
145
|
+
isError: true,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return { content: `Error: No task definition found with ID "${taskId}". Use task_list to see available task templates, or provide just a title to create an ad-hoc work item.`, isError: true };
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Search by name (case-insensitive substring match)
|
|
152
|
+
const needle = taskName!.toLowerCase();
|
|
153
|
+
const allTasks = listTasks();
|
|
154
|
+
const matches = allTasks.filter((t) => t.title.toLowerCase().includes(needle));
|
|
155
|
+
|
|
156
|
+
if (matches.length === 0) {
|
|
157
|
+
return {
|
|
158
|
+
content: `Error: No task definition found matching "${taskName}". Use task_list to see available tasks.`,
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (matches.length > 1) {
|
|
164
|
+
const lines = [
|
|
165
|
+
`Multiple task definitions match "${taskName}". Please specify by ID:`,
|
|
166
|
+
'',
|
|
167
|
+
];
|
|
168
|
+
for (const m of matches) {
|
|
169
|
+
lines.push(`- ${m.title} (ID: ${m.id})`);
|
|
170
|
+
}
|
|
171
|
+
return { content: lines.join('\n'), isError: true };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
resolvedTask = matches[0];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const finalTitle = titleOverride ?? resolvedTask.title;
|
|
178
|
+
|
|
179
|
+
// Duplicate-prevention guard
|
|
180
|
+
if (ifExists !== 'create_duplicate') {
|
|
181
|
+
const duplicateResult = handleDuplicate(finalTitle, ifExists, input);
|
|
182
|
+
if (duplicateResult) return duplicateResult;
|
|
183
|
+
} else {
|
|
184
|
+
log.info({ title: finalTitle }, 'task_list_add: creating duplicate (if_exists=create_duplicate)');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
log.debug({ title: finalTitle }, 'task_list_add: creating new item');
|
|
188
|
+
|
|
189
|
+
const selectorType = taskId ? 'task_id' : 'task_name';
|
|
190
|
+
|
|
191
|
+
// Snapshot precedence: explicit required_tools > template tools > null
|
|
192
|
+
let resolvedRequiredTools: string | undefined;
|
|
193
|
+
if (hasExplicitTools) {
|
|
194
|
+
resolvedRequiredTools = JSON.stringify(sanitizedTools);
|
|
195
|
+
} else if (resolvedTask.requiredTools) {
|
|
196
|
+
// Snapshot template tools at add time
|
|
197
|
+
const templateTools = sanitizeToolList(JSON.parse(resolvedTask.requiredTools));
|
|
198
|
+
resolvedRequiredTools = JSON.stringify(templateTools);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const workItem = createWorkItemWithPermissions({
|
|
202
|
+
taskId: resolvedTask.id,
|
|
203
|
+
title: finalTitle,
|
|
204
|
+
notes,
|
|
205
|
+
priorityTier: priorityTier ?? 1,
|
|
206
|
+
sortIndex,
|
|
207
|
+
requiredTools: resolvedRequiredTools,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
log.info({ selectorType, taskId: resolvedTask.id, workItemId: workItem.id, title: workItem.title }, 'work item created from task definition');
|
|
211
|
+
|
|
212
|
+
const priority = PRIORITY_LABELS[workItem.priorityTier] ?? `tier ${workItem.priorityTier}`;
|
|
213
|
+
const lines = [
|
|
214
|
+
`Enqueued work item:`,
|
|
215
|
+
` Title: ${workItem.title}`,
|
|
216
|
+
` ID: ${workItem.id}`,
|
|
217
|
+
` Task definition: ${resolvedTask.title} (${resolvedTask.id})`,
|
|
218
|
+
` Priority: ${priority}`,
|
|
219
|
+
` Status: ${workItem.status}`,
|
|
220
|
+
];
|
|
221
|
+
if (workItem.notes) {
|
|
222
|
+
lines.push(` Notes: ${workItem.notes}`);
|
|
223
|
+
}
|
|
224
|
+
if (workItem.sortIndex !== null) {
|
|
225
|
+
lines.push(` Sort index: ${workItem.sortIndex}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { content: lines.join('\n'), isError: false };
|
|
229
|
+
} catch (err) {
|
|
230
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
231
|
+
log.error({ error: msg }, 'enqueue failed');
|
|
232
|
+
return { content: `Error: ${msg}`, isError: true };
|
|
173
233
|
}
|
|
174
234
|
}
|
|
175
|
-
|
|
176
|
-
export const taskListAddTool = new TaskListAddTool();
|
|
@@ -1,86 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
|
|
3
|
-
import type { ToolDefinition } from '../../providers/types.js';
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../types.js';
|
|
4
2
|
import { listWorkItems, type WorkItemStatus } from '../../work-items/work-item-store.js';
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
class TaskListShowTool implements Tool {
|
|
31
|
-
name = 'task_list_show';
|
|
32
|
-
description = definition.description;
|
|
33
|
-
category = 'tasks';
|
|
34
|
-
defaultRiskLevel = RiskLevel.Low;
|
|
35
|
-
|
|
36
|
-
getDefinition(): ToolDefinition {
|
|
37
|
-
return definition;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async execute(input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
|
|
41
|
-
try {
|
|
42
|
-
const statusFilter = input.status as string | string[] | undefined;
|
|
43
|
-
|
|
44
|
-
let items;
|
|
45
|
-
if (typeof statusFilter === 'string') {
|
|
46
|
-
items = listWorkItems({ status: statusFilter as WorkItemStatus });
|
|
47
|
-
} else if (Array.isArray(statusFilter)) {
|
|
48
|
-
// listWorkItems only supports a single status filter, so we fetch all
|
|
49
|
-
// and filter client-side when an array is provided
|
|
50
|
-
const allItems = listWorkItems();
|
|
51
|
-
const allowed = new Set(statusFilter);
|
|
52
|
-
items = allItems.filter((item) => allowed.has(item.status));
|
|
53
|
-
} else {
|
|
54
|
-
items = listWorkItems();
|
|
55
|
-
}
|
|
4
|
+
export async function executeTaskListShow(
|
|
5
|
+
input: Record<string, unknown>,
|
|
6
|
+
_context: ToolContext,
|
|
7
|
+
): Promise<ToolExecutionResult> {
|
|
8
|
+
try {
|
|
9
|
+
const statusFilter = input.status as string | string[] | undefined;
|
|
10
|
+
|
|
11
|
+
let items;
|
|
12
|
+
if (typeof statusFilter === 'string') {
|
|
13
|
+
items = listWorkItems({ status: statusFilter as WorkItemStatus });
|
|
14
|
+
} else if (Array.isArray(statusFilter)) {
|
|
15
|
+
// listWorkItems only supports a single status filter, so we fetch all
|
|
16
|
+
// and filter client-side when an array is provided
|
|
17
|
+
const allItems = listWorkItems();
|
|
18
|
+
const allowed = new Set(statusFilter);
|
|
19
|
+
items = allItems.filter((item) => allowed.has(item.status));
|
|
20
|
+
} else {
|
|
21
|
+
items = listWorkItems();
|
|
22
|
+
}
|
|
56
23
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
24
|
+
const count = items.length;
|
|
25
|
+
const filtered = statusFilter !== undefined;
|
|
60
26
|
|
|
61
|
-
|
|
27
|
+
if (count === 0) {
|
|
28
|
+
const suffix = filtered ? 'no items matching filter.' : 'no tasks queued.';
|
|
29
|
+
return { content: `Opened Tasks window \u2014 ${suffix}`, isError: false };
|
|
30
|
+
}
|
|
62
31
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
lines.push(` ID: ${item.id}`);
|
|
67
|
-
lines.push(` Status: ${item.status}`);
|
|
68
|
-
lines.push(` Priority: ${priority}`);
|
|
69
|
-
if (item.notes) {
|
|
70
|
-
lines.push(` Notes: ${item.notes}`);
|
|
71
|
-
}
|
|
72
|
-
if (item.lastRunStatus) {
|
|
73
|
-
lines.push(` Last run: ${item.lastRunStatus}`);
|
|
74
|
-
}
|
|
75
|
-
lines.push('');
|
|
76
|
-
}
|
|
32
|
+
const label = filtered
|
|
33
|
+
? `${count} ${Array.isArray(statusFilter) ? 'matching' : statusFilter} item${count === 1 ? '' : 's'}`
|
|
34
|
+
: `${count} item${count === 1 ? '' : 's'}`;
|
|
77
35
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
36
|
+
return { content: `Opened Tasks window (${label}).`, isError: false };
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39
|
+
return { content: `Error: ${msg}`, isError: true };
|
|
83
40
|
}
|
|
84
41
|
}
|
|
85
|
-
|
|
86
|
-
export const taskListShowTool = new TaskListShowTool();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../types.js';
|
|
2
|
+
import { resolveWorkItem, removeWorkItemFromQueue, identifyEntityById, buildTaskTemplateMismatchError, type WorkItemStatus } from '../../work-items/work-item-store.js';
|
|
3
|
+
import { getLogger } from '../../util/logger.js';
|
|
4
|
+
|
|
5
|
+
const log = getLogger('task-list-remove');
|
|
6
|
+
|
|
7
|
+
export async function executeTaskListRemove(
|
|
8
|
+
input: Record<string, unknown>,
|
|
9
|
+
_context: ToolContext,
|
|
10
|
+
): Promise<ToolExecutionResult> {
|
|
11
|
+
const selectorType = input.work_item_id ? 'work_item_id' : input.task_id ? 'task_id' : input.task_name ? 'task_name' : input.title ? 'title' : 'none';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const selector = {
|
|
15
|
+
workItemId: input.work_item_id as string | undefined,
|
|
16
|
+
taskId: input.task_id as string | undefined,
|
|
17
|
+
title: (input.task_name ?? input.title) as string | undefined,
|
|
18
|
+
priorityTier: input.priority_tier as number | undefined,
|
|
19
|
+
status: input.status as WorkItemStatus | undefined,
|
|
20
|
+
createdOrder: input.created_order as number | undefined,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const resolveResult = resolveWorkItem(selector);
|
|
24
|
+
|
|
25
|
+
if (resolveResult.status === 'not_found') {
|
|
26
|
+
// When the model passes an ID directly, check if it's a task template
|
|
27
|
+
if (selector.workItemId) {
|
|
28
|
+
const entity = identifyEntityById(selector.workItemId);
|
|
29
|
+
if (entity.type === 'task_template') {
|
|
30
|
+
log.warn({ selectorType, inputId: selector.workItemId }, 'task template ID passed as work_item_id');
|
|
31
|
+
return {
|
|
32
|
+
content: `Error: ${buildTaskTemplateMismatchError(selector.workItemId, entity.title!, 'task_delete to delete task templates')}`,
|
|
33
|
+
isError: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
log.warn({ selectorType, error: resolveResult.message }, 'work item not found for removal');
|
|
38
|
+
return { content: `Error: ${resolveResult.message}`, isError: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (resolveResult.status === 'ambiguous') {
|
|
42
|
+
log.warn({ selectorType, matchCount: resolveResult.matches.length }, 'ambiguous selector for removal');
|
|
43
|
+
return { content: `Error: ${resolveResult.message}`, isError: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const item = resolveResult.workItem;
|
|
47
|
+
|
|
48
|
+
log.info({ selectorType, selectorValue: input[selectorType], resolvedWorkItemId: item.id, title: item.title }, 'resolved work item for removal');
|
|
49
|
+
|
|
50
|
+
const removeResult = removeWorkItemFromQueue(item.id);
|
|
51
|
+
|
|
52
|
+
log.info({ resolvedWorkItemId: item.id, deletedCount: 1 }, 'work item removed');
|
|
53
|
+
|
|
54
|
+
return { content: removeResult.message, isError: false };
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
+
log.error({ selectorType, error: msg }, 'remove failed');
|
|
58
|
+
return { content: `Error: ${msg}`, isError: true };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../types.js';
|
|
2
|
+
import { resolveWorkItem, updateWorkItem, identifyEntityById, buildTaskTemplateMismatchError, type WorkItemStatus } from '../../work-items/work-item-store.js';
|
|
3
|
+
import { getLogger } from '../../util/logger.js';
|
|
4
|
+
|
|
5
|
+
const log = getLogger('task-list-update');
|
|
6
|
+
|
|
7
|
+
const PRIORITY_LABELS: Record<number, string> = {
|
|
8
|
+
0: 'high',
|
|
9
|
+
1: 'medium',
|
|
10
|
+
2: 'low',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function executeTaskListUpdate(
|
|
14
|
+
input: Record<string, unknown>,
|
|
15
|
+
_context: ToolContext,
|
|
16
|
+
): Promise<ToolExecutionResult> {
|
|
17
|
+
const selectorType = input.work_item_id ? 'work_item_id' : input.task_id ? 'task_id' : input.task_name ? 'task_name' : input.title ? 'title' : 'none';
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Build selector from whichever identifier was provided
|
|
21
|
+
const selector = {
|
|
22
|
+
workItemId: input.work_item_id as string | undefined,
|
|
23
|
+
taskId: input.task_id as string | undefined,
|
|
24
|
+
title: (input.task_name ?? input.title) as string | undefined,
|
|
25
|
+
priorityTier: input.filter_priority_tier as number | undefined,
|
|
26
|
+
status: input.filter_status as WorkItemStatus | undefined,
|
|
27
|
+
createdOrder: input.created_order as number | undefined,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Resolve the target work item
|
|
31
|
+
const result = resolveWorkItem(selector);
|
|
32
|
+
|
|
33
|
+
if (result.status === 'not_found') {
|
|
34
|
+
// When the model passes an ID directly, check if it's a task template
|
|
35
|
+
if (selector.workItemId) {
|
|
36
|
+
const entity = identifyEntityById(selector.workItemId);
|
|
37
|
+
if (entity.type === 'task_template') {
|
|
38
|
+
log.warn({ selectorType, inputId: selector.workItemId }, 'task template ID passed as work_item_id');
|
|
39
|
+
return {
|
|
40
|
+
content: `Error: ${buildTaskTemplateMismatchError(selector.workItemId, entity.title!, 'task_delete to remove task templates, or task_list to view them')}`,
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
log.warn({ selectorType, error: result.message }, 'work item not found for update');
|
|
46
|
+
return { content: `Error: ${result.message}`, isError: true };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (result.status === 'ambiguous') {
|
|
50
|
+
log.warn({ selectorType, matchCount: result.matches.length }, 'ambiguous selector for update');
|
|
51
|
+
return { content: `Error: ${result.message}`, isError: true };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const item = result.workItem;
|
|
55
|
+
|
|
56
|
+
// Block direct transitions to 'done' — the only path to done is
|
|
57
|
+
// through the Review action (handleWorkItemComplete in the daemon).
|
|
58
|
+
if (input.status === 'done') {
|
|
59
|
+
log.warn({ selectorType, resolvedWorkItemId: item.id }, 'rejected attempt to set status to done directly');
|
|
60
|
+
return {
|
|
61
|
+
content: 'Error: Cannot set status to \'done\' directly. Use the Review action in the Tasks window.',
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
log.info({ selectorType, selectorValue: input[selectorType], resolvedWorkItemId: item.id }, 'resolved work item for update');
|
|
67
|
+
|
|
68
|
+
// Build updates from provided fields
|
|
69
|
+
const updates: Partial<{
|
|
70
|
+
priorityTier: number;
|
|
71
|
+
notes: string;
|
|
72
|
+
status: WorkItemStatus;
|
|
73
|
+
sortIndex: number;
|
|
74
|
+
}> = {};
|
|
75
|
+
if (input.priority_tier !== undefined) updates.priorityTier = input.priority_tier as number;
|
|
76
|
+
if (input.notes !== undefined) updates.notes = input.notes as string;
|
|
77
|
+
if (input.status !== undefined) updates.status = input.status as WorkItemStatus;
|
|
78
|
+
if (input.sort_index !== undefined) updates.sortIndex = input.sort_index as number;
|
|
79
|
+
|
|
80
|
+
if (Object.keys(updates).length === 0) {
|
|
81
|
+
log.warn({ selectorType, resolvedWorkItemId: item.id }, 'update called with no fields to update');
|
|
82
|
+
return {
|
|
83
|
+
content: 'No updates specified. Provide at least one field to update (priority_tier, notes, status, sort_index).',
|
|
84
|
+
isError: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const updated = updateWorkItem(item.id, updates);
|
|
89
|
+
if (!updated) {
|
|
90
|
+
log.error({ selectorType, resolvedWorkItemId: item.id, updates }, 'updateWorkItem returned null');
|
|
91
|
+
return {
|
|
92
|
+
content: `Error: Failed to update work item "${item.title}".`,
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
log.info({ resolvedWorkItemId: item.id, updatedFields: Object.keys(updates) }, 'work item updated');
|
|
98
|
+
|
|
99
|
+
// Build confirmation message
|
|
100
|
+
const parts: string[] = [`Updated "${updated.title}"`];
|
|
101
|
+
if (input.priority_tier !== undefined) {
|
|
102
|
+
parts.push(`priority → ${PRIORITY_LABELS[updated.priorityTier] ?? updated.priorityTier}`);
|
|
103
|
+
}
|
|
104
|
+
if (input.notes !== undefined) parts.push('notes updated');
|
|
105
|
+
if (input.status !== undefined) parts.push(`status → ${updated.status}`);
|
|
106
|
+
if (input.sort_index !== undefined) parts.push(`sort index → ${updated.sortIndex}`);
|
|
107
|
+
|
|
108
|
+
return { content: parts.join(', ') + '.', isError: false };
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
log.error({ selectorType, error: msg }, 'update failed');
|
|
112
|
+
return { content: `Error: ${msg}`, isError: true };
|
|
113
|
+
}
|
|
114
|
+
}
|