vellum 0.2.1 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/bun.lock +71 -100
- package/package.json +5 -3
- 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 +305 -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-twilio-config.test.ts +221 -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 +71 -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-regressions.test.ts +100 -2
- 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-commit-message-generator.test.ts +303 -0
- 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-conflict-gate.test.ts +28 -25
- 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/__tests__/twilio-webhook-urls.test.ts +162 -0
- 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-config.ts +8 -8
- package/src/calls/twilio-provider.ts +13 -9
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/twilio-webhook-urls.ts +50 -0
- 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 +270 -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 +34 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +165 -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/config/vellum-skills/telegram-setup/SKILL.md +1 -5
- 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 +205 -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 +32 -4
- package/src/daemon/ipc-contract.ts +156 -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 +75 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +922 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-conflict-gate.ts +18 -109
- 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/conflict-intent.ts +114 -0
- 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/job-handlers/conflict.ts +23 -1
- 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/gateway-client.ts +36 -0
- package/src/runtime/http-server.ts +166 -22
- 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 +125 -88
- 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 +293 -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 +207 -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 +269 -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
|
@@ -2,16 +2,18 @@ import * as net from 'node:net';
|
|
|
2
2
|
import type {
|
|
3
3
|
WorkItemsListRequest,
|
|
4
4
|
WorkItemGetRequest,
|
|
5
|
-
WorkItemCreateRequest,
|
|
6
5
|
WorkItemUpdateRequest,
|
|
7
6
|
WorkItemCompleteRequest,
|
|
8
7
|
WorkItemDeleteRequest,
|
|
9
8
|
WorkItemRunTaskRequest,
|
|
10
9
|
WorkItemOutputRequest,
|
|
10
|
+
WorkItemPreflightRequest,
|
|
11
|
+
WorkItemApprovePermissionsRequest,
|
|
12
|
+
WorkItemCancelRequest,
|
|
11
13
|
} from '../ipc-protocol.js';
|
|
12
|
-
import { log, type HandlerContext } from './shared.js';
|
|
14
|
+
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
15
|
+
import { getSubagentManager } from '../../subagent/index.js';
|
|
13
16
|
import {
|
|
14
|
-
createWorkItem,
|
|
15
17
|
deleteWorkItem,
|
|
16
18
|
getWorkItem,
|
|
17
19
|
listWorkItems,
|
|
@@ -21,6 +23,9 @@ import {
|
|
|
21
23
|
import { getTask, getTaskRun } from '../../tasks/task-store.js';
|
|
22
24
|
import { runTask } from '../../tasks/task-runner.js';
|
|
23
25
|
import { getMessages } from '../../memory/conversation-store.js';
|
|
26
|
+
import { classifyRisk, check } from '../../permissions/checker.js';
|
|
27
|
+
import { truncate } from '../../util/truncate.js';
|
|
28
|
+
import { sanitizeToolList, getRegisteredToolNames, getToolDescription } from '../../tasks/tool-sanitizer.js';
|
|
24
29
|
|
|
25
30
|
export function handleWorkItemsList(
|
|
26
31
|
msg: WorkItemsListRequest,
|
|
@@ -40,35 +45,20 @@ export function handleWorkItemGet(
|
|
|
40
45
|
ctx.send(socket, { type: 'work_item_get_response', item });
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
export function handleWorkItemCreate(
|
|
44
|
-
msg: WorkItemCreateRequest,
|
|
45
|
-
socket: net.Socket,
|
|
46
|
-
ctx: HandlerContext,
|
|
47
|
-
): void {
|
|
48
|
-
const task = getTask(msg.taskId);
|
|
49
|
-
if (!task) {
|
|
50
|
-
ctx.send(socket, { type: 'error', message: `Task not found: ${msg.taskId}` });
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const item = createWorkItem({
|
|
54
|
-
taskId: msg.taskId,
|
|
55
|
-
title: msg.title ?? task.title,
|
|
56
|
-
notes: msg.notes,
|
|
57
|
-
priorityTier: msg.priorityTier,
|
|
58
|
-
sortIndex: msg.sortIndex,
|
|
59
|
-
});
|
|
60
|
-
ctx.send(socket, { type: 'work_item_create_response', item });
|
|
61
|
-
|
|
62
|
-
// Notify all connected clients so open Task Queue views refresh immediately
|
|
63
|
-
broadcastWorkItemStatus(ctx, item.id);
|
|
64
|
-
ctx.broadcast({ type: 'tasks_changed' });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
48
|
export function handleWorkItemUpdate(
|
|
68
49
|
msg: WorkItemUpdateRequest,
|
|
69
50
|
socket: net.Socket,
|
|
70
51
|
ctx: HandlerContext,
|
|
71
52
|
): void {
|
|
53
|
+
// Don't allow overwriting a cancelled status (e.g. from a late chat-completion observer)
|
|
54
|
+
if (msg.status !== undefined) {
|
|
55
|
+
const existing = getWorkItem(msg.id);
|
|
56
|
+
if (existing?.status === 'cancelled' && msg.status !== 'cancelled') {
|
|
57
|
+
ctx.send(socket, { type: 'work_item_update_response', item: existing });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
72
62
|
const updates: Record<string, unknown> = {};
|
|
73
63
|
if (msg.title !== undefined) updates.title = msg.title;
|
|
74
64
|
if (msg.notes !== undefined) updates.notes = msg.notes;
|
|
@@ -158,6 +148,107 @@ function broadcastWorkItemStatus(ctx: HandlerContext, id: string): void {
|
|
|
158
148
|
}
|
|
159
149
|
}
|
|
160
150
|
|
|
151
|
+
/** Extract plain text from a message content string (handles JSON content block arrays). */
|
|
152
|
+
function extractTextFromContent(content: string): string {
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(content);
|
|
155
|
+
if (Array.isArray(parsed)) {
|
|
156
|
+
return parsed
|
|
157
|
+
.filter((b: { type: string }) => b.type === 'text')
|
|
158
|
+
.map((b: { text: string }) => b.text)
|
|
159
|
+
.join('\n');
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Plain text content — use as-is
|
|
163
|
+
}
|
|
164
|
+
return content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Extract tool_result blocks from a user message's content. */
|
|
168
|
+
function extractToolResults(content: string): Array<{ tool_use_id: string; content: string; is_error?: boolean }> {
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(content);
|
|
171
|
+
if (Array.isArray(parsed)) {
|
|
172
|
+
return parsed
|
|
173
|
+
.filter((b: { type: string }) => b.type === 'tool_result')
|
|
174
|
+
.map((b: { tool_use_id: string; content?: string | Array<{ type: string; text?: string }>; is_error?: boolean }) => {
|
|
175
|
+
let text = '';
|
|
176
|
+
if (typeof b.content === 'string') {
|
|
177
|
+
text = b.content;
|
|
178
|
+
} else if (Array.isArray(b.content)) {
|
|
179
|
+
text = b.content
|
|
180
|
+
.filter((c) => c.type === 'text' && c.text)
|
|
181
|
+
.map((c) => c.text!)
|
|
182
|
+
.join('\n');
|
|
183
|
+
}
|
|
184
|
+
return { tool_use_id: b.tool_use_id, content: text, is_error: b.is_error };
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// Not JSON — no tool_result blocks
|
|
189
|
+
}
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Build highlights from tool outcomes in the conversation. Scans for
|
|
195
|
+
* tool_use (assistant) and tool_result (user) pairs, extracting concrete
|
|
196
|
+
* outcomes like errors, file paths, and URLs.
|
|
197
|
+
*/
|
|
198
|
+
function extractToolHighlights(
|
|
199
|
+
msgs: Array<{ role: string; content: string }>,
|
|
200
|
+
maxHighlights: number,
|
|
201
|
+
): string[] {
|
|
202
|
+
const highlights: string[] = [];
|
|
203
|
+
|
|
204
|
+
// Build a map of tool_use_id -> tool name from assistant messages
|
|
205
|
+
const toolNameById = new Map<string, string>();
|
|
206
|
+
for (const m of msgs) {
|
|
207
|
+
if (m.role !== 'assistant') continue;
|
|
208
|
+
try {
|
|
209
|
+
const parsed = JSON.parse(m.content);
|
|
210
|
+
if (Array.isArray(parsed)) {
|
|
211
|
+
for (const block of parsed) {
|
|
212
|
+
if (block.type === 'tool_use' && block.id && block.name) {
|
|
213
|
+
toolNameById.set(block.id, block.name);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch { /* skip */ }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Scan tool_result messages in reverse order (most recent first)
|
|
221
|
+
for (let i = msgs.length - 1; i >= 0 && highlights.length < maxHighlights; i--) {
|
|
222
|
+
const m = msgs[i];
|
|
223
|
+
if (m.role !== 'user') continue;
|
|
224
|
+
|
|
225
|
+
const results = extractToolResults(m.content);
|
|
226
|
+
for (const result of results) {
|
|
227
|
+
if (highlights.length >= maxHighlights) break;
|
|
228
|
+
|
|
229
|
+
const toolName = toolNameById.get(result.tool_use_id) ?? 'tool';
|
|
230
|
+
const resultText = result.content.trim();
|
|
231
|
+
|
|
232
|
+
if (result.is_error) {
|
|
233
|
+
// Always surface errors
|
|
234
|
+
const errorSnippet = truncate(resultText, 200, '...');
|
|
235
|
+
highlights.push(`- ${toolName}: Error — ${errorSnippet}`);
|
|
236
|
+
} else if (resultText) {
|
|
237
|
+
// Extract notable signal from successful results: file paths, URLs, or
|
|
238
|
+
// a short summary of what happened
|
|
239
|
+
const firstLine = resultText.split('\n')[0].trim();
|
|
240
|
+
if (firstLine.length > 0 && firstLine.length <= 200) {
|
|
241
|
+
highlights.push(`- ${toolName}: ${firstLine}`);
|
|
242
|
+
} else if (firstLine.length > 200) {
|
|
243
|
+
highlights.push(`- ${toolName}: ${truncate(firstLine, 200, '...')}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return highlights;
|
|
250
|
+
}
|
|
251
|
+
|
|
161
252
|
export function handleWorkItemOutput(
|
|
162
253
|
msg: WorkItemOutputRequest,
|
|
163
254
|
socket: net.Socket,
|
|
@@ -170,41 +261,50 @@ export function handleWorkItemOutput(
|
|
|
170
261
|
return;
|
|
171
262
|
}
|
|
172
263
|
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
|
|
264
|
+
// Use the task run's conversationId as the authoritative source. This
|
|
265
|
+
// ensures we read from the actual run's conversation, not stale references
|
|
266
|
+
// on the work item.
|
|
267
|
+
let conversationId: string | null = null;
|
|
268
|
+
let completedAt: number | null = null;
|
|
269
|
+
|
|
270
|
+
if (workItem.lastRunId) {
|
|
271
|
+
const run = getTaskRun(workItem.lastRunId);
|
|
272
|
+
if (run) {
|
|
273
|
+
conversationId = run.conversationId;
|
|
274
|
+
completedAt = run.finishedAt != null ? Math.floor(run.finishedAt / 1000) : null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Fall back to the work item's stored conversationId if the run lookup
|
|
279
|
+
// didn't yield one (e.g. run record was deleted but work item still has
|
|
280
|
+
// the reference).
|
|
281
|
+
if (!conversationId) {
|
|
282
|
+
conversationId = workItem.lastRunConversationId;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!conversationId) {
|
|
176
286
|
ctx.send(socket, { type: 'work_item_output_response', id: msg.id, success: false, error: 'This task has not been run yet. No output is available.' });
|
|
177
287
|
return;
|
|
178
288
|
}
|
|
179
289
|
|
|
180
290
|
let summary = '';
|
|
181
|
-
|
|
291
|
+
let highlights: string[] = [];
|
|
182
292
|
|
|
183
|
-
const msgs = getMessages(
|
|
184
|
-
|
|
293
|
+
const msgs = getMessages(conversationId);
|
|
294
|
+
|
|
295
|
+
// Find the last assistant message with text content (not tool calls).
|
|
296
|
+
// Skip messages that are purely about task management rather than
|
|
297
|
+
// reporting what the run actually did.
|
|
185
298
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
186
299
|
const m = msgs[i];
|
|
187
300
|
if (m.role !== 'assistant') continue;
|
|
188
301
|
|
|
189
|
-
|
|
190
|
-
// Content may be JSON array of content blocks — extract text blocks only
|
|
191
|
-
try {
|
|
192
|
-
const parsed = JSON.parse(text);
|
|
193
|
-
if (Array.isArray(parsed)) {
|
|
194
|
-
text = parsed
|
|
195
|
-
.filter((b: { type: string }) => b.type === 'text')
|
|
196
|
-
.map((b: { text: string }) => b.text)
|
|
197
|
-
.join('\n');
|
|
198
|
-
}
|
|
199
|
-
} catch {
|
|
200
|
-
// Plain text content — use as-is
|
|
201
|
-
}
|
|
202
|
-
|
|
302
|
+
const text = extractTextFromContent(m.content);
|
|
203
303
|
if (!text.trim()) continue;
|
|
204
304
|
|
|
205
|
-
summary = text
|
|
305
|
+
summary = truncate(text, 2000, '');
|
|
206
306
|
|
|
207
|
-
// Extract
|
|
307
|
+
// Extract bullet points from the assistant's prose
|
|
208
308
|
const lines = text.split('\n');
|
|
209
309
|
for (const line of lines) {
|
|
210
310
|
const trimmed = line.trim();
|
|
@@ -216,12 +316,22 @@ export function handleWorkItemOutput(
|
|
|
216
316
|
break;
|
|
217
317
|
}
|
|
218
318
|
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
319
|
+
// If we didn't get enough highlights from the assistant prose, supplement
|
|
320
|
+
// with concrete tool outcomes from the conversation.
|
|
321
|
+
if (highlights.length < 5) {
|
|
322
|
+
const toolHighlights = extractToolHighlights(msgs, 5 - highlights.length);
|
|
323
|
+
highlights = [...highlights, ...toolHighlights];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// If there's no assistant summary at all, synthesize one from tool results
|
|
327
|
+
// so the user still sees what happened.
|
|
328
|
+
if (!summary && msgs.length > 0) {
|
|
329
|
+
const toolHighlights = extractToolHighlights(msgs, 10);
|
|
330
|
+
if (toolHighlights.length > 0) {
|
|
331
|
+
summary = 'Task completed. Tool outcomes:\n' + toolHighlights.join('\n');
|
|
332
|
+
// Use the tool highlights as the main highlights too
|
|
333
|
+
highlights = toolHighlights.slice(0, 5);
|
|
334
|
+
}
|
|
225
335
|
}
|
|
226
336
|
|
|
227
337
|
ctx.send(socket, {
|
|
@@ -232,7 +342,7 @@ export function handleWorkItemOutput(
|
|
|
232
342
|
title: workItem.title,
|
|
233
343
|
status: workItem.lastRunStatus ?? workItem.status,
|
|
234
344
|
runId: workItem.lastRunId,
|
|
235
|
-
conversationId
|
|
345
|
+
conversationId,
|
|
236
346
|
completedAt,
|
|
237
347
|
summary,
|
|
238
348
|
highlights,
|
|
@@ -260,7 +370,7 @@ export async function handleWorkItemRunTask(
|
|
|
260
370
|
return;
|
|
261
371
|
}
|
|
262
372
|
|
|
263
|
-
const NON_RUNNABLE_STATUSES: readonly string[] = ['
|
|
373
|
+
const NON_RUNNABLE_STATUSES: readonly string[] = ['archived'];
|
|
264
374
|
if (NON_RUNNABLE_STATUSES.includes(workItem.status)) {
|
|
265
375
|
ctx.send(socket, { type: 'work_item_run_task_response', id: msg.id, lastRunId: workItem.lastRunId ?? '', success: false, error: `Work item has status '${workItem.status}' and cannot be run`, errorCode: 'invalid_status' });
|
|
266
376
|
return;
|
|
@@ -272,6 +382,37 @@ export async function handleWorkItemRunTask(
|
|
|
272
382
|
return;
|
|
273
383
|
}
|
|
274
384
|
|
|
385
|
+
// Compute required tools using the same resolution logic as preflight:
|
|
386
|
+
// work-item snapshot first, then task template, then all registered tools.
|
|
387
|
+
let requiredTools: string[];
|
|
388
|
+
if (workItem.requiredTools !== null && workItem.requiredTools !== undefined) {
|
|
389
|
+
requiredTools = sanitizeToolList(JSON.parse(workItem.requiredTools));
|
|
390
|
+
} else {
|
|
391
|
+
requiredTools = task.requiredTools
|
|
392
|
+
? sanitizeToolList(JSON.parse(task.requiredTools))
|
|
393
|
+
: getRegisteredToolNames();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Permission checkpoint: if the task requires tools, verify all have been approved.
|
|
397
|
+
// Empty required tools means no approvals needed.
|
|
398
|
+
let approvedTools: string[] | undefined;
|
|
399
|
+
if (requiredTools.length > 0) {
|
|
400
|
+
approvedTools = workItem.approvedTools ? JSON.parse(workItem.approvedTools) : undefined;
|
|
401
|
+
const approvedSet = new Set<string>(approvedTools ?? []);
|
|
402
|
+
const missingApprovals = requiredTools.filter((t) => !approvedSet.has(t));
|
|
403
|
+
if (missingApprovals.length > 0) {
|
|
404
|
+
ctx.send(socket, {
|
|
405
|
+
type: 'work_item_run_task_response',
|
|
406
|
+
id: msg.id,
|
|
407
|
+
lastRunId: '',
|
|
408
|
+
success: false,
|
|
409
|
+
error: 'Required tool permissions have not been approved. Run preflight first.',
|
|
410
|
+
errorCode: 'permission_required',
|
|
411
|
+
});
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
275
416
|
// Set status to running
|
|
276
417
|
updateWorkItem(msg.id, { status: 'running' });
|
|
277
418
|
|
|
@@ -282,29 +423,62 @@ export async function handleWorkItemRunTask(
|
|
|
282
423
|
broadcastWorkItemStatus(ctx, msg.id);
|
|
283
424
|
ctx.broadcast({ type: 'tasks_changed' });
|
|
284
425
|
|
|
285
|
-
// Execute task asynchronously — create a session
|
|
426
|
+
// Execute task asynchronously — lazily create a session inside the callback
|
|
427
|
+
// using the conversationId provided by runTask, so the session references
|
|
428
|
+
// the conversation that was actually inserted into the database.
|
|
286
429
|
try {
|
|
287
|
-
|
|
430
|
+
let session: Awaited<ReturnType<typeof ctx.getOrCreateSession>> | null = null;
|
|
288
431
|
const result = await runTask(
|
|
289
|
-
{ taskId: workItem.taskId, workingDir: process.cwd() },
|
|
290
|
-
async (
|
|
432
|
+
{ taskId: workItem.taskId, workingDir: process.cwd(), approvedTools },
|
|
433
|
+
async (conversationId, message, taskRunId) => {
|
|
434
|
+
if (!session) {
|
|
435
|
+
// Store conversationId on the work item immediately so the cancel
|
|
436
|
+
// handler can locate the session while the task is still running.
|
|
437
|
+
updateWorkItem(msg.id, { lastRunConversationId: conversationId });
|
|
438
|
+
session = await ctx.getOrCreateSession(conversationId);
|
|
439
|
+
|
|
440
|
+
// Notify clients so they can create a visible chat thread for this task run
|
|
441
|
+
ctx.broadcast({
|
|
442
|
+
type: 'task_run_thread_created',
|
|
443
|
+
conversationId,
|
|
444
|
+
workItemId: msg.id,
|
|
445
|
+
title: workItem.title,
|
|
446
|
+
});
|
|
447
|
+
// Wire the taskRunId so the executor can retrieve ephemeral permission rules
|
|
448
|
+
(session as unknown as { taskRunId?: string }).taskRunId = taskRunId;
|
|
449
|
+
// Prevent interactive clients from rebinding to this session mid-run
|
|
450
|
+
(session as unknown as { headlessLock: boolean }).headlessLock = true;
|
|
451
|
+
}
|
|
291
452
|
await session.processMessage(message, [], (event) => {
|
|
292
453
|
ctx.broadcast(event);
|
|
293
454
|
});
|
|
294
455
|
},
|
|
295
456
|
);
|
|
296
457
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
458
|
+
// Release the headless lock now that the task run is done
|
|
459
|
+
if (session) {
|
|
460
|
+
(session as unknown as { headlessLock: boolean }).headlessLock = false;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Don't overwrite cancelled status — the cancel handler already set it
|
|
464
|
+
const current = getWorkItem(msg.id);
|
|
465
|
+
if (current?.status !== 'cancelled') {
|
|
466
|
+
const finalStatus: WorkItemStatus = result.status === 'completed' ? 'awaiting_review' : 'failed';
|
|
467
|
+
updateWorkItem(msg.id, {
|
|
468
|
+
status: finalStatus,
|
|
469
|
+
lastRunId: result.taskRunId,
|
|
470
|
+
lastRunConversationId: result.conversationId,
|
|
471
|
+
lastRunStatus: result.status,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
304
474
|
|
|
305
475
|
broadcastWorkItemStatus(ctx, msg.id);
|
|
306
476
|
ctx.broadcast({ type: 'tasks_changed' });
|
|
307
477
|
} catch (err) {
|
|
478
|
+
// Release the headless lock on failure
|
|
479
|
+
if (session) {
|
|
480
|
+
(session as unknown as { headlessLock: boolean }).headlessLock = false;
|
|
481
|
+
}
|
|
308
482
|
log.error({ err, workItemId: msg.id }, 'work_item_run_task failed');
|
|
309
483
|
updateWorkItem(msg.id, {
|
|
310
484
|
status: 'failed',
|
|
@@ -314,3 +488,145 @@ export async function handleWorkItemRunTask(
|
|
|
314
488
|
ctx.broadcast({ type: 'tasks_changed' });
|
|
315
489
|
}
|
|
316
490
|
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
export async function handleWorkItemPreflight(
|
|
494
|
+
msg: WorkItemPreflightRequest,
|
|
495
|
+
socket: net.Socket,
|
|
496
|
+
ctx: HandlerContext,
|
|
497
|
+
): Promise<void> {
|
|
498
|
+
const workItem = getWorkItem(msg.id);
|
|
499
|
+
if (!workItem) {
|
|
500
|
+
ctx.send(socket, { type: 'work_item_preflight_response', id: msg.id, success: false, error: 'Work item not found' });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Compute required tools from the work-item snapshot first; only fall
|
|
505
|
+
// back to the task template (or all registered tools) when the
|
|
506
|
+
// snapshot is null.
|
|
507
|
+
let requiredTools: string[];
|
|
508
|
+
if (workItem.requiredTools !== null && workItem.requiredTools !== undefined) {
|
|
509
|
+
requiredTools = sanitizeToolList(JSON.parse(workItem.requiredTools));
|
|
510
|
+
} else {
|
|
511
|
+
const task = getTask(workItem.taskId);
|
|
512
|
+
if (!task) {
|
|
513
|
+
ctx.send(socket, { type: 'work_item_preflight_response', id: msg.id, success: false, error: `Associated task not found: ${workItem.taskId}` });
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
requiredTools = task.requiredTools
|
|
517
|
+
? sanitizeToolList(JSON.parse(task.requiredTools))
|
|
518
|
+
: getRegisteredToolNames();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// If the work item explicitly requires no tools, skip the dialog.
|
|
522
|
+
if (requiredTools.length === 0) {
|
|
523
|
+
ctx.send(socket, { type: 'work_item_preflight_response', id: msg.id, success: true, permissions: [] });
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// If some tools are already approved, only prompt for the missing ones.
|
|
528
|
+
// When all required tools are covered, skip the dialog entirely.
|
|
529
|
+
if (workItem.approvedTools) {
|
|
530
|
+
const approvedSet = new Set<string>(JSON.parse(workItem.approvedTools));
|
|
531
|
+
requiredTools = requiredTools.filter((t) => !approvedSet.has(t));
|
|
532
|
+
if (requiredTools.length === 0) {
|
|
533
|
+
ctx.send(socket, { type: 'work_item_preflight_response', id: msg.id, success: true, permissions: [] });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const workingDir = process.cwd();
|
|
539
|
+
const permissions = await Promise.all(
|
|
540
|
+
requiredTools.map(async (tool) => {
|
|
541
|
+
const risk = await classifyRisk(tool, {}, workingDir);
|
|
542
|
+
const result = await check(tool, {}, workingDir);
|
|
543
|
+
return {
|
|
544
|
+
tool,
|
|
545
|
+
description: getToolDescription(tool),
|
|
546
|
+
riskLevel: risk.toLowerCase() as 'low' | 'medium' | 'high',
|
|
547
|
+
currentDecision: result.decision as 'allow' | 'deny' | 'prompt',
|
|
548
|
+
};
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
ctx.send(socket, { type: 'work_item_preflight_response', id: msg.id, success: true, permissions });
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export function handleWorkItemApprovePermissions(
|
|
556
|
+
msg: WorkItemApprovePermissionsRequest,
|
|
557
|
+
socket: net.Socket,
|
|
558
|
+
ctx: HandlerContext,
|
|
559
|
+
): void {
|
|
560
|
+
const workItem = getWorkItem(msg.id);
|
|
561
|
+
if (!workItem) {
|
|
562
|
+
ctx.send(socket, { type: 'work_item_approve_permissions_response', id: msg.id, success: false, error: 'Work item not found' });
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Merge newly approved tools with any previously approved ones so reruns
|
|
567
|
+
// that only need a subset of previously-approved tools don't require
|
|
568
|
+
// re-approval.
|
|
569
|
+
const existingApproved: string[] = workItem.approvedTools
|
|
570
|
+
? JSON.parse(workItem.approvedTools)
|
|
571
|
+
: [];
|
|
572
|
+
const newApproved = sanitizeToolList(msg.approvedTools);
|
|
573
|
+
const merged = [...new Set([...existingApproved, ...newApproved])];
|
|
574
|
+
|
|
575
|
+
updateWorkItem(msg.id, {
|
|
576
|
+
approvedTools: JSON.stringify(sanitizeToolList(merged)),
|
|
577
|
+
approvalStatus: 'approved',
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
ctx.send(socket, { type: 'work_item_approve_permissions_response', id: msg.id, success: true });
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export function handleWorkItemCancel(
|
|
584
|
+
msg: WorkItemCancelRequest,
|
|
585
|
+
socket: net.Socket,
|
|
586
|
+
ctx: HandlerContext,
|
|
587
|
+
): void {
|
|
588
|
+
const workItem = getWorkItem(msg.id);
|
|
589
|
+
if (!workItem) {
|
|
590
|
+
ctx.send(socket, { type: 'work_item_cancel_response', id: msg.id, success: false, error: 'Work item not found' });
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (workItem.status !== 'running') {
|
|
595
|
+
ctx.send(socket, { type: 'work_item_cancel_response', id: msg.id, success: false, error: `Work item is not running (status: ${workItem.status})` });
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Abort the session associated with this work item's current run
|
|
600
|
+
const conversationId = workItem.lastRunConversationId;
|
|
601
|
+
if (conversationId) {
|
|
602
|
+
const session = ctx.sessions.get(conversationId);
|
|
603
|
+
if (session) {
|
|
604
|
+
(session as unknown as { headlessLock: boolean }).headlessLock = false;
|
|
605
|
+
session.abort();
|
|
606
|
+
getSubagentManager().abortAllForParent(conversationId);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
updateWorkItem(msg.id, {
|
|
611
|
+
status: 'cancelled',
|
|
612
|
+
lastRunStatus: 'cancelled',
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
ctx.send(socket, { type: 'work_item_cancel_response', id: msg.id, success: true });
|
|
616
|
+
|
|
617
|
+
broadcastWorkItemStatus(ctx, msg.id);
|
|
618
|
+
ctx.broadcast({ type: 'tasks_changed' });
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export const workItemHandlers = defineHandlers({
|
|
622
|
+
work_items_list: handleWorkItemsList,
|
|
623
|
+
work_item_get: handleWorkItemGet,
|
|
624
|
+
work_item_update: handleWorkItemUpdate,
|
|
625
|
+
work_item_complete: handleWorkItemComplete,
|
|
626
|
+
work_item_delete: handleWorkItemDelete,
|
|
627
|
+
work_item_run_task: handleWorkItemRunTask,
|
|
628
|
+
work_item_output: handleWorkItemOutput,
|
|
629
|
+
work_item_preflight: handleWorkItemPreflight,
|
|
630
|
+
work_item_approve_permissions: handleWorkItemApprovePermissions,
|
|
631
|
+
work_item_cancel: handleWorkItemCancel,
|
|
632
|
+
});
|
|
@@ -80,6 +80,10 @@
|
|
|
80
80
|
"SuggestionRequest",
|
|
81
81
|
"TaskSubmit",
|
|
82
82
|
"TrustRulesList",
|
|
83
|
+
"TwilioWebhookConfigRequest",
|
|
84
|
+
"TwitterAuthStartRequest",
|
|
85
|
+
"TwitterAuthStatusRequest",
|
|
86
|
+
"TwitterIntegrationConfigRequest",
|
|
83
87
|
"UiSurfaceAction",
|
|
84
88
|
"UiSurfaceUndoRequest",
|
|
85
89
|
"UndoRequest",
|
|
@@ -89,17 +93,20 @@
|
|
|
89
93
|
"UserMessage",
|
|
90
94
|
"VercelApiConfigRequest",
|
|
91
95
|
"WatchObservation",
|
|
96
|
+
"WorkItemApprovePermissionsRequest",
|
|
97
|
+
"WorkItemCancelRequest",
|
|
92
98
|
"WorkItemCompleteRequest",
|
|
93
|
-
"WorkItemCreateRequest",
|
|
94
99
|
"WorkItemDeleteRequest",
|
|
95
100
|
"WorkItemGetRequest",
|
|
96
101
|
"WorkItemOutputRequest",
|
|
102
|
+
"WorkItemPreflightRequest",
|
|
97
103
|
"WorkItemRunTaskRequest",
|
|
98
104
|
"WorkItemUpdateRequest",
|
|
99
105
|
"WorkItemsListRequest"
|
|
100
106
|
],
|
|
101
107
|
"serverMessageTypes": [
|
|
102
108
|
"AcceptStarterBundleResponse",
|
|
109
|
+
"AgentHeartbeatAlert",
|
|
103
110
|
"AppDataResponse",
|
|
104
111
|
"AppFilesChanged",
|
|
105
112
|
"AppPreviewResponse",
|
|
@@ -177,6 +184,7 @@
|
|
|
177
184
|
"SubagentStatusChanged",
|
|
178
185
|
"SuggestionResponse",
|
|
179
186
|
"TaskRouted",
|
|
187
|
+
"TaskRunThreadCreated",
|
|
180
188
|
"TasksChanged",
|
|
181
189
|
"ToolInputDelta",
|
|
182
190
|
"ToolOutputChunk",
|
|
@@ -184,6 +192,10 @@
|
|
|
184
192
|
"ToolUseStart",
|
|
185
193
|
"TraceEvent",
|
|
186
194
|
"TrustRulesListResponse",
|
|
195
|
+
"TwilioWebhookConfigResponse",
|
|
196
|
+
"TwitterAuthResult",
|
|
197
|
+
"TwitterAuthStatusResponse",
|
|
198
|
+
"TwitterIntegrationConfigResponse",
|
|
187
199
|
"UiSurfaceComplete",
|
|
188
200
|
"UiSurfaceDismiss",
|
|
189
201
|
"UiSurfaceShow",
|
|
@@ -199,10 +211,12 @@
|
|
|
199
211
|
"WatchStarted",
|
|
200
212
|
"WatcherEscalation",
|
|
201
213
|
"WatcherNotification",
|
|
202
|
-
"
|
|
214
|
+
"WorkItemApprovePermissionsResponse",
|
|
215
|
+
"WorkItemCancelResponse",
|
|
203
216
|
"WorkItemDeleteResponse",
|
|
204
217
|
"WorkItemGetResponse",
|
|
205
218
|
"WorkItemOutputResponse",
|
|
219
|
+
"WorkItemPreflightResponse",
|
|
206
220
|
"WorkItemRunTaskResponse",
|
|
207
221
|
"WorkItemStatusChanged",
|
|
208
222
|
"WorkItemUpdateResponse",
|
|
@@ -289,6 +303,10 @@
|
|
|
289
303
|
"suggestion_request",
|
|
290
304
|
"task_submit",
|
|
291
305
|
"trust_rules_list",
|
|
306
|
+
"twilio_webhook_config",
|
|
307
|
+
"twitter_auth_start",
|
|
308
|
+
"twitter_auth_status",
|
|
309
|
+
"twitter_integration_config",
|
|
292
310
|
"ui_surface_action",
|
|
293
311
|
"ui_surface_undo",
|
|
294
312
|
"undo",
|
|
@@ -298,17 +316,20 @@
|
|
|
298
316
|
"user_message",
|
|
299
317
|
"vercel_api_config",
|
|
300
318
|
"watch_observation",
|
|
319
|
+
"work_item_approve_permissions",
|
|
320
|
+
"work_item_cancel",
|
|
301
321
|
"work_item_complete",
|
|
302
|
-
"work_item_create",
|
|
303
322
|
"work_item_delete",
|
|
304
323
|
"work_item_get",
|
|
305
324
|
"work_item_output",
|
|
325
|
+
"work_item_preflight",
|
|
306
326
|
"work_item_run_task",
|
|
307
327
|
"work_item_update",
|
|
308
328
|
"work_items_list"
|
|
309
329
|
],
|
|
310
330
|
"serverWireTypes": [
|
|
311
331
|
"accept_starter_bundle_response",
|
|
332
|
+
"agent_heartbeat_alert",
|
|
312
333
|
"app_data_response",
|
|
313
334
|
"app_files_changed",
|
|
314
335
|
"app_preview_response",
|
|
@@ -386,6 +407,7 @@
|
|
|
386
407
|
"subagent_status_changed",
|
|
387
408
|
"suggestion_response",
|
|
388
409
|
"task_routed",
|
|
410
|
+
"task_run_thread_created",
|
|
389
411
|
"tasks_changed",
|
|
390
412
|
"tool_input_delta",
|
|
391
413
|
"tool_output_chunk",
|
|
@@ -393,6 +415,10 @@
|
|
|
393
415
|
"tool_use_start",
|
|
394
416
|
"trace_event",
|
|
395
417
|
"trust_rules_list_response",
|
|
418
|
+
"twilio_webhook_config_response",
|
|
419
|
+
"twitter_auth_result",
|
|
420
|
+
"twitter_auth_status_response",
|
|
421
|
+
"twitter_integration_config_response",
|
|
396
422
|
"ui_surface_complete",
|
|
397
423
|
"ui_surface_dismiss",
|
|
398
424
|
"ui_surface_undo_result",
|
|
@@ -407,10 +433,12 @@
|
|
|
407
433
|
"watch_started",
|
|
408
434
|
"watcher_escalation",
|
|
409
435
|
"watcher_notification",
|
|
410
|
-
"
|
|
436
|
+
"work_item_approve_permissions_response",
|
|
437
|
+
"work_item_cancel_response",
|
|
411
438
|
"work_item_delete_response",
|
|
412
439
|
"work_item_get_response",
|
|
413
440
|
"work_item_output_response",
|
|
441
|
+
"work_item_preflight_response",
|
|
414
442
|
"work_item_run_task_response",
|
|
415
443
|
"work_item_status_changed",
|
|
416
444
|
"work_item_update_response",
|