mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 01:16:45 +08:00
MUL-7257: refactor(server): remove unused compatibility queries and capabilities (#9671)
* refactor(comments): drop redundant RFC3339 fallback parse RFC3339Nano parsing already accepts timestamps without fractional seconds, so the second time.Parse(time.RFC3339) attempt can never succeed where the first failed. Keep the existing 400 responses and add a test pinning second-precision since and the invalid since/before cases. Co-authored-by: multica-agent <[email protected]> * refactor(daemon): stop advertising unread plugin capabilities execution-manifest-v1, agent-skill-v1 and remote-mcp-v1 have had no server-side reader since the Plugin V1 gates were removed in #7144. Drop the constants and the daemon advertisement. Co-authored-by: multica-agent <[email protected]> * refactor(autopilot): remove legacy no-reservation terminal queries UpdateAutopilotRunCompleted, UpdateAutopilotRunFailed and UpdateAutopilotRunSkippedWithResult have no production callers; terminal transitions go through UpdateAutopilotRunTerminalWithQuota. Their generated Params structs were still used as service argument types, so replace them with service-local structs. The reconciler test now seeds its crash window with an explicit fixture UPDATE. Co-authored-by: multica-agent <[email protected]> * refactor(tasks): remove unused StartAgentTask query Production start paths use StartAgentTaskWithSupplement. Move the seven test fixtures to the same query with both capability flags off, which performs the same running transition. The empty-body start handler branch is unchanged. Co-authored-by: multica-agent <[email protected]> * refactor(channels): remove unused pending_fresh queries MarkChannelChatSessionPendingFresh, LockChannelChatSessionPendingFresh and ClearChannelChatSessionPendingFresh have no callers. The context generation path and ClearChannelChatSessionPendingFreshForRevision remain. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
multica-agent
parent
e7dfbe83bd
commit
f2a6398d6e
@@ -117,9 +117,9 @@ func TestAutopilotRunOnlyTaskTerminalEventsUpdateRun(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("mark task dispatched: %v", err)
|
||||
}
|
||||
task, err := queries.StartAgentTask(ctx, run.TaskID)
|
||||
task, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: run.TaskID})
|
||||
if err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
tc.finalize(task)
|
||||
@@ -239,8 +239,8 @@ func runTaskWithBudget(t *testing.T, queries *db.Queries, taskID pgtype.UUID, ma
|
||||
); err != nil {
|
||||
t.Fatalf("mark task dispatched: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(context.Background(), taskID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(context.Background(), db.StartAgentTaskWithSupplementParams{TaskID: taskID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ func TestQuickCreateFailure_DoesNotSubscribeRequester(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("dispatch task: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(ctx, task.ID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
// No issue with origin_type=quick_create + this task id exists. Completion
|
||||
@@ -139,8 +139,8 @@ func TestQuickCreateFailure_SurfacesAgentOutput(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("dispatch task: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(ctx, task.ID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
// No issue is created; the agent exits with the CLI duplicate error as its
|
||||
@@ -211,8 +211,8 @@ func TestQuickCreateLookupFault_WritesUnconfirmedInbox(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("dispatch task: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(ctx, task.ID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
// Completion runs against a Queries whose origin lookup always faults. The
|
||||
@@ -292,8 +292,8 @@ func TestQuickCreateFailure_RedactsAgentOutput(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("dispatch task: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(ctx, task.ID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
// Fake, non-functional token shaped like a real GitHub PAT.
|
||||
@@ -361,8 +361,8 @@ func TestQuickCreateLookupCancelled_StillWritesUnconfirmedInbox(t *testing.T) {
|
||||
); err != nil {
|
||||
t.Fatalf("dispatch task: %v", err)
|
||||
}
|
||||
if _, err := queries.StartAgentTask(setupCtx, task.ID); err != nil {
|
||||
t.Fatalf("StartAgentTask: %v", err)
|
||||
if _, err := queries.StartAgentTaskWithSupplement(setupCtx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil {
|
||||
t.Fatalf("StartAgentTaskWithSupplement: %v", err)
|
||||
}
|
||||
|
||||
// The completion transaction runs on a healthy context; the context dies at
|
||||
|
||||
@@ -204,9 +204,6 @@ func daemonCommonCapabilities() []string {
|
||||
return []string{
|
||||
protocol.DaemonCapabilitySkillBundlesV1,
|
||||
protocol.DaemonCapabilityCoalescedCommentsV1,
|
||||
protocol.DaemonCapabilityExecutionManifestV1,
|
||||
protocol.DaemonCapabilityAgentSkillV1,
|
||||
protocol.DaemonCapabilityRemoteMCPV1,
|
||||
protocol.DaemonCapabilityLocalWorktreeV1,
|
||||
protocol.DaemonCapabilitySourceContextQuickCreateV1,
|
||||
protocol.DaemonCapabilityRPCV1,
|
||||
|
||||
@@ -353,14 +353,11 @@ func (h *Handler) ListComments(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var sinceTime pgtype.Timestamptz
|
||||
if v := q.Get("since"); v != "" {
|
||||
// RFC3339Nano parsing also accepts timestamps without fractional seconds.
|
||||
t, err := time.Parse(time.RFC3339Nano, v)
|
||||
if err != nil {
|
||||
// Fall back to RFC3339 for backwards-compat with the original CLI.
|
||||
t, err = time.Parse(time.RFC3339, v)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid since parameter; expected RFC3339 format")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusBadRequest, "invalid since parameter; expected RFC3339 format")
|
||||
return
|
||||
}
|
||||
sinceTime = pgtype.Timestamptz{Time: t, Valid: true}
|
||||
}
|
||||
@@ -489,11 +486,8 @@ func (h *Handler) ListComments(w http.ResponseWriter, r *http.Request) {
|
||||
if beforeTimeStr != "" {
|
||||
t, err := time.Parse(time.RFC3339Nano, beforeTimeStr)
|
||||
if err != nil {
|
||||
t, err = time.Parse(time.RFC3339, beforeTimeStr)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid before parameter; expected RFC3339 format")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusBadRequest, "invalid before parameter; expected RFC3339 format")
|
||||
return
|
||||
}
|
||||
beforeCursor = pgtype.Timestamptz{Time: t, Valid: true}
|
||||
uuid, perr := util.ParseUUID(beforeIDStr)
|
||||
|
||||
@@ -229,6 +229,37 @@ func TestListComments_RootsOnlyWithSinceReturnsNewTopLevelComments(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
// TestListComments_TimestampParamsAcceptSecondPrecision pins that RFC3339
|
||||
// timestamps without fractional seconds still parse after the single
|
||||
// RFC3339Nano parse, and that malformed values keep their 400 responses.
|
||||
func TestListComments_TimestampParamsAcceptSecondPrecision(t *testing.T) {
|
||||
if testHandler == nil || testPool == nil {
|
||||
t.Skip("database not available")
|
||||
}
|
||||
fx := newCommentListFixture(t)
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("roots_only", "true")
|
||||
v.Set("since", fx.Base.Add(5*time.Minute).UTC().Format(time.RFC3339))
|
||||
w, rows := listComments(t, fx.IssueID, v.Encode())
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("second-precision since: status %d body %s", w.Code, w.Body.String())
|
||||
}
|
||||
eqIDs(t, ids(rows), []string{fx.Root2}, "second-precision since")
|
||||
|
||||
for _, tc := range []struct {
|
||||
name, query, wantMessage string
|
||||
}{
|
||||
{"since", "since=2026-01-02+03:04:05", "invalid since parameter"},
|
||||
{"before", cursorQuery(1, "2026-01-02T03:04", fx.Root1), "invalid before parameter"},
|
||||
} {
|
||||
w, _ := listComments(t, fx.IssueID, tc.query)
|
||||
if w.Code != http.StatusBadRequest || !strings.Contains(w.Body.String(), tc.wantMessage) {
|
||||
t.Fatalf("invalid %s: status %d body %s", tc.name, w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestListComments_RootsOnlyReturnsThreadStats pins the orientation metadata:
|
||||
// each root returned by roots_only carries reply_count (recursive descendant
|
||||
// count) and last_activity_at (MAX created_at over the subtree), so an agent
|
||||
|
||||
@@ -1166,7 +1166,7 @@ func (s *AutopilotService) SyncRunFromIssue(ctx context.Context, issue db.Issue)
|
||||
// settles the run; do not treat "not yet dispatched" as success.
|
||||
return
|
||||
}
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{
|
||||
ID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1177,7 +1177,7 @@ func (s *AutopilotService) SyncRunFromIssue(ctx context.Context, issue db.Issue)
|
||||
s.publishRunDone(wsID, updatedRun, "completed")
|
||||
case "cancelled":
|
||||
reason := "issue " + issue.Status
|
||||
updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: run.ID,
|
||||
FailureReason: pgtype.Text{String: reason, Valid: true},
|
||||
})
|
||||
@@ -1209,7 +1209,7 @@ func (s *AutopilotService) SyncRunFromTask(ctx context.Context, task db.AgentTas
|
||||
|
||||
switch task.Status {
|
||||
case "completed":
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{
|
||||
ID: run.ID,
|
||||
Result: task.Result,
|
||||
})
|
||||
@@ -1221,7 +1221,7 @@ func (s *AutopilotService) SyncRunFromTask(ctx context.Context, task db.AgentTas
|
||||
s.publishRunDone(wsID, updatedRun, "completed")
|
||||
case "failed", "cancelled":
|
||||
reason := taskFailureReasonForAutopilotRun(task)
|
||||
updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: run.ID,
|
||||
FailureReason: pgtype.Text{String: reason, Valid: reason != ""},
|
||||
})
|
||||
@@ -1311,7 +1311,7 @@ func (s *AutopilotService) settleRunFromDispatchTask(ctx context.Context, run db
|
||||
}
|
||||
|
||||
reason := taskFailureReasonForAutopilotRun(task)
|
||||
updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: run.ID,
|
||||
FailureReason: pgtype.Text{String: reason, Valid: reason != ""},
|
||||
})
|
||||
@@ -1336,7 +1336,7 @@ func (s *AutopilotService) completeRunFromLinkedIssueTask(ctx context.Context, r
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{
|
||||
updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{
|
||||
ID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1525,7 +1525,7 @@ func (s *AutopilotService) handleDispatchSkip(ctx context.Context, ap db.Autopil
|
||||
}
|
||||
|
||||
func (s *AutopilotService) failRun(ctx context.Context, runID pgtype.UUID, reason string) {
|
||||
if _, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
if _, err := s.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: runID,
|
||||
FailureReason: pgtype.Text{String: reason, Valid: true},
|
||||
ReasonCode: pgtype.Text{String: string(dispatch.ReasonInternalError), Valid: true},
|
||||
|
||||
@@ -245,7 +245,21 @@ func settleAutopilotQuota(ctx context.Context, q *db.Queries, reservationID pgty
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params db.UpdateAutopilotRunCompletedParams) (db.AutopilotRun, error) {
|
||||
// autopilotRunCompletion and autopilotRunFailure carry the terminal inputs
|
||||
// that UpdateAutopilotRunTerminalWithQuota settles atomically with the run's
|
||||
// quota reservation.
|
||||
type autopilotRunCompletion struct {
|
||||
ID pgtype.UUID
|
||||
Result []byte
|
||||
}
|
||||
|
||||
type autopilotRunFailure struct {
|
||||
ID pgtype.UUID
|
||||
FailureReason pgtype.Text
|
||||
ReasonCode pgtype.Text
|
||||
}
|
||||
|
||||
func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params autopilotRunCompletion) (db.AutopilotRun, error) {
|
||||
row, err := s.Queries.UpdateAutopilotRunTerminalWithQuota(ctx, db.UpdateAutopilotRunTerminalWithQuotaParams{
|
||||
TerminalStatus: "completed",
|
||||
Result: params.Result,
|
||||
@@ -255,7 +269,7 @@ func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params db.U
|
||||
return autopilotRunFromTerminalRow(row), err
|
||||
}
|
||||
|
||||
func (s *AutopilotService) failAutopilotRun(ctx context.Context, params db.UpdateAutopilotRunFailedParams) (db.AutopilotRun, error) {
|
||||
func (s *AutopilotService) failAutopilotRun(ctx context.Context, params autopilotRunFailure) (db.AutopilotRun, error) {
|
||||
row, err := s.Queries.UpdateAutopilotRunTerminalWithQuota(ctx, db.UpdateAutopilotRunTerminalWithQuotaParams{
|
||||
TerminalStatus: "failed",
|
||||
FailureReason: params.FailureReason,
|
||||
|
||||
@@ -707,7 +707,7 @@ func TestAutopilotQuotaObserveToEnforceAndOffFinalization(t *testing.T) {
|
||||
// under observe/enforce. Terminal finalization relies on the persisted slot.
|
||||
fixture.setPolicy(entitlement.ActionOff, fixture.periodStart, fixture.periodEnd, 1)
|
||||
for _, run := range runs {
|
||||
if _, err := fixture.service.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
if _, err := fixture.service.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: run.ID, FailureReason: pgtype.Text{String: "test cleanup", Valid: true},
|
||||
}); err != nil {
|
||||
t.Fatalf("finalize while policy off: %v", err)
|
||||
@@ -738,7 +738,7 @@ func TestAutopilotQuotaConsumedCreateIssueCannotBeRefunded(t *testing.T) {
|
||||
if _, err := settleAutopilotQuota(ctx, fixture.queries, cancelled.QuotaReservationID, true); err != nil {
|
||||
t.Fatalf("consume cancelled run: %v", err)
|
||||
}
|
||||
if _, err := fixture.service.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{
|
||||
if _, err := fixture.service.failAutopilotRun(ctx, autopilotRunFailure{
|
||||
ID: cancelled.ID, FailureReason: pgtype.Text{String: "issue cancelled", Valid: true},
|
||||
}); err != nil {
|
||||
t.Fatalf("fail consumed run: %v", err)
|
||||
@@ -777,7 +777,7 @@ func TestAutopilotQuotaTerminalUpdateNeedsNoTransactionStarter(t *testing.T) {
|
||||
t.Fatalf("create off-path run: %v", err)
|
||||
}
|
||||
serviceWithoutTx := &AutopilotService{Queries: fixture.queries}
|
||||
updated, err := serviceWithoutTx.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ID: run.ID})
|
||||
updated, err := serviceWithoutTx.completeAutopilotRun(ctx, autopilotRunCompletion{ID: run.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("complete off-path run: %v", err)
|
||||
}
|
||||
@@ -798,7 +798,7 @@ func TestAutopilotQuotaSettlementStaysInOriginalPeriod(t *testing.T) {
|
||||
secondStart := fixture.periodEnd
|
||||
secondEnd := secondStart.Add(19 * time.Hour)
|
||||
fixture.setPolicy(entitlement.ActionEnforce, secondStart, secondEnd, 1)
|
||||
if _, err := fixture.service.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ID: run.ID}); err != nil {
|
||||
if _, err := fixture.service.completeAutopilotRun(ctx, autopilotRunCompletion{ID: run.ID}); err != nil {
|
||||
t.Fatalf("complete after period rollover: %v", err)
|
||||
}
|
||||
|
||||
@@ -861,7 +861,9 @@ func TestAutopilotQuotaReconcilerSettlesTerminalOnlyOnce(t *testing.T) {
|
||||
}
|
||||
// Simulate a crash after the run reached terminal state but before the old
|
||||
// two-step finalizer settled its reservation.
|
||||
if _, err := fixture.queries.UpdateAutopilotRunCompleted(ctx, db.UpdateAutopilotRunCompletedParams{ID: terminal.ID}); err != nil {
|
||||
if _, err := fixture.pool.Exec(ctx, `
|
||||
UPDATE autopilot_run SET status = 'completed', completed_at = now() WHERE id = $1
|
||||
`, terminal.ID); err != nil {
|
||||
t.Fatalf("seed terminal crash window: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -6917,8 +6917,9 @@ type MarkAgentTaskWaitingLocalDirectoryParams struct {
|
||||
// alongside the status.
|
||||
//
|
||||
// The CHECK only allows the transition from 'dispatched' so a daemon can't
|
||||
// mark an already-running or terminal task as waiting; the StartAgentTask
|
||||
// mutation handles the reverse transition once the lock is acquired.
|
||||
// mark an already-running or terminal task as waiting; the
|
||||
// StartAgentTaskWithSupplement mutation handles the reverse transition once
|
||||
// the lock is acquired.
|
||||
func (q *Queries) MarkAgentTaskWaitingLocalDirectory(ctx context.Context, arg MarkAgentTaskWaitingLocalDirectoryParams) (AgentTaskQueue, error) {
|
||||
row := q.db.QueryRow(ctx, markAgentTaskWaitingLocalDirectory, arg.ID, arg.WaitReason, arg.PrepareLeaseSecs)
|
||||
var i AgentTaskQueue
|
||||
@@ -8710,91 +8711,6 @@ func (q *Queries) SettleDelegatedFailureRecoveryComment(ctx context.Context, com
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const startAgentTask = `-- name: StartAgentTask :one
|
||||
UPDATE agent_task_queue
|
||||
SET status = 'running',
|
||||
started_at = now(),
|
||||
wait_reason = NULL,
|
||||
prepare_lease_expires_at = NULL
|
||||
WHERE agent_task_queue.id = $1 AND agent_task_queue.status IN ('dispatched', 'waiting_local_directory')
|
||||
RETURNING id, agent_id, issue_id, status, priority, dispatched_at, started_at, completed_at, result, error, created_at, context, runtime_id, session_id, work_dir, trigger_comment_id, chat_session_id, autopilot_run_id, attempt, max_attempts, parent_task_id, failure_reason, trigger_summary, force_fresh_session, is_leader_task, wait_reason, initiator_user_id, handoff_note, prepare_lease_expires_at, squad_id, runtime_mcp_overlay, escalation_for_task_id, fire_at, originator_user_id, runtime_connected_apps, coalesced_comment_ids, delivered_comment_ids, chat_input_task_id, chat_finalize_deferred_at, originator_source, delegated_from_task_id, retry_of_task_id, rerun_of_task_id, rule_version_id, trigger_evidence_kind, trigger_evidence_ref_id, accountable_user_id, session_rollout_missing, retired_session_id, quick_actions_disabled, regenerate_quick_actions_for, branch_name, durable_work_dir, channel_context_revision, comment_thread_id, cancelled_by_type, cancelled_by_id, cancelled_by_name, issue_snapshot, workflow_step
|
||||
`
|
||||
|
||||
// Legacy single-winner transition; generation-aware callers lock the claim first.
|
||||
// Transitions a task to running. Accepts either 'dispatched' (the normal
|
||||
// claim → run flow) or 'waiting_local_directory' (the daemon held the row in
|
||||
// a wait state while another task owned the local_directory path lock; once
|
||||
// the lock was acquired the daemon flips here). wait_reason is cleared on
|
||||
// the transition so a future read can't conflate "currently waiting" with
|
||||
// "previously waited".
|
||||
func (q *Queries) StartAgentTask(ctx context.Context, id pgtype.UUID) (AgentTaskQueue, error) {
|
||||
row := q.db.QueryRow(ctx, startAgentTask, id)
|
||||
var i AgentTaskQueue
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AgentID,
|
||||
&i.IssueID,
|
||||
&i.Status,
|
||||
&i.Priority,
|
||||
&i.DispatchedAt,
|
||||
&i.StartedAt,
|
||||
&i.CompletedAt,
|
||||
&i.Result,
|
||||
&i.Error,
|
||||
&i.CreatedAt,
|
||||
&i.Context,
|
||||
&i.RuntimeID,
|
||||
&i.SessionID,
|
||||
&i.WorkDir,
|
||||
&i.TriggerCommentID,
|
||||
&i.ChatSessionID,
|
||||
&i.AutopilotRunID,
|
||||
&i.Attempt,
|
||||
&i.MaxAttempts,
|
||||
&i.ParentTaskID,
|
||||
&i.FailureReason,
|
||||
&i.TriggerSummary,
|
||||
&i.ForceFreshSession,
|
||||
&i.IsLeaderTask,
|
||||
&i.WaitReason,
|
||||
&i.InitiatorUserID,
|
||||
&i.HandoffNote,
|
||||
&i.PrepareLeaseExpiresAt,
|
||||
&i.SquadID,
|
||||
&i.RuntimeMcpOverlay,
|
||||
&i.EscalationForTaskID,
|
||||
&i.FireAt,
|
||||
&i.OriginatorUserID,
|
||||
&i.RuntimeConnectedApps,
|
||||
&i.CoalescedCommentIds,
|
||||
&i.DeliveredCommentIds,
|
||||
&i.ChatInputTaskID,
|
||||
&i.ChatFinalizeDeferredAt,
|
||||
&i.OriginatorSource,
|
||||
&i.DelegatedFromTaskID,
|
||||
&i.RetryOfTaskID,
|
||||
&i.RerunOfTaskID,
|
||||
&i.RuleVersionID,
|
||||
&i.TriggerEvidenceKind,
|
||||
&i.TriggerEvidenceRefID,
|
||||
&i.AccountableUserID,
|
||||
&i.SessionRolloutMissing,
|
||||
&i.RetiredSessionID,
|
||||
&i.QuickActionsDisabled,
|
||||
&i.RegenerateQuickActionsFor,
|
||||
&i.BranchName,
|
||||
&i.DurableWorkDir,
|
||||
&i.ChannelContextRevision,
|
||||
&i.CommentThreadID,
|
||||
&i.CancelledByType,
|
||||
&i.CancelledByID,
|
||||
&i.CancelledByName,
|
||||
&i.IssueSnapshot,
|
||||
&i.WorkflowStep,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAgent = `-- name: UpdateAgent :one
|
||||
UPDATE agent SET
|
||||
name = COALESCE($2, name),
|
||||
|
||||
@@ -2267,88 +2267,6 @@ func (q *Queries) UpdateAutopilotLastRunAt(ctx context.Context, id pgtype.UUID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateAutopilotRunCompleted = `-- name: UpdateAutopilotRunCompleted :one
|
||||
UPDATE autopilot_run
|
||||
SET status = 'completed', completed_at = now(), result = $2
|
||||
WHERE id = $1
|
||||
RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code
|
||||
`
|
||||
|
||||
type UpdateAutopilotRunCompletedParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Result []byte `json:"result"`
|
||||
}
|
||||
|
||||
// Quota safety: only use for a run known to have no reservation. Normal
|
||||
// terminal paths must call UpdateAutopilotRunTerminalWithQuota instead.
|
||||
func (q *Queries) UpdateAutopilotRunCompleted(ctx context.Context, arg UpdateAutopilotRunCompletedParams) (AutopilotRun, error) {
|
||||
row := q.db.QueryRow(ctx, updateAutopilotRunCompleted, arg.ID, arg.Result)
|
||||
var i AutopilotRun
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AutopilotID,
|
||||
&i.TriggerID,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.IssueID,
|
||||
&i.TaskID,
|
||||
&i.TriggeredAt,
|
||||
&i.CompletedAt,
|
||||
&i.FailureReason,
|
||||
&i.TriggerPayload,
|
||||
&i.Result,
|
||||
&i.CreatedAt,
|
||||
&i.SquadID,
|
||||
&i.PlannedAt,
|
||||
&i.WebhookDeliveryID,
|
||||
&i.QuotaReservationID,
|
||||
&i.ReasonCode,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAutopilotRunFailed = `-- name: UpdateAutopilotRunFailed :one
|
||||
UPDATE autopilot_run
|
||||
SET status = 'failed', completed_at = now(), failure_reason = $2,
|
||||
reason_code = $3
|
||||
WHERE id = $1
|
||||
RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code
|
||||
`
|
||||
|
||||
type UpdateAutopilotRunFailedParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
FailureReason pgtype.Text `json:"failure_reason"`
|
||||
ReasonCode pgtype.Text `json:"reason_code"`
|
||||
}
|
||||
|
||||
// Quota safety: only use for a run known to have no reservation. Normal
|
||||
// terminal paths must call UpdateAutopilotRunTerminalWithQuota instead.
|
||||
func (q *Queries) UpdateAutopilotRunFailed(ctx context.Context, arg UpdateAutopilotRunFailedParams) (AutopilotRun, error) {
|
||||
row := q.db.QueryRow(ctx, updateAutopilotRunFailed, arg.ID, arg.FailureReason, arg.ReasonCode)
|
||||
var i AutopilotRun
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AutopilotID,
|
||||
&i.TriggerID,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.IssueID,
|
||||
&i.TaskID,
|
||||
&i.TriggeredAt,
|
||||
&i.CompletedAt,
|
||||
&i.FailureReason,
|
||||
&i.TriggerPayload,
|
||||
&i.Result,
|
||||
&i.CreatedAt,
|
||||
&i.SquadID,
|
||||
&i.PlannedAt,
|
||||
&i.WebhookDeliveryID,
|
||||
&i.QuotaReservationID,
|
||||
&i.ReasonCode,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAutopilotRunIssueCreated = `-- name: UpdateAutopilotRunIssueCreated :one
|
||||
UPDATE autopilot_run
|
||||
SET status = 'issue_created', issue_id = $2
|
||||
@@ -2473,50 +2391,6 @@ func (q *Queries) UpdateAutopilotRunSkipped(ctx context.Context, arg UpdateAutop
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAutopilotRunSkippedWithResult = `-- name: UpdateAutopilotRunSkippedWithResult :one
|
||||
UPDATE autopilot_run
|
||||
SET status = 'skipped',
|
||||
completed_at = now(),
|
||||
failure_reason = $2,
|
||||
result = $3
|
||||
WHERE id = $1
|
||||
RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code
|
||||
`
|
||||
|
||||
type UpdateAutopilotRunSkippedWithResultParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
FailureReason pgtype.Text `json:"failure_reason"`
|
||||
Result []byte `json:"result"`
|
||||
}
|
||||
|
||||
// Quota safety: legacy no-reservation helper. New terminal paths must call
|
||||
// UpdateAutopilotRunTerminalWithQuota instead.
|
||||
func (q *Queries) UpdateAutopilotRunSkippedWithResult(ctx context.Context, arg UpdateAutopilotRunSkippedWithResultParams) (AutopilotRun, error) {
|
||||
row := q.db.QueryRow(ctx, updateAutopilotRunSkippedWithResult, arg.ID, arg.FailureReason, arg.Result)
|
||||
var i AutopilotRun
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AutopilotID,
|
||||
&i.TriggerID,
|
||||
&i.Source,
|
||||
&i.Status,
|
||||
&i.IssueID,
|
||||
&i.TaskID,
|
||||
&i.TriggeredAt,
|
||||
&i.CompletedAt,
|
||||
&i.FailureReason,
|
||||
&i.TriggerPayload,
|
||||
&i.Result,
|
||||
&i.CreatedAt,
|
||||
&i.SquadID,
|
||||
&i.PlannedAt,
|
||||
&i.WebhookDeliveryID,
|
||||
&i.QuotaReservationID,
|
||||
&i.ReasonCode,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAutopilotRunTerminalWithQuota = `-- name: UpdateAutopilotRunTerminalWithQuota :one
|
||||
WITH updated_run AS (
|
||||
UPDATE autopilot_run AS ar
|
||||
|
||||
@@ -420,17 +420,6 @@ func (q *Queries) ClearChannelChatContextPendingFresh(ctx context.Context, arg C
|
||||
return err
|
||||
}
|
||||
|
||||
const clearChannelChatSessionPendingFresh = `-- name: ClearChannelChatSessionPendingFresh :exec
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = FALSE
|
||||
WHERE chat_session_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ClearChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) error {
|
||||
_, err := q.db.Exec(ctx, clearChannelChatSessionPendingFresh, chatSessionID)
|
||||
return err
|
||||
}
|
||||
|
||||
const clearChannelChatSessionPendingFreshForRevision = `-- name: ClearChannelChatSessionPendingFreshForRevision :exec
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = FALSE
|
||||
@@ -2476,22 +2465,6 @@ func (q *Queries) LockChannelChatSessionBindingForContext(ctx context.Context, c
|
||||
return i, err
|
||||
}
|
||||
|
||||
const lockChannelChatSessionPendingFresh = `-- name: LockChannelChatSessionPendingFresh :one
|
||||
SELECT pending_fresh FROM channel_chat_session_binding
|
||||
WHERE chat_session_id = $1
|
||||
FOR UPDATE
|
||||
`
|
||||
|
||||
// EnqueueChatTask reads this under the same row lock and transaction that
|
||||
// creates the task. A concurrent `/clear` therefore lands either before this
|
||||
// task and is consumed by it, or after this task and remains for the next one.
|
||||
func (q *Queries) LockChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) (bool, error) {
|
||||
row := q.db.QueryRow(ctx, lockChannelChatSessionPendingFresh, chatSessionID)
|
||||
var pending_fresh bool
|
||||
err := row.Scan(&pending_fresh)
|
||||
return pending_fresh, err
|
||||
}
|
||||
|
||||
const lockChannelInstallationAppIDSlot = `-- name: LockChannelInstallationAppIDSlot :exec
|
||||
SELECT pg_advisory_xact_lock(
|
||||
hashtext($1::text),
|
||||
@@ -2592,23 +2565,6 @@ func (q *Queries) LockCurrentChannelChatSessionBindingBySession(ctx context.Cont
|
||||
return i, err
|
||||
}
|
||||
|
||||
const markChannelChatSessionPendingFresh = `-- name: MarkChannelChatSessionPendingFresh :one
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = TRUE
|
||||
WHERE chat_session_id = $1 AND retired_at IS NULL
|
||||
RETURNING pending_fresh
|
||||
`
|
||||
|
||||
// Persists a channel `/clear` intent until the next chat task is successfully
|
||||
// created. RETURNING makes a missing binding an error instead of silently
|
||||
// acknowledging a fresh start that was never stored.
|
||||
func (q *Queries) MarkChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) (bool, error) {
|
||||
row := q.db.QueryRow(ctx, markChannelChatSessionPendingFresh, chatSessionID)
|
||||
var pending_fresh bool
|
||||
err := row.Scan(&pending_fresh)
|
||||
return pending_fresh, err
|
||||
}
|
||||
|
||||
const markChannelInboundDedupProcessed = `-- name: MarkChannelInboundDedupProcessed :execrows
|
||||
UPDATE channel_inbound_message_dedup
|
||||
SET processed_at = now()
|
||||
|
||||
@@ -963,22 +963,6 @@ WHERE id = $1
|
||||
AND started_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: StartAgentTask :one
|
||||
-- Legacy single-winner transition; generation-aware callers lock the claim first.
|
||||
-- Transitions a task to running. Accepts either 'dispatched' (the normal
|
||||
-- claim → run flow) or 'waiting_local_directory' (the daemon held the row in
|
||||
-- a wait state while another task owned the local_directory path lock; once
|
||||
-- the lock was acquired the daemon flips here). wait_reason is cleared on
|
||||
-- the transition so a future read can't conflate "currently waiting" with
|
||||
-- "previously waited".
|
||||
UPDATE agent_task_queue
|
||||
SET status = 'running',
|
||||
started_at = now(),
|
||||
wait_reason = NULL,
|
||||
prepare_lease_expires_at = NULL
|
||||
WHERE agent_task_queue.id = $1 AND agent_task_queue.status IN ('dispatched', 'waiting_local_directory')
|
||||
RETURNING *;
|
||||
|
||||
-- name: LockAgentTaskStartClaim :one
|
||||
-- Serialize start/replay with reclaim and cancellation. A stale delivery must
|
||||
-- never start or acknowledge a newer claim, even on the same runtime.
|
||||
@@ -995,8 +979,9 @@ FOR UPDATE;
|
||||
-- alongside the status.
|
||||
--
|
||||
-- The CHECK only allows the transition from 'dispatched' so a daemon can't
|
||||
-- mark an already-running or terminal task as waiting; the StartAgentTask
|
||||
-- mutation handles the reverse transition once the lock is acquired.
|
||||
-- mark an already-running or terminal task as waiting; the
|
||||
-- StartAgentTaskWithSupplement mutation handles the reverse transition once
|
||||
-- the lock is acquired.
|
||||
UPDATE agent_task_queue
|
||||
SET status = 'waiting_local_directory',
|
||||
wait_reason = $2,
|
||||
|
||||
@@ -441,23 +441,6 @@ SET status = 'running', task_id = $2
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAutopilotRunCompleted :one
|
||||
-- Quota safety: only use for a run known to have no reservation. Normal
|
||||
-- terminal paths must call UpdateAutopilotRunTerminalWithQuota instead.
|
||||
UPDATE autopilot_run
|
||||
SET status = 'completed', completed_at = now(), result = sqlc.narg('result')
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAutopilotRunFailed :one
|
||||
-- Quota safety: only use for a run known to have no reservation. Normal
|
||||
-- terminal paths must call UpdateAutopilotRunTerminalWithQuota instead.
|
||||
UPDATE autopilot_run
|
||||
SET status = 'failed', completed_at = now(), failure_reason = $2,
|
||||
reason_code = sqlc.narg('reason_code')
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAutopilotRunSkipped :one
|
||||
-- Quota safety: this is only for pre-admission skipped rows, which never have
|
||||
-- reservations. Post-admission skips use UpdateAutopilotRunTerminalWithQuota.
|
||||
@@ -532,17 +515,6 @@ WITH updated_run AS (
|
||||
)
|
||||
SELECT * FROM updated_run;
|
||||
|
||||
-- name: UpdateAutopilotRunSkippedWithResult :one
|
||||
-- Quota safety: legacy no-reservation helper. New terminal paths must call
|
||||
-- UpdateAutopilotRunTerminalWithQuota instead.
|
||||
UPDATE autopilot_run
|
||||
SET status = 'skipped',
|
||||
completed_at = now(),
|
||||
failure_reason = $2,
|
||||
result = sqlc.narg('result')
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- =====================
|
||||
-- Scheduler Queries
|
||||
-- =====================
|
||||
|
||||
@@ -791,23 +791,6 @@ SET last_message_id = sqlc.narg('last_message_id'),
|
||||
FROM current_route
|
||||
WHERE binding.id = current_route.id;
|
||||
|
||||
-- name: MarkChannelChatSessionPendingFresh :one
|
||||
-- Persists a channel `/clear` intent until the next chat task is successfully
|
||||
-- created. RETURNING makes a missing binding an error instead of silently
|
||||
-- acknowledging a fresh start that was never stored.
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = TRUE
|
||||
WHERE chat_session_id = $1 AND retired_at IS NULL
|
||||
RETURNING pending_fresh;
|
||||
|
||||
-- name: LockChannelChatSessionPendingFresh :one
|
||||
-- EnqueueChatTask reads this under the same row lock and transaction that
|
||||
-- creates the task. A concurrent `/clear` therefore lands either before this
|
||||
-- task and is consumed by it, or after this task and remains for the next one.
|
||||
SELECT pending_fresh FROM channel_chat_session_binding
|
||||
WHERE chat_session_id = $1
|
||||
FOR UPDATE;
|
||||
|
||||
-- name: LockChannelChatSessionBindingForContext :one
|
||||
-- Context mutations acquire this row after chat_session and before any
|
||||
-- channel_chat_context_generation row. Keeping the statements separate makes
|
||||
@@ -816,11 +799,6 @@ SELECT * FROM channel_chat_session_binding
|
||||
WHERE chat_session_id = $1
|
||||
FOR UPDATE;
|
||||
|
||||
-- name: ClearChannelChatSessionPendingFresh :exec
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = FALSE
|
||||
WHERE chat_session_id = $1;
|
||||
|
||||
-- name: ClearChannelChatSessionPendingFreshForRevision :exec
|
||||
UPDATE channel_chat_session_binding
|
||||
SET pending_fresh = FALSE
|
||||
|
||||
@@ -5,9 +5,6 @@ import "encoding/json"
|
||||
const (
|
||||
DaemonCapabilitySkillBundlesV1 = "skill-bundles-v1"
|
||||
DaemonCapabilityCoalescedCommentsV1 = "coalesced-comments-v1"
|
||||
DaemonCapabilityExecutionManifestV1 = "execution-manifest-v1"
|
||||
DaemonCapabilityAgentSkillV1 = "agent-skill-v1"
|
||||
DaemonCapabilityRemoteMCPV1 = "remote-mcp-v1"
|
||||
// DaemonCapabilityLocalWorktreeV1 advertises that the daemon implements
|
||||
// worktree mode for local_directory resources (execution_mode=worktree).
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user