vellum 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/bun.lock +5 -2
- package/package.json +4 -2
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +299 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +62 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +246 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +163 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +28 -4
- package/src/daemon/ipc-contract.ts +133 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/http-server.ts +108 -20
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +5 -10
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +220 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +77 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
|
@@ -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,9 @@
|
|
|
80
80
|
"SuggestionRequest",
|
|
81
81
|
"TaskSubmit",
|
|
82
82
|
"TrustRulesList",
|
|
83
|
+
"TwitterAuthStartRequest",
|
|
84
|
+
"TwitterAuthStatusRequest",
|
|
85
|
+
"TwitterIntegrationConfigRequest",
|
|
83
86
|
"UiSurfaceAction",
|
|
84
87
|
"UiSurfaceUndoRequest",
|
|
85
88
|
"UndoRequest",
|
|
@@ -89,17 +92,20 @@
|
|
|
89
92
|
"UserMessage",
|
|
90
93
|
"VercelApiConfigRequest",
|
|
91
94
|
"WatchObservation",
|
|
95
|
+
"WorkItemApprovePermissionsRequest",
|
|
96
|
+
"WorkItemCancelRequest",
|
|
92
97
|
"WorkItemCompleteRequest",
|
|
93
|
-
"WorkItemCreateRequest",
|
|
94
98
|
"WorkItemDeleteRequest",
|
|
95
99
|
"WorkItemGetRequest",
|
|
96
100
|
"WorkItemOutputRequest",
|
|
101
|
+
"WorkItemPreflightRequest",
|
|
97
102
|
"WorkItemRunTaskRequest",
|
|
98
103
|
"WorkItemUpdateRequest",
|
|
99
104
|
"WorkItemsListRequest"
|
|
100
105
|
],
|
|
101
106
|
"serverMessageTypes": [
|
|
102
107
|
"AcceptStarterBundleResponse",
|
|
108
|
+
"AgentHeartbeatAlert",
|
|
103
109
|
"AppDataResponse",
|
|
104
110
|
"AppFilesChanged",
|
|
105
111
|
"AppPreviewResponse",
|
|
@@ -177,6 +183,7 @@
|
|
|
177
183
|
"SubagentStatusChanged",
|
|
178
184
|
"SuggestionResponse",
|
|
179
185
|
"TaskRouted",
|
|
186
|
+
"TaskRunThreadCreated",
|
|
180
187
|
"TasksChanged",
|
|
181
188
|
"ToolInputDelta",
|
|
182
189
|
"ToolOutputChunk",
|
|
@@ -184,6 +191,9 @@
|
|
|
184
191
|
"ToolUseStart",
|
|
185
192
|
"TraceEvent",
|
|
186
193
|
"TrustRulesListResponse",
|
|
194
|
+
"TwitterAuthResult",
|
|
195
|
+
"TwitterAuthStatusResponse",
|
|
196
|
+
"TwitterIntegrationConfigResponse",
|
|
187
197
|
"UiSurfaceComplete",
|
|
188
198
|
"UiSurfaceDismiss",
|
|
189
199
|
"UiSurfaceShow",
|
|
@@ -199,10 +209,12 @@
|
|
|
199
209
|
"WatchStarted",
|
|
200
210
|
"WatcherEscalation",
|
|
201
211
|
"WatcherNotification",
|
|
202
|
-
"
|
|
212
|
+
"WorkItemApprovePermissionsResponse",
|
|
213
|
+
"WorkItemCancelResponse",
|
|
203
214
|
"WorkItemDeleteResponse",
|
|
204
215
|
"WorkItemGetResponse",
|
|
205
216
|
"WorkItemOutputResponse",
|
|
217
|
+
"WorkItemPreflightResponse",
|
|
206
218
|
"WorkItemRunTaskResponse",
|
|
207
219
|
"WorkItemStatusChanged",
|
|
208
220
|
"WorkItemUpdateResponse",
|
|
@@ -289,6 +301,9 @@
|
|
|
289
301
|
"suggestion_request",
|
|
290
302
|
"task_submit",
|
|
291
303
|
"trust_rules_list",
|
|
304
|
+
"twitter_auth_start",
|
|
305
|
+
"twitter_auth_status",
|
|
306
|
+
"twitter_integration_config",
|
|
292
307
|
"ui_surface_action",
|
|
293
308
|
"ui_surface_undo",
|
|
294
309
|
"undo",
|
|
@@ -298,17 +313,20 @@
|
|
|
298
313
|
"user_message",
|
|
299
314
|
"vercel_api_config",
|
|
300
315
|
"watch_observation",
|
|
316
|
+
"work_item_approve_permissions",
|
|
317
|
+
"work_item_cancel",
|
|
301
318
|
"work_item_complete",
|
|
302
|
-
"work_item_create",
|
|
303
319
|
"work_item_delete",
|
|
304
320
|
"work_item_get",
|
|
305
321
|
"work_item_output",
|
|
322
|
+
"work_item_preflight",
|
|
306
323
|
"work_item_run_task",
|
|
307
324
|
"work_item_update",
|
|
308
325
|
"work_items_list"
|
|
309
326
|
],
|
|
310
327
|
"serverWireTypes": [
|
|
311
328
|
"accept_starter_bundle_response",
|
|
329
|
+
"agent_heartbeat_alert",
|
|
312
330
|
"app_data_response",
|
|
313
331
|
"app_files_changed",
|
|
314
332
|
"app_preview_response",
|
|
@@ -386,6 +404,7 @@
|
|
|
386
404
|
"subagent_status_changed",
|
|
387
405
|
"suggestion_response",
|
|
388
406
|
"task_routed",
|
|
407
|
+
"task_run_thread_created",
|
|
389
408
|
"tasks_changed",
|
|
390
409
|
"tool_input_delta",
|
|
391
410
|
"tool_output_chunk",
|
|
@@ -393,6 +412,9 @@
|
|
|
393
412
|
"tool_use_start",
|
|
394
413
|
"trace_event",
|
|
395
414
|
"trust_rules_list_response",
|
|
415
|
+
"twitter_auth_result",
|
|
416
|
+
"twitter_auth_status_response",
|
|
417
|
+
"twitter_integration_config_response",
|
|
396
418
|
"ui_surface_complete",
|
|
397
419
|
"ui_surface_dismiss",
|
|
398
420
|
"ui_surface_undo_result",
|
|
@@ -407,10 +429,12 @@
|
|
|
407
429
|
"watch_started",
|
|
408
430
|
"watcher_escalation",
|
|
409
431
|
"watcher_notification",
|
|
410
|
-
"
|
|
432
|
+
"work_item_approve_permissions_response",
|
|
433
|
+
"work_item_cancel_response",
|
|
411
434
|
"work_item_delete_response",
|
|
412
435
|
"work_item_get_response",
|
|
413
436
|
"work_item_output_response",
|
|
437
|
+
"work_item_preflight_response",
|
|
414
438
|
"work_item_run_task_response",
|
|
415
439
|
"work_item_status_changed",
|
|
416
440
|
"work_item_update_response",
|