Files
kandev/apps/backend/pkg/websocket/actions.go
T
João Salavisa 97c1f689e4 feat(workflow): explicit step-completion signal for auto-advance (#1276)
* feat(workflow): explicit step-completion signal for auto-advance

Adds `step_complete_kandev` MCP tool + `WorkflowStep.auto_advance_requires_signal`
column so workflow steps can gate their `on_turn_complete` transitions on an
agent-emitted completion signal instead of inferring "done" from any turn-end.
See docs/decisions/0015-explicit-completion-signal-for-auto-advance.md for the
motivation, alternatives considered, and trade-offs.

* fix(workflow): address PR review feedback for explicit completion signal

- Dedup `LoadPendingStepSignal` into `internal/task/models` so the MCP
  handler (write site) and the orchestrator (read site) share one decoder.
- Gate the out-of-band subscriber on `WorkflowStep.AutoAdvanceRequiresSignal`
  so `step_complete_kandev` is not an unintended manual-advance path for
  signalless steps.
- Fail closed on step-load errors in `processOnTurnCompleteViaEngine`: a
  loader hiccup must block the transition rather than silently bypass the
  gate.
- Reject `step_complete_kandev` for terminal sessions
  (Completed / Failed / Cancelled) so the agent gets a clear error instead
  of `accepted: true` followed by a no-op.
- Surface bus-publish failures back to the agent instead of swallowing
  them — the idempotency guard makes the retry safe and prevents the
  session from getting stuck.
- New `TestOnStepCompletionSignaled` covers the subscriber paths
  (RUNNING / WAITING+match / stale step / not signal-gated).
- ADR doc fixes: payload field list now matches the wire shape, writer
  ownership is documented, the unsubstantiated 250ms-debounce claim is
  removed, and the late-signal re-close race is recorded as a deferred
  open question.
- Drop the new toggle's HelpTip in favour of an inline description — the
  shadcn tooltip portal was intercepting clicks on the agent-profile
  dropdown options in the workflow settings E2E.

* fix(workflow): round-2 review fixes for explicit completion signal

- Split `handleStepComplete` into `resolveStepCompleteTarget` +
  `publishStepCompletionEvent` helpers so the entrypoint clears funlen
  (88 → under 80 lines) and the two halves are independently testable.
- Classify session/task load errors instead of returning `NotFound` for
  every failure: use the exported `taskrepo.ErrTaskNotFound` sentinel for
  task lookups, substring-match for the session repo (no sentinel today),
  and surface anything else as `InternalError` with the raw error logged
  but not leaked to the agent — so a transient DB hiccup triggers a retry
  rather than session abandonment.
- Pin `step_complete_kandev` explicitly in the mode-specific tool-count
  tests: `Contains` in `ModeTask`, `NotContains` in `ModeConfig` and
  `ModeOffice`, so accidental dereg or cross-mode registration is caught
  by the diff instead of by a still-equal count.

* fix(workflow): round-3 review fixes + defer pipeline-editor toggle

- Defer the "Wait for agent completion signal" sub-toggle to a follow-up
  PR. Adding it to the step config card shifts the layout enough that the
  agent profile select's Radix popper opens into a sibling tooltip's hover
  path, breaking workflow-agent-profile.spec.ts on shard 7. The backend
  column ships in this PR — workflows can opt in via the YAML import or
  the MCP `update_workflow_step_kandev` tool — and the UI affordance gets
  its own iteration where the test interference can be investigated
  without blocking the rest of the change.
- Re-publish the bus event on the `already_signaled` short-circuit when
  the session is WAITING_FOR_INPUT. Without this, an agent's retry after
  the first call's publish failed would short-circuit on the persisted
  bag, never fire the subscriber, and leave the session stuck.
- handleStepComplete docstring: drop the non-existent feature-flag claim
  and the "audit entry" wording — gating is purely
  `WorkflowStep.AutoAdvanceRequiresSignal` and the bag is cleared on the
  next turn start with no separate audit trail.
- `step_complete_kandev` tool description: same fix — clarify that the
  call succeeds for non-gated steps but the signal is discarded on the
  next turn start (no audit history to query later).

* test(workflow): cover handleStepComplete + reword defer-comment

- Add `TestHandleStepComplete_*` (6 subtests, all passing) covering the
  uncovered handler paths flagged by Claude/cubic on round 3:
  missing-fields validation, session-does-not-belong-to-task ownership
  guard, terminal-session rejection (Completed/Failed/Cancelled),
  first-call happy path (bag persisted + bus payload shape pinned), dedup
  while RUNNING (no re-publish), and dedup while WAITING_FOR_INPUT (the
  retry-after-publish-failure path — must re-publish so the subscriber
  fires).
- Reword the deferred-toggle comment to drop the YAML-export claim:
  the workflow export model doesn't yet serialize
  `auto_advance_requires_signal`, so MCP `update_workflow_step_kandev`
  is the only configuration path that round-trips today. Note the
  follow-up needs to extend the export schema alongside the UI toggle.

* test(workflow): pin signaled_at + drop redundant GetTaskSession on bag-clear

- Assert `signaled_at` is present and parses as RFC3339Nano in
  `TestHandleStepComplete_FirstCallAccepted`. The field is part of the
  handler's documented response contract; pinning it catches accidental
  drop/rename regressions.
- Add `clearPendingStepSignalByID(ctx, sessionID)` as a thin DB-only
  variant of `clearPendingStepSignal` and use it from
  `executeStepTransition`, where the session struct isn't read after the
  clear. Avoids an extra `GetTaskSession` round-trip per transition.
  `clearPendingStepSignal(session)` still exists for the
  in-memory-mutation case (subscriber's stale-step path).

* fix(workflow): subscriber stale-clear guard + ADR/comment drift fixes

- onStepCompletionSignaled's stale-step path now reads the bag and only
  clears it when its current StepID matches the (stale) event's stepID.
  Previously the unconditional clear could destroy a valid signal that
  the new step's agent had just written when sessions are reused across
  steps without auto_start_agent. (Greptile P1.)
- Drop the "feature flag" phrase from WorkflowStep.AutoAdvanceRequiresSignal
  docstring — leftover from an earlier draft. The gating is purely the
  boolean field; the global flag was removed two rounds ago.
- Merge the two adjacent `if triggerOnEnter` blocks in
  executeStepTransition so the full triggerOnEnter=true path reads top-
  to-bottom in one place. (CodeRabbit + cubic.)
- ADR 0015 Mechanism section: re-label the SessionStepHistory.Metadata
  audit annotation, the `step_complete_signaled` system message, and the
  expvar telemetry counters as deferred. They're planned shapes, not
  current behaviour — calling them "Audit" / "Chat visibility" /
  "Telemetry" in present tense was misleading for future readers.

* fix(workflow): drop subscriber complexity + reload session on stale-clear

- Extract `parseStepCompletionEvent` and `stepIsSignalGated` helpers from
  `onStepCompletionSignaled`. Cyclomatic complexity goes 17 → under 15
  (cyclop on new code, CI lint regression after the round-6
  stale-clear guard).
- The stale-step branch now re-loads the session from the DB before
  deciding whether to clear the bag. The previous round-6 fix checked
  the local `session.Metadata` snapshot, which can be older than a
  concurrent write from the new step's agent — that check could see a
  stale signal that had since been overwritten with a valid one and
  erase the new bag entry. The narrower load-on-clear is cheap and
  closes the cubic P1 hole. (The broader race in
  `processOnTurnCompleteViaEngine` reading session.Metadata is the
  monotonic-marker open question in ADR 0015; out of scope here.)

* test(workflow): pin stale-event guard's preserve-current-bag side + dedup StringFromAny

- Add `TestOnStepCompletionSignaled` subtest "stale event → valid bag
  for CURRENT step is preserved" — fires a stale step-A event while
  the bag holds a valid step-B signal, asserts the bag still contains
  StepID=step_b after the subscriber runs. Pins the negative side of
  the round-6 `existing.StepID == stepID` guard, so a regression that
  drops or inverts the guard (and would silently leave signal-gated
  steps stuck) fails the test instead of slipping through.
- Promote `pendingSignalString` to exported `models.StringFromAny`
  and reuse it from the orchestrator subscriber's
  `parseStepCompletionEvent`. Eliminates the byte-for-byte duplicate
  helper across the two packages; the bag-shape decoder and the bus-
  event decoder now share one source of truth for "missing string".

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-06-05 18:32:27 +01:00

563 lines
27 KiB
Go

package websocket
// Action constants for WebSocket messages
const (
// Health
ActionHealthCheck = "health.check"
// Workflow actions
ActionWorkflowList = "workflow.list"
ActionWorkflowCreate = "workflow.create"
ActionWorkflowGet = "workflow.get"
ActionWorkflowUpdate = "workflow.update"
ActionWorkflowDelete = "workflow.delete"
ActionWorkflowReorder = "workflow.reorder"
// Workspace actions
ActionWorkspaceList = "workspace.list"
ActionWorkspaceCreate = "workspace.create"
ActionWorkspaceGet = "workspace.get"
ActionWorkspaceUpdate = "workspace.update"
ActionWorkspaceDelete = "workspace.delete"
// Repository actions
ActionRepositoryList = "repository.list"
ActionRepositoryCreate = "repository.create"
ActionRepositoryGet = "repository.get"
ActionRepositoryUpdate = "repository.update"
ActionRepositoryDelete = "repository.delete"
// Repository Script actions
ActionRepositoryScriptList = "repository.script.list"
ActionRepositoryScriptCreate = "repository.script.create"
ActionRepositoryScriptGet = "repository.script.get"
ActionRepositoryScriptUpdate = "repository.script.update"
ActionRepositoryScriptDelete = "repository.script.delete"
// Executor actions
ActionExecutorList = "executor.list"
ActionExecutorCreate = "executor.create"
ActionExecutorGet = "executor.get"
ActionExecutorUpdate = "executor.update"
ActionExecutorDelete = "executor.delete"
// Executor profile actions
ActionExecutorProfileList = "executor.profile.list"
ActionExecutorProfileListAll = "executor.profile.list_all"
ActionExecutorProfileCreate = "executor.profile.create"
ActionExecutorProfileGet = "executor.profile.get"
ActionExecutorProfileUpdate = "executor.profile.update"
ActionExecutorProfileDelete = "executor.profile.delete"
// Environment actions
ActionEnvironmentList = "environment.list"
ActionEnvironmentCreate = "environment.create"
ActionEnvironmentGet = "environment.get"
ActionEnvironmentUpdate = "environment.update"
ActionEnvironmentDelete = "environment.delete"
// Task actions
ActionTaskList = "task.list"
ActionTaskCreate = "task.create"
ActionTaskGet = "task.get"
ActionTaskUpdate = "task.update"
ActionTaskRepoUpdate = "task.repository.update"
ActionTaskDelete = "task.delete"
ActionTaskMove = "task.move"
ActionTaskState = "task.state"
ActionTaskArchive = "task.archive"
ActionTaskPlanCreate = "task.plan.create"
ActionTaskPlanGet = "task.plan.get"
ActionTaskPlanUpdate = "task.plan.update"
ActionTaskPlanDelete = "task.plan.delete"
ActionTaskPlanRevisionsList = "task.plan.revisions.list"
ActionTaskPlanRevisionGet = "task.plan.revision.get"
ActionTaskPlanRevert = "task.plan.revert"
ActionTaskSessionList = "task.session.list"
ActionTaskSessionStatus = "task.session.status"
// Unified session launch
ActionSessionLaunch = "session.launch"
ActionSessionEnsure = "session.ensure"
ActionSessionRecover = "session.recover"
ActionSessionResetContext = "session.reset_context"
ActionSessionStop = "session.stop"
ActionSessionDelete = "session.delete"
ActionSessionSetPrimary = "session.set_primary"
ActionSessionSetPlanMode = "session.set_plan_mode"
// Agent actions
ActionAgentList = "agent.list"
ActionAgentLaunch = "agent.launch"
ActionAgentStatus = "agent.status"
ActionAgentLogs = "agent.logs"
ActionAgentStop = "agent.stop"
ActionAgentPrompt = "agent.prompt"
ActionAgentCancel = "agent.cancel"
ActionTaskSession = "task.session"
ActionAgentTypes = "agent.types"
// Agent passthrough actions
ActionAgentStdin = "agent.stdin" // Send input to agent process stdin (passthrough mode)
ActionAgentStdout = "agent.stdout" // Agent stdout notification (passthrough mode)
ActionAgentResize = "agent.resize" // Resize agent PTY (passthrough mode)
// Orchestrator actions
ActionOrchestratorStatus = "orchestrator.status"
ActionOrchestratorQueue = "orchestrator.queue"
ActionOrchestratorStop = "orchestrator.stop"
// Message Queue actions
ActionMessageQueueAdd = "message.queue.add"
ActionMessageQueueCancel = "message.queue.cancel" // Clears the entire queue for a session
ActionMessageQueueGet = "message.queue.get"
ActionMessageQueueUpdate = "message.queue.update"
ActionMessageQueueAppend = "message.queue.append"
ActionMessageQueueDrain = "message.queue.drain" // Dispatch one queued entry now when the session is promptable
ActionMessageQueueRemove = "message.queue.remove" // Delete a single entry by id
ActionMessageQueueStatusChanged = "message.queue.status_changed" // Notification: queue status changed
// Workflow template/step actions
ActionWorkflowTemplateList = "workflow.template.list"
ActionWorkflowTemplateGet = "workflow.template.get"
ActionWorkflowStepList = "workflow.step.list"
ActionWorkflowStepGet = "workflow.step.get"
ActionWorkflowStepCreate = "workflow.step.create"
ActionWorkflowHistoryList = "workflow.history.list"
// Subscription actions
ActionTaskSubscribe = "task.subscribe"
ActionTaskUnsubscribe = "task.unsubscribe"
ActionSessionSubscribe = "session.subscribe"
ActionSessionUnsubscribe = "session.unsubscribe"
// Focus signals are layered on top of subscriptions to indicate which
// session the user is actively viewing (task details page or task panel),
// vs merely subscribed (sidebar diff badges). Drives backend polling tier.
ActionSessionFocus = "session.focus"
ActionSessionUnfocus = "session.unfocus"
ActionUserSubscribe = "user.subscribe"
ActionUserUnsubscribe = "user.unsubscribe"
ActionRunSubscribe = "run.subscribe"
ActionRunUnsubscribe = "run.unsubscribe"
// Message actions
ActionMessageAdd = "message.add"
ActionMessageGet = "message.get"
ActionMessageList = "message.list"
ActionMessageSearch = "message.search"
// Notification actions (server -> client)
ActionACPProgress = "acp.progress"
ActionACPLog = "acp.log"
ActionACPResult = "acp.result"
ActionACPError = "acp.error"
ActionACPStatus = "acp.status"
ActionACPHeartbeat = "acp.heartbeat"
ActionTaskCreated = "task.created"
ActionTaskUpdated = "task.updated"
ActionTaskDeleted = "task.deleted"
ActionTaskStateChanged = "task.state_changed"
ActionTaskPlanCreated = "task.plan.created"
ActionTaskPlanUpdated = "task.plan.updated"
ActionTaskPlanDeleted = "task.plan.deleted"
ActionTaskPlanRevisionCreated = "task.plan.revision.created"
ActionTaskPlanReverted = "task.plan.reverted"
ActionAgentUpdated = "agent.updated"
ActionAgentAvailableUpdated = "agent.available.updated"
ActionAgentInstallStarted = "agent.install.started"
ActionAgentInstallOutput = "agent.install.output"
ActionAgentInstallFinished = "agent.install.finished"
ActionWorkspaceCreated = "workspace.created"
ActionWorkspaceUpdated = "workspace.updated"
ActionWorkspaceDeleted = "workspace.deleted"
ActionWorkflowCreated = "workflow.created"
ActionWorkflowUpdated = "workflow.updated"
ActionWorkflowDeleted = "workflow.deleted"
ActionWorkflowStepCreated = "workflow.step.created"
ActionWorkflowStepUpdated = "workflow.step.updated"
ActionWorkflowStepDeleted = "workflow.step.deleted"
ActionSessionMessageAdded = "session.message.added"
ActionSessionMessageUpdated = "session.message.updated"
ActionSessionStateChanged = "session.state_changed"
ActionSessionWaitingForInput = "session.waiting_for_input"
ActionSessionAgentctlStarting = "session.agentctl_starting"
ActionSessionAgentctlReady = "session.agentctl_ready"
ActionSessionAgentctlError = "session.agentctl_error"
ActionSessionTurnStarted = "session.turn.started"
ActionSessionTurnCompleted = "session.turn.completed"
ActionSessionAvailableCommands = "session.available_commands"
ActionSessionModeChanged = "session.mode_changed"
ActionSessionAgentCapabilities = "session.agent_capabilities"
ActionSessionModelsUpdated = "session.models_updated"
ActionSessionSetMode = "session.set_mode"
ActionSessionTodosUpdated = "session.todos_updated"
ActionSessionPromptUsage = "session.prompt_usage"
ActionSessionPollModeChanged = "session.poll_mode_changed"
ActionInputRequested = "input.requested"
ActionRepositoryCreated = "repository.created"
ActionRepositoryUpdated = "repository.updated"
ActionRepositoryDeleted = "repository.deleted"
ActionRepositoryScriptCreated = "repository.script.created"
ActionRepositoryScriptUpdated = "repository.script.updated"
ActionRepositoryScriptDeleted = "repository.script.deleted"
ActionExecutorCreated = "executor.created"
ActionExecutorUpdated = "executor.updated"
ActionExecutorDeleted = "executor.deleted"
ActionEnvironmentCreated = "environment.created"
ActionEnvironmentUpdated = "environment.updated"
ActionEnvironmentDeleted = "environment.deleted"
ActionExecutorProfileCreated = "executor.profile.created"
ActionExecutorProfileUpdated = "executor.profile.updated"
ActionExecutorProfileDeleted = "executor.profile.deleted"
ActionExecutorPrepareProgress = "executor.prepare.progress"
ActionExecutorPrepareCompleted = "executor.prepare.completed"
ActionAgentProfileDeleted = "agent.profile.deleted"
ActionAgentProfileCreated = "agent.profile.created"
ActionAgentProfileUpdated = "agent.profile.updated"
// Permission request actions (agent -> user -> agent)
ActionPermissionRequested = "permission.requested" // Agent requesting permission
ActionPermissionRespond = "permission.respond" // User responding to permission request
// Workspace file operations
ActionWorkspaceFileTreeGet = "workspace.tree.get"
ActionWorkspaceFileContentGet = "workspace.file.get"
ActionWorkspaceFileContentGetRef = "workspace.file.get_at_ref"
ActionWorkspaceFileContentUpdate = "workspace.file.update"
ActionWorkspaceFileCreate = "workspace.file.create"
ActionWorkspaceFileDelete = "workspace.file.delete"
ActionWorkspaceFileRename = "workspace.file.rename"
ActionWorkspaceFilesSearch = "workspace.files.search"
ActionWorkspaceFileChanges = "session.workspace.file.changes" // Notification
// Shell actions
ActionShellStatus = "session.shell.status" // Get shell status
ActionShellSubscribe = "shell.subscribe" // Subscribe to shell output
ActionShellInput = "shell.input" // Send input to shell
ActionSessionShellOutput = "session.shell.output" // Shell output notification (also used for exit with type: "exit")
// User shell actions (independent terminal tabs)
ActionUserShellList = "user_shell.list" // List user shells for a task (DB rows + agentctl probes)
ActionUserShellCreate = "user_shell.create" // Create a new user shell terminal (assigns ID + seq)
ActionUserShellStop = "user_shell.stop" // Legacy alias of destroy; kept for one release
ActionUserShellRename = "user_shell.rename" // Rename an ordinary user shell (set/clear custom_name)
ActionUserShellPark = "user_shell.park" // Hide tab; PTY continues running
ActionUserShellResume = "user_shell.resume" // Unhide a parked tab
ActionUserShellDestroy = "user_shell.destroy" // Stop the PTY and delete the row
// Session file review actions
ActionSessionFileReviewGet = "session.file_review.get" // Get all file reviews for a session
ActionSessionFileReviewUpdate = "session.file_review.update" // Upsert a single file review
ActionSessionFileReviewReset = "session.file_review.reset" // Delete all reviews for a session
// Session git actions (requests)
ActionSessionGitSnapshots = "session.git.snapshots" // Get git snapshots for a session
ActionSessionGitCommits = "session.git.commits" // Get commits for a session
ActionSessionCumulativeDiff = "session.cumulative_diff" // Get cumulative diff from base branch
ActionSessionCommitDiff = "session.commit_diff" // Get diff for a specific commit
// Session git event (unified notification)
ActionSessionGitEvent = "session.git.event" // Notification: unified git event
// Process runner actions
ActionSessionProcessOutput = "session.process.output"
ActionSessionProcessStatus = "session.process.status"
// Git worktree actions
ActionWorktreePull = "worktree.pull" // Pull from remote
ActionWorktreePush = "worktree.push" // Push to remote
ActionWorktreeRebase = "worktree.rebase" // Rebase onto base branch
ActionWorktreeMerge = "worktree.merge" // Merge base branch into worktree
ActionWorktreeAbort = "worktree.abort" // Abort in-progress merge or rebase
ActionWorktreeCommit = "worktree.commit" // Commit changes
ActionWorktreeStage = "worktree.stage" // Stage files for commit
ActionWorktreeUnstage = "worktree.unstage" // Unstage files from index
ActionWorktreeDiscard = "worktree.discard" // Discard changes to files
ActionWorktreeCreatePR = "worktree.create_pr" // Create a pull request
ActionWorktreeRevertCommit = "worktree.revert_commit" // Revert a commit (staged, no new commit)
ActionWorktreeRenameBranch = "worktree.rename_branch" // Rename the current branch
ActionWorktreeReset = "worktree.reset" // Reset HEAD to a commit (soft/hard)
// User actions
ActionUserGet = "user.get"
ActionUserSettingsUpdate = "user.settings.update"
ActionUserSettingsUpdated = "user.settings.updated"
// System maintenance jobs (VACUUM, factory reset, snapshot create/restore,
// disk walk). Broadcast to all connected clients so the System pages can
// render progress.
ActionSystemJobUpdate = "system.job.update"
// VS Code server actions
ActionVscodeStart = "vscode.start" // Start code-server for a session
ActionVscodeStop = "vscode.stop" // Stop code-server for a session
ActionVscodeStatus = "vscode.status" // Get code-server status for a session
ActionVscodeOpenFile = "vscode.openFile" // Open a file in code-server for a session
// Port actions
ActionPortList = "port.list" // List listening ports on a remote executor
ActionPortTunnelStart = "port.tunnel.start" // Start a port tunnel
ActionPortTunnelStop = "port.tunnel.stop" // Stop a port tunnel
ActionPortTunnelList = "port.tunnel.list" // List active port tunnels
// Secret actions
ActionSecretList = "secrets.list"
ActionSecretCreate = "secrets.create"
ActionSecretUpdate = "secrets.update"
ActionSecretDelete = "secrets.delete"
ActionSecretReveal = "secrets.reveal"
// Sprites actions
ActionSpritesStatus = "sprites.status"
ActionSpritesInstancesList = "sprites.instances.list"
ActionSpritesInstancesDestroy = "sprites.instances.destroy"
ActionSpritesTest = "sprites.test"
ActionSpritesNetworkPolicyGet = "sprites.network_policy.get"
ActionSpritesNetworkPolicyUpdate = "sprites.network_policy.update"
// MCP tool actions (agentctl -> backend via WS tunnel)
ActionMCPListWorkspaces = "mcp.list_workspaces"
ActionMCPListWorkflows = "mcp.list_workflows"
ActionMCPListWorkflowSteps = "mcp.list_workflow_steps"
ActionMCPListRepositories = "mcp.list_repositories"
ActionMCPListTasks = "mcp.list_tasks"
ActionMCPCreateTask = "mcp.create_task"
ActionMCPUpdateTask = "mcp.update_task"
ActionMCPAddBranchToTask = "mcp.add_branch_to_task"
ActionMCPUpdateRepositoryBaseBranch = "mcp.update_repository_base_branch"
ActionMCPStepComplete = "mcp.step_complete" // ADR 0015: agent-emitted explicit completion signal
ActionMCPAskUserQuestion = "mcp.ask_user_question"
ActionMCPCreateTaskPlan = "mcp.create_task_plan"
ActionMCPGetTaskPlan = "mcp.get_task_plan"
ActionMCPUpdateTaskPlan = "mcp.update_task_plan"
ActionMCPDeleteTaskPlan = "mcp.delete_task_plan"
ActionMCPClarificationTimeout = "mcp.clarification_timeout"
// Office task handoffs (cross-task context).
ActionMCPListRelatedTasks = "mcp.list_related_tasks"
ActionMCPListTaskDocuments = "mcp.list_task_documents"
ActionMCPGetTaskDocument = "mcp.get_task_document"
ActionMCPWriteTaskDocument = "mcp.write_task_document"
// Config-mode MCP actions (agent-native configuration)
ActionMCPCreateWorkflow = "mcp.create_workflow"
ActionMCPUpdateWorkflow = "mcp.update_workflow"
ActionMCPDeleteWorkflow = "mcp.delete_workflow"
ActionMCPImportWorkflow = "mcp.import_workflow"
ActionMCPCreateWorkflowStep = "mcp.create_workflow_step"
ActionMCPUpdateWorkflowStep = "mcp.update_workflow_step"
ActionMCPDeleteWorkflowStep = "mcp.delete_workflow_step"
ActionMCPReorderWorkflowStep = "mcp.reorder_workflow_steps"
ActionMCPListAgents = "mcp.list_agents"
ActionMCPUpdateAgent = "mcp.update_agent"
ActionMCPListAgentProfiles = "mcp.list_agent_profiles"
ActionMCPCreateAgentProfile = "mcp.create_agent_profile"
ActionMCPUpdateAgentProfile = "mcp.update_agent_profile"
ActionMCPDeleteAgentProfile = "mcp.delete_agent_profile"
ActionMCPGetMcpConfig = "mcp.get_mcp_config"
ActionMCPUpdateMcpConfig = "mcp.update_mcp_config"
ActionMCPListExecutors = "mcp.list_executors"
ActionMCPListExecutorProfiles = "mcp.list_executor_profiles"
ActionMCPCreateExecutorProfile = "mcp.create_executor_profile"
ActionMCPUpdateExecutorProfile = "mcp.update_executor_profile"
ActionMCPDeleteExecutorProfile = "mcp.delete_executor_profile"
ActionMCPMoveTask = "mcp.move_task"
ActionMCPDeleteTask = "mcp.delete_task"
ActionMCPArchiveTask = "mcp.archive_task"
ActionMCPUpdateTaskState = "mcp.update_task_state"
ActionMCPMessageTask = "mcp.message_task"
ActionMCPGetTaskConversation = "mcp.get_task_conversation"
)
// GitHub integration actions
const (
ActionGitHubStatus = "github.status"
ActionGitHubTaskPRsList = "github.task_prs.list"
ActionGitHubTaskPRGet = "github.task_pr.get"
ActionGitHubPRFeedbackGet = "github.pr_feedback.get"
ActionGitHubReviewWatchesList = "github.review_watches.list"
ActionGitHubReviewWatchCreate = "github.review_watches.create"
ActionGitHubReviewWatchUpdate = "github.review_watches.update"
ActionGitHubReviewWatchDelete = "github.review_watches.delete"
ActionGitHubReviewTrigger = "github.review_watches.trigger"
ActionGitHubReviewTriggerAll = "github.review_watches.trigger_all"
ActionGitHubPRWatchesList = "github.pr_watches.list"
ActionGitHubPRWatchDelete = "github.pr_watches.delete"
ActionGitHubPRFilesGet = "github.pr_files.get"
ActionGitHubPRCommitsGet = "github.pr_commits.get"
ActionGitHubTaskPRUpdated = "github.task_pr.updated" // Notification
ActionGitHubRateLimitUpdated = "github.rate_limit.updated" // Notification
ActionGitHubPRFeedbackNotify = "github.pr_feedback.notify" // Notification
ActionGitHubNewReviewPRNotify = "github.new_review_pr.notify" // Notification
ActionGitHubTaskPRSync = "github.task_pr.sync"
ActionGitHubStats = "github.stats"
ActionGitHubCheckSessionPR = "github.check_session_pr"
// Issue watch actions
ActionGitHubIssueWatchesList = "github.issue_watches.list"
ActionGitHubIssueWatchCreate = "github.issue_watches.create"
ActionGitHubIssueWatchUpdate = "github.issue_watches.update"
ActionGitHubIssueWatchDelete = "github.issue_watches.delete"
ActionGitHubIssueTrigger = "github.issue_watches.trigger"
ActionGitHubIssueTriggerAll = "github.issue_watches.trigger_all"
ActionGitHubNewIssueNotify = "github.new_issue.notify" // Notification
// Action preset actions for the /github page quick-launch prompts.
ActionGitHubActionPresetsList = "github.action_presets.list"
ActionGitHubActionPresetsUpdate = "github.action_presets.update"
ActionGitHubActionPresetsReset = "github.action_presets.reset"
// Manual cleanup sweeps over all dedup rows (review/issue). Used by the
// settings-page button so users can drain piled-up tasks on demand.
ActionGitHubCleanupReviewTasks = "github.cleanup.review_tasks"
ActionGitHubCleanupIssueTasks = "github.cleanup.issue_tasks"
)
// GitLab integration actions
const (
ActionGitLabStatus = "gitlab.status"
ActionGitLabTaskMRsList = "gitlab.task_mrs.list"
ActionGitLabTaskMRGet = "gitlab.task_mr.get"
ActionGitLabMRFeedbackGet = "gitlab.mr_feedback.get"
ActionGitLabReviewWatchesList = "gitlab.review_watches.list"
ActionGitLabReviewWatchCreate = "gitlab.review_watches.create"
ActionGitLabReviewWatchUpdate = "gitlab.review_watches.update"
ActionGitLabReviewWatchDelete = "gitlab.review_watches.delete"
ActionGitLabReviewTrigger = "gitlab.review_watches.trigger"
ActionGitLabReviewTriggerAll = "gitlab.review_watches.trigger_all"
ActionGitLabMRWatchesList = "gitlab.mr_watches.list"
ActionGitLabMRWatchDelete = "gitlab.mr_watches.delete"
ActionGitLabMRFilesGet = "gitlab.mr_files.get"
ActionGitLabMRCommitsGet = "gitlab.mr_commits.get"
ActionGitLabTaskMRUpdated = "gitlab.task_mr.updated" // Notification
ActionGitLabMRFeedbackNotify = "gitlab.mr_feedback.notify" // Notification
ActionGitLabNewReviewMRNotify = "gitlab.new_review_mr.notify" // Notification
ActionGitLabTaskMRSync = "gitlab.task_mr.sync"
ActionGitLabStats = "gitlab.stats"
ActionGitLabMRMerge = "gitlab.mr.merge"
ActionGitLabMRApprove = "gitlab.mr.approve"
ActionGitLabMRUnapprove = "gitlab.mr.unapprove"
ActionGitLabMRSetLabels = "gitlab.mr.set_labels"
ActionGitLabMRSetAssignees = "gitlab.mr.set_assignees"
ActionGitLabMRDiscussionNew = "gitlab.mr.discussion.new"
ActionGitLabMRDiscussionResolve = "gitlab.mr.discussion.resolve"
ActionGitLabProjectMergeMethodsGet = "gitlab.project.merge_methods.get"
// Issue watch actions
ActionGitLabIssueWatchesList = "gitlab.issue_watches.list"
ActionGitLabIssueWatchCreate = "gitlab.issue_watches.create"
ActionGitLabIssueWatchUpdate = "gitlab.issue_watches.update"
ActionGitLabIssueWatchDelete = "gitlab.issue_watches.delete"
ActionGitLabIssueTrigger = "gitlab.issue_watches.trigger"
ActionGitLabIssueTriggerAll = "gitlab.issue_watches.trigger_all"
ActionGitLabNewIssueNotify = "gitlab.new_issue.notify" // Notification
// Action preset actions for the /gitlab page quick-launch prompts.
ActionGitLabActionPresetsList = "gitlab.action_presets.list"
ActionGitLabActionPresetsUpdate = "gitlab.action_presets.update"
ActionGitLabActionPresetsReset = "gitlab.action_presets.reset"
// Project discovery / autocomplete.
ActionGitLabListUserProjects = "gitlab.projects.list"
ActionGitLabSearchProjects = "gitlab.projects.search"
ActionGitLabProjectBranches = "gitlab.project.branches"
// Manual cleanup sweeps.
ActionGitLabCleanupReviewTasks = "gitlab.cleanup.review_tasks"
ActionGitLabCleanupIssueTasks = "gitlab.cleanup.issue_tasks"
)
// Jira integration actions
const (
ActionJiraConfigGet = "jira.config.get"
ActionJiraConfigSet = "jira.config.set"
ActionJiraConfigDelete = "jira.config.delete"
ActionJiraConfigTest = "jira.config.test"
ActionJiraTicketGet = "jira.ticket.get"
ActionJiraTicketTransition = "jira.ticket.transition"
ActionJiraProjectsList = "jira.projects.list"
)
// Linear integration actions
const (
ActionLinearConfigGet = "linear.config.get"
ActionLinearConfigSet = "linear.config.set"
ActionLinearConfigDelete = "linear.config.delete"
ActionLinearConfigTest = "linear.config.test"
ActionLinearIssueGet = "linear.issue.get"
ActionLinearIssueTransition = "linear.issue.transition"
ActionLinearTeamsList = "linear.teams.list"
)
// Slack integration actions
const (
ActionSlackConfigGet = "slack.config.get"
ActionSlackConfigSet = "slack.config.set"
ActionSlackConfigDelete = "slack.config.delete"
ActionSlackConfigTest = "slack.config.test"
)
// Office notification actions (server -> client)
const (
ActionOfficeTaskUpdated = "office.task.updated"
ActionOfficeTaskCreated = "office.task.created"
ActionOfficeTaskMoved = "office.task.moved"
ActionOfficeTaskStatus = "office.task.status_changed"
ActionOfficeTaskDecision = "office.task.decision_recorded"
ActionOfficeTaskReview = "office.task.review_requested"
ActionOfficeCommentCreated = "office.comment.created"
ActionOfficeAgentCompleted = "office.agent.completed"
ActionOfficeAgentFailed = "office.agent.failed"
ActionOfficeAgentUpdated = "office.agent.updated"
ActionOfficeApprovalCreated = "office.approval.created"
ActionOfficeApprovalResolved = "office.approval.resolved"
ActionOfficeCostRecorded = "office.cost.recorded"
ActionOfficeRunQueued = "office.run.queued"
ActionOfficeRunProcessed = "office.run.processed"
ActionOfficeRoutineTriggered = "office.routine.triggered"
ActionOfficeActivityCreated = "office.activity.created"
ActionRunEventAppended = "run.event.appended"
// Office provider-routing events.
ActionOfficeProviderHealthChanged = "office.provider.health_changed"
ActionOfficeRouteAttemptAppended = "office.route_attempt.appended"
ActionOfficeRoutingSettingsUpdated = "office.routing.settings_updated"
)
// Automation actions
const (
ActionAutomationList = "automation.list"
ActionAutomationGet = "automation.get"
ActionAutomationCreate = "automation.create"
ActionAutomationUpdate = "automation.update"
ActionAutomationDelete = "automation.delete"
ActionAutomationEnable = "automation.enable"
ActionAutomationDisable = "automation.disable"
ActionAutomationTrigger = "automation.trigger"
ActionAutomationRunsList = "automation.runs.list"
ActionAutomationTriggerAdd = "automation.trigger.add"
ActionAutomationTriggerUpdate = "automation.trigger.update"
ActionAutomationTriggerDelete = "automation.trigger.delete"
ActionAutomationTriggerTypes = "automation.trigger_types"
ActionAutomationWebhookRevealSecret = "automation.webhook.reveal_secret"
)
// Error codes
const (
ErrorCodeBadRequest = "BAD_REQUEST"
ErrorCodeNotFound = "NOT_FOUND"
ErrorCodeInternalError = "INTERNAL_ERROR"
ErrorCodeUnauthorized = "UNAUTHORIZED"
ErrorCodeForbidden = "FORBIDDEN"
ErrorCodeValidation = "VALIDATION_ERROR"
ErrorCodeConflict = "CONFLICT"
ErrorCodeUnknownAction = "UNKNOWN_ACTION"
)