@quantiya/codevibe-claude-plugin 2.0.43 → 2.0.45

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.
Files changed (38) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +124 -3
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.d.ts +6 -0
  4. package/node_modules/@quantiya/codevibe-core/dist/index.js +629 -510
  5. package/node_modules/@quantiya/codevibe-core/dist/local-executor/agentic-resolver-flag.d.ts +4 -7
  6. package/node_modules/@quantiya/codevibe-core/dist/local-executor/class-b-consumer.d.ts +10 -1
  7. package/node_modules/@quantiya/codevibe-core/dist/local-executor/descendant-reaper.d.ts +81 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/local-executor/index.d.ts +1 -1
  9. package/node_modules/@quantiya/codevibe-core/dist/local-executor/local-executor-impl.d.ts +15 -1
  10. package/node_modules/@quantiya/codevibe-core/dist/local-executor/merge-train.d.ts +1 -1
  11. package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-tree.d.ts +6 -0
  12. package/node_modules/@quantiya/codevibe-core/dist/local-executor/reviewer-rationale-flag.d.ts +3 -8
  13. package/node_modules/@quantiya/codevibe-core/dist/local-executor/snapshot-merge.d.ts +10 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/local-executor/spawn.d.ts +1 -1
  15. package/node_modules/@quantiya/codevibe-core/dist/local-executor/team-execution-flag.d.ts +0 -6
  16. package/node_modules/@quantiya/codevibe-core/dist/local-executor/types.d.ts +16 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/local-model/manager.d.ts +5 -3
  18. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +46310 -44043
  19. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/ImplementorSetupWizard.d.ts +10 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/LiveProgressLine.d.ts +4 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/OrchestrationApp.d.ts +13 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/implementor-preference.d.ts +24 -0
  23. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +97 -18
  24. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/non-tty-fallback.d.ts +2 -3
  25. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/prompt-reply-router.d.ts +53 -0
  26. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +163 -40
  27. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/slash-routes/implementors.d.ts +17 -0
  28. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-lifecycle-lines.d.ts +29 -0
  29. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-progress.d.ts +1 -0
  30. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/team-decompose.d.ts +9 -8
  31. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +96 -1
  32. package/node_modules/@quantiya/codevibe-core/dist/planner/types.d.ts +2 -2
  33. package/node_modules/@quantiya/codevibe-core/dist/substrate/command-runner.d.ts +2 -0
  34. package/node_modules/@quantiya/codevibe-core/dist/substrate/types.d.ts +5 -0
  35. package/node_modules/@quantiya/codevibe-core/dist/substrate-launch/engage-substrate.d.ts +2 -0
  36. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/types.d.ts +12 -12
  37. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  38. package/package.json +2 -2
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import type { ImplementorWizardState } from '../types';
3
+ import { type ImplementorPreference } from '../implementor-preference';
4
+ export declare const IMPLEMENTOR_WIZARD_MAX_ROWS = 12;
5
+ export interface ImplementorSetupWizardProps {
6
+ wizard: ImplementorWizardState;
7
+ onSubmit: (pref: ImplementorPreference) => void;
8
+ onCancel: () => void;
9
+ }
10
+ export declare function ImplementorSetupWizard(props: ImplementorSetupWizardProps): React.ReactElement;
@@ -2,6 +2,10 @@ import * as React from 'react';
2
2
  import { LiveProgress } from '../types';
3
3
  export interface LiveProgressLineProps {
4
4
  progress: LiveProgress | null;
5
+ /** P46b §3.7 — the task's label (`#2`) when several task lines render. */
6
+ label?: string;
7
+ /** P46b §3.7 — how many more busy tasks are not shown (last line only). */
8
+ moreCount?: number;
5
9
  /** Test seam; otherwise derived from the live terminal width, then 80 (mirrors StatusBar). */
6
10
  columns?: number;
7
11
  }
@@ -4,6 +4,19 @@ import { ConversationEntry, PostDecisionAction, Tier } from '../types';
4
4
  import type { PlannerRuntimeKind } from '../status-format';
5
5
  import type { UpdateReviewerPolicyInput, UserReviewerPolicySnapshot } from '../../types/reviewer';
6
6
  import { type ApplyUserDecisionClient as SharedApplyUserDecisionClient, type GroupDecisionSubmitDeps } from '../gate-decision-submit';
7
+ /**
8
+ * Stable empty list passed to <InputBar> to SUPPRESS slash autocomplete unless
9
+ * it fits (Stage-1 MEDIUM + Stage-2 — #469): the dropdown lives in the live
10
+ * region below the input, so an open dropdown while the live region is tall
11
+ * could push total output over the terminal height and re-trigger Ink's
12
+ * whole-scrollback reprint. P46b §3.7 — the dropdown is offered while tasks run
13
+ * (progress lines no longer suppress it); a pinned gate-prompt card or the
14
+ * reviewer wizard still does, and while it can be open we reserve
15
+ * DROPDOWN_MAX_ROWS plus every progress line beyond the first in the height cap
16
+ * (below) so it always fits. Module-scope constant → stable identity.
17
+ */
18
+ /** P46b §3.7 — at most this many per-task progress lines render (`+n more` after). */
19
+ export declare const MAX_TASK_PROGRESS_LINES = 3;
7
20
  /**
8
21
  * Minimal SDK contract for the applyUserDecision wire. Structurally
9
22
  * compatible with the full `AppSyncClient.applyUserDecision` signature
@@ -0,0 +1,24 @@
1
+ export type ActiveImplementorAgentKind = 'CLAUDE' | 'CODEX' | 'ANTIGRAVITY';
2
+ export declare const DEFAULT_IMPLEMENTOR_PRIORITY: readonly ActiveImplementorAgentKind[];
3
+ export interface ImplementorPreference {
4
+ schemaVersion: 1;
5
+ priorityOrder: ActiveImplementorAgentKind[];
6
+ confirmBeforeStart: boolean;
7
+ updatedAt: string;
8
+ }
9
+ export declare function implementorPreferenceFilePath(): string;
10
+ export declare function defaultImplementorPreference(): ImplementorPreference;
11
+ export declare function normalizeImplementorAgent(val: string): ActiveImplementorAgentKind | null;
12
+ /**
13
+ * Synchronously reads the implementor preference. Fails soft: returns defaults
14
+ * if the file is absent or corrupt.
15
+ */
16
+ export declare function readImplementorPreferenceSync(): ImplementorPreference;
17
+ /**
18
+ * Saves updated implementor preference to ~/.codevibe/implementor-preference.json.
19
+ */
20
+ export declare function saveImplementorPreference(patch: {
21
+ priorityOrder?: ActiveImplementorAgentKind[];
22
+ confirmBeforeStart?: boolean;
23
+ }): Promise<ImplementorPreference>;
24
+ export declare function resetImplementorPreference(): Promise<ImplementorPreference>;
@@ -47,8 +47,14 @@ import type { EnsureFreshResult } from './context-store';
47
47
  import { contextItemOccurrence, type ContextDurabilityReceipt } from './context-items';
48
48
  import { renderRehydratedSessionContext } from './context-compaction';
49
49
  import type { AgentKind as LocalExecutorAgentKind } from '../local-executor/types';
50
+ import type { OrchestrationState as OrchestrationStateType } from './types';
50
51
  import { type TaskProgressEvent } from './task-progress';
51
- import { taskLabel } from './task-label';
52
+ import { type PromptReplyOrigin } from './prompt-reply-router';
53
+ /**
54
+ * Gathers the implementor agents currently active across single-implementor running tasks
55
+ * and multi-track Agent Teams tracks. Used for multi-track priority allocation.
56
+ */
57
+ export declare function getInFlightImplementorAgents(state: OrchestrationStateType): LocalExecutorAgentKind[];
52
58
  /**
53
59
  * P46a (Stage 1 r1 F2a) — the oldest unresolved continuation-offer gate
54
60
  * prompt, whatever other gates are open. Pure.
@@ -57,14 +63,37 @@ export declare function findActiveContinuationOfferEntry(conversation: ReadonlyA
57
63
  kind: 'gate-prompt';
58
64
  }) | null;
59
65
  /**
60
- * P46a (Stage 1 r1 F2c) — a typed digit from the phone cannot choose between
61
- * several open review gates (the phone shows no task label until P46b): the
62
- * advisory to send back instead of answering the oldest gate, or null when the
63
- * reply is not a bare digit or at most one gate is answerable. Pure.
66
+ * P46a (Stage 1 r1 F2c) / P46b §3.7 — a bare digit from the phone cannot choose
67
+ * between several open prompts: every prompt the phone can answer counts (gate
68
+ * prompts awaiting a number or a note, the retry prompt, a pending planner
69
+ * offer). Returns the advisory naming them, or null when the reply is not a bare
70
+ * digit or at most one prompt is open. Pure.
71
+ */
72
+ export declare function mobileGateReplyAmbiguity(state: Pick<OrchestrationStateType, 'conversation' | 'taskOrdinals' | 'team' | 'pendingApplyConflicts' | 'pendingPlannerOffer'>, text: string): string | null;
73
+ /**
74
+ * P46b §3.13 item 3 — route a reply that names or must choose among the open
75
+ * prompts. Returns true when the line was consumed (answered, or refused with
76
+ * an advisory); false sends it down today's paths (the keyboard's active
77
+ * prompt, slash commands, the planner).
78
+ *
79
+ * 1. While any prompt is open, `#n <rest>` / `#offer <rest>` is always
80
+ * consumed, from either origin: naming an open prompt it answers it
81
+ * (making a gate the active prompt), otherwise an advisory.
82
+ * 2. Keyboard lines without `#` → today's paths (never refused).
83
+ * 3. Phone lines without `#`: a bare digit with several open prompts is
84
+ * refused; with one it answers it. With several prompts awaiting a note a
85
+ * plain line or bare `/cancel` is refused; with exactly one it is that
86
+ * prompt's note (or leaves its notes entry). Anything else → today's paths.
64
87
  */
65
- export declare function mobileGateReplyAmbiguity(state: Parameters<typeof taskLabel>[0] & {
66
- conversation: ReadonlyArray<ConversationEntry>;
67
- }, text: string): string | null;
88
+ export declare function routeOpenPromptReply(deps: {
89
+ store: OrchestrationStore;
90
+ args: RunOrchestrationShellArgs;
91
+ emitShellEventBound: ReturnType<typeof createShellEventEmitter>;
92
+ generator: StructuralSummaryGenerator;
93
+ gateDecisionDeps: GateDecisionSubmitDeps | null;
94
+ origin: PromptReplyOrigin;
95
+ text: string;
96
+ }): Promise<boolean>;
68
97
  export declare class OrchestrationShellStartupError extends Error {
69
98
  readonly cause?: unknown;
70
99
  constructor(message: string, cause?: unknown);
@@ -374,6 +403,8 @@ export declare function runOrchestrationShell(args: RunOrchestrationShellArgs):
374
403
  * simply renders with no review panel (fail-open).
375
404
  */
376
405
  export declare const TASK_REVIEW_SUMMARY_FETCH_TIMEOUT_MS = 5000;
406
+ /** P46b §3.9 — the engine's phone retry prompt kind (core-rs `apply_retry.rs`). */
407
+ export declare const APPLY_RETRY_PROMPT_KIND = "orchestration_apply_retry";
377
408
  /**
378
409
  * Plaintext-first dispatcher per #C8M-7 (Stage 1 R1 M6 lock):
379
410
  * 1. If `event.metadata` is an object, use it directly. Otherwise try
@@ -502,18 +533,48 @@ export declare function createSubscribedEventDispatcher(deps: {
502
533
  onDurableGateResolution?: (resolution: DurableGateResolution) => Promise<void> | void;
503
534
  updateEventStatus?: (input: UpdateEventStatusInput) => Promise<unknown>;
504
535
  }): (event: Event) => void;
536
+ export declare function registerTeamTrackStarts(taskGroupId: string, initialRunning: number): void;
537
+ export declare function routeTeamShellEventToStore(store: OrchestrationStore, evt: TeamTapEvent, postLine?: (text: string) => void): void;
505
538
  /**
506
- * Route ONE team-mode LE shell event into the store's minimal `team` state.
507
- * The W2.b `emitTrackProgress` emits a NOTIFICATION carrying
508
- * `metadata.track_index` + `task_id` (+ `source: 'track_progress'`); the
509
- * `haltTaskGroup` path emits a NOTIFICATION with `source: 'task_group_halted'`.
510
- * We discriminate on `metadata.source` so non-team NOTIFICATIONs (planner
511
- * health, late-arrival audits) are IGNORED — they have no team `source` tag.
539
+ * CP-12 W3 — minimal `/team <json>` arg parse. Accepts a JSON `WorkItemInput[]`
540
+ * (≥2 items). Shallow structural validation only — the backend
541
+ * `validate_create_input` (#C12-2 disjointness) is authoritative; we just need
542
+ * a well-formed array to drive `createTaskGroup`. Polished ergonomics
543
+ * (file-path-set shorthand, interactive builder) are [DEFERRED → CP-8 #469].
544
+ */
545
+ /**
546
+ * Audited-path fix (Fix 4 + Fix 5) — parse a `/task` invocation into an optional
547
+ * implementor-agent override + the remaining brief. Supports a LEADING `--agent
548
+ * codex|claude|antigravity|agy` flag (case-insensitive value, space or '=' separated); everything after it (or the
549
+ * whole arg list when absent) is the brief, re-joined verbatim. The override is
550
+ * returned even when not detected on this host — `selectImplementorAgent` decides
551
+ * whether to honor it (fail-closed to the default with a user-facing note).
512
552
  *
513
- * Pure mapping store dispatch; no rich rendering (that is [DEFERRED CP-8
514
- * #469]). Called from the `teamShellEventTap.fn` installed at startup.
553
+ * Tolerant: an unknown `--agent <value>` is IGNORED (the `--agent` token is dropped,
554
+ * the unrecognized value becomes part of the brief) rather than erroring — keeps the shortcut
555
+ * forgiving. When using the composite `--agent=<value>` form, the entire token including
556
+ * any unrecognized value is dropped so it does not pollute the brief. `--agent` with no
557
+ * following value is dropped (no brief consumed).
515
558
  */
516
- export declare function routeTeamShellEventToStore(store: OrchestrationStore, evt: TeamTapEvent): void;
559
+ export declare function parseTaskCommand(text: string): {
560
+ agent: LocalExecutorAgentKind | null;
561
+ brief: string;
562
+ /** P46a B1 — `/task focus <n>` (or `#n`): the task number to focus, else null. */
563
+ focus: number | null;
564
+ /** P46a C3 (Stage 1 r1 F3) — `/task retry <n>` / `/task discard <n>`: answer the apply-conflict menu of task #n. */
565
+ conflictChoice: {
566
+ choice: 1 | 2;
567
+ ordinal: number;
568
+ } | null;
569
+ /**
570
+ * P46b §3.7 — `/task cancel <n>`: `{ ordinal }`, or `{ ordinal: null }` for a
571
+ * `/task cancel` without a valid number (usage). Matched before the brief
572
+ * fallback, so it never starts a task with the brief "cancel …".
573
+ */
574
+ cancel: {
575
+ ordinal: number | null;
576
+ } | null;
577
+ };
517
578
  export declare function readOnlyAgentPrompt(args: {
518
579
  agent: LocalExecutorAgentKind;
519
580
  intent: AgentMentionIntent;
@@ -650,6 +711,8 @@ export declare function launchTeamFromWorkItems(deps: {
650
711
  * rewrite resolves each chip to the right ref. Retained for REJECT_RESTART reissue.
651
712
  */
652
713
  attachmentPaths?: string[];
714
+ /** P46b §3.10 — the failed group this launch retries (the new group is `#m retry`). */
715
+ retryOf?: string;
653
716
  }): Promise<LaunchTeamOutcome>;
654
717
  /**
655
718
  * PHASE-589/469 W3.2 RESIDUAL 1 — re-dispatch the WHOLE team for a
@@ -673,7 +736,9 @@ export declare function reissueTeamFromRetainedSpec(deps: {
673
736
  quorumLoop?: QuorumLoop;
674
737
  sessionId: string;
675
738
  durableStore: RevertManifestStore;
676
- }, taskGroupId: string): Promise<void>;
739
+ }, taskGroupId: string, options?: {
740
+ retryOf?: string;
741
+ }): Promise<void>;
677
742
  /**
678
743
  * PHASE-590 §3.D — the `team_decompose` routing path. Runs the local decomposer,
679
744
  * shape-checks (the schema is applied inside the decomposer parse) +
@@ -703,6 +768,20 @@ export declare function routeTeamDecompose(deps: {
703
768
  /** Stage-2 R3 HIGH — the loop's durable store, so the live launch persists the retained spec + attachments. */
704
769
  durableStore?: RevertManifestStore;
705
770
  }): Promise<void>;
771
+ /**
772
+ * Shared helper for handling LaunchTeamOutcome for both unconfirmed (auto-launch)
773
+ * and confirmed ("yes" reply) team launches. Ensures parity across both paths:
774
+ * - 'accepted' -> clean return (success advisory already dispatched by launchTeamFromWorkItems)
775
+ * - 'disjointness_rejected' -> dispatch advisory, call fallbackToSingle (if provided)
776
+ * - 'unknown_create_error' -> retry ONCE with same idempotency key; if second attempt
777
+ * is disjointness_rejected -> fallbackToSingle; if second attempt is unknown_create_error ->
778
+ * dispatch "Couldn't confirm the team started — check /status or retry. Nothing was launched."
779
+ */
780
+ export declare function handleTeamLaunchOutcome(deps: {
781
+ launch: () => Promise<LaunchTeamOutcome>;
782
+ store: OrchestrationStore;
783
+ fallbackToSingle?: () => Promise<void>;
784
+ }): Promise<void>;
706
785
  /** Test seam — reset the module-level A1e and M7 composers (vitest-gated). */
707
786
  export declare function resetDispatchContextComposerForTests(): void;
708
787
  /** Test seam — install the M7 decomposer user-context composer (vitest-gated). */
@@ -38,9 +38,8 @@ export interface RunLineLogFallbackArgs {
38
38
  /**
39
39
  * Whether the internal planner-classification entry (`[planner] <action>:
40
40
  * <rationale>` / the TTY "Planner classified …" node) is shown to the user.
41
- * OFF by default — it's internal mechanics that duplicate the actual response /
42
- * clarification the user already sees, and leaks taxonomy jargon ("classify").
43
- * Opt in with `CODEVIBE_PLANNER_DEBUG=1` when debugging the planner. The entry is
41
+ * OFF by default — it's internal mechanics that duplicate the actual response.
42
+ * Standardized to log level `CODEVIBE_LOG_LEVEL=debug`. The entry is
44
43
  * ALWAYS kept in conversation state (audit / logic / tests) — only the VIEW is gated.
45
44
  */
46
45
  export declare function plannerDecisionRenderEnabled(env?: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1,53 @@
1
+ import type { ConversationEntry, OrchestrationState, PendingApplyConflict, PendingPlannerOffer } from './types';
2
+ export type PromptReplyOrigin = 'keyboard' | 'mobile';
3
+ type GatePromptEntry = Extract<ConversationEntry, {
4
+ kind: 'gate-prompt';
5
+ }>;
6
+ export type OpenPrompt = {
7
+ kind: 'gate';
8
+ /** `#3`, `#5.2`, or the short-id fallback of an unnumbered task. */
9
+ label: string;
10
+ entry: GatePromptEntry;
11
+ awaiting: 'number' | 'note';
12
+ optionCount: number;
13
+ } | {
14
+ kind: 'apply';
15
+ label: string;
16
+ menu: PendingApplyConflict;
17
+ awaiting: 'number';
18
+ optionCount: 2;
19
+ } | {
20
+ kind: 'offer';
21
+ label: 'offer';
22
+ offer: PendingPlannerOffer;
23
+ awaiting: 'number';
24
+ optionCount: number;
25
+ };
26
+ /** The reference a reply uses for `prompt`: `3`, `5.2` or `offer`. */
27
+ export declare function promptReference(prompt: OpenPrompt): string;
28
+ /** How advisories name `prompt`: `Task #3`, `Task #5.2`, `#offer`. */
29
+ export declare function promptName(prompt: OpenPrompt): string;
30
+ /** Every open prompt in presentation order (gates, then menus, then the offer). */
31
+ export declare function listOpenPrompts(state: Pick<OrchestrationState, 'conversation' | 'taskOrdinals' | 'team' | 'pendingApplyConflicts' | 'pendingPlannerOffer'>, origin: PromptReplyOrigin): OpenPrompt[];
32
+ /**
33
+ * `#2 1` → `{ reference: '2', rest: '1' }`; `#5.2 note text` → `{ '5.2', 'note text' }`;
34
+ * `#offer 3` → `{ 'offer', '3' }`; a bare `#2` → rest `''`. Null when the line
35
+ * does not start with a reference followed by whitespace or the end.
36
+ */
37
+ export declare function parsePromptReference(text: string): {
38
+ reference: string;
39
+ rest: string;
40
+ } | null;
41
+ /** The open prompt a reference names, or null. */
42
+ export declare function findPromptByReference(open: ReadonlyArray<OpenPrompt>, reference: string): OpenPrompt | null;
43
+ /** §3.7 — a bare digit from the phone while several prompts are open. */
44
+ export declare function severalPromptsAdvisory(open: ReadonlyArray<OpenPrompt>): string;
45
+ /** §3.13 item 3.3.2 — a plain phone line while several prompts await a note. */
46
+ export declare function severalNotesAdvisory(notes: ReadonlyArray<OpenPrompt>): string;
47
+ /** §3.13 item 3.1 — a `#n` reply the named prompt cannot take. */
48
+ export declare function expectedReplyAdvisory(prompt: OpenPrompt): string;
49
+ /** §3.13 item 3.1 — a `#n` reply naming no open prompt. */
50
+ export declare function noOpenPromptAdvisory(reference: string): string;
51
+ /** §3.13 item 3 — the phone hint when a numbered answer opens a gate's notes entry. */
52
+ export declare function notesHint(prompt: OpenPrompt): string;
53
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { AppSyncClient, TeamTrackRecoveryRow } from '../appsync/appsync-client';
2
2
  import type { Session } from '../types';
3
3
  import type { LocalExecutor } from '../local-executor';
4
- import type { ClassBInbound } from '../local-executor/types';
4
+ import type { ClassBInbound, QueuedWorkEndedPayload } from '../local-executor/types';
5
5
  import type { ImageAttachment } from './types';
6
6
  import { type TaskAuditSummaryModel, type ReviewHistorySnapshot, type UserDecisionRecord } from './audit-summary';
7
7
  import type { DecryptedReviewSummaryInput } from './gate-prompts';
@@ -103,17 +103,18 @@ export declare function policyRejectionAdvisoryText(info: {
103
103
  * - An explicit `override` (from `/task --agent claude|codex|antigravity`) wins, BUT only
104
104
  * when that agent was actually detected on this host (else fail-closed to the
105
105
  * default — we never spawn a binary the host doesn't have).
106
- * - Otherwise: CLAUDE when it is among the detected agents (the documented
107
- * default), else the FIRST detected agent, else CLAUDE as the universal
108
- * fail-safe (matches the engine's Claude-only fallback when the host detected
109
- * nothing). The implementor argv allowlist already covers all three agents,
110
- * so any returned kind is spawnable without widening it.
106
+ * - Configurable implementor priority order via ~/.codevibe/implementor-preference.json
107
+ * (defaults to CLAUDE > CODEX > ANTIGRAVITY).
108
+ * - Multi-track priority allocation: when concurrent tracks run, allocates each track
109
+ * to the next idle agent in priority order. For hosts with a single installed agent,
110
+ * all tracks allocate to that agent.
111
+ * - Explicit override (@mention or --agent flag) always takes precedence if installed.
111
112
  *
112
113
  * Returns `{ agent, note }` — `note` is a non-null user-facing string ONLY when
113
114
  * an explicit override could not be honored (so the shell can surface it); null
114
115
  * when the selection was clean. NEVER throws.
115
116
  */
116
- export declare function selectImplementorAgent(detected: readonly AgentKind[], override?: AgentKind | null): {
117
+ export declare function selectImplementorAgent(detected: readonly AgentKind[], override?: AgentKind | null, inFlightAgents?: readonly AgentKind[] | null, priorityOrder?: readonly AgentKind[] | null): {
117
118
  agent: AgentKind;
118
119
  note: string | null;
119
120
  };
@@ -320,9 +321,8 @@ export interface QuorumLoopDeps {
320
321
  /**
321
322
  * CP-14 Wave A — the team execution model. `worktree_merge` makes each team
322
323
  * track commit to a persistent branch (the merge-before-push spine) instead of
323
- * the merge-train promote (the only model since the §10 retirement). Omitted keeps the
324
- * shipped path byte-identical. Resolved from `CODEVIBE_TEAM_EXECUTION` when
325
- * unset (see {@link resolveTeamExecution}).
324
+ * the merge-train promote (the sole model since the §10 retirement). Omitted keeps the
325
+ * shipped path byte-identical (see {@link resolveTeamExecution}).
326
326
  */
327
327
  teamExecution?: TeamExecution;
328
328
  /**
@@ -332,11 +332,8 @@ export interface QuorumLoopDeps {
332
332
  * agent reviewer runs INSIDE the sandbox with no ambient vendor creds and a
333
333
  * broker-routed model call — closing the CP-14 §8/§13 reviewer-sandbox gap.
334
334
  * ABSENT (legacy / companion / tests) → reviewers take the legacy unsandboxed
335
- * path BYTE-IDENTICAL (§7 non-regression). Reviewer-sandboxing DEFAULTS ON
336
- * when this engager is wired (R7 A4), with an operator opt-out env flag
337
- * (`CODEVIBE_SANDBOX_REVIEWERS=0`, read in `engageSeatSubstrate` on the
338
- * `spawnOneSeat` path — see `reviewerSandboxingEnabled`). The flag is
339
- * independent of implementor sandboxing (which has its own engager).
335
+ * path BYTE-IDENTICAL (§7 non-regression). Reviewer-sandboxing is mandatory
336
+ * when this engager is wired (see `reviewerSandboxingEnabled`).
340
337
  */
341
338
  engageReviewerSubstrate?: ReviewerSubstrateEngager;
342
339
  /**
@@ -599,6 +596,51 @@ export interface TaskOrdinalSink {
599
596
  }): number | undefined;
600
597
  label(taskId: string): string;
601
598
  }
599
+ /**
600
+ * P46a D2 / P46b §3.7 — a single task's recorded state for `/status`. `queued`
601
+ * waits for a running-track slot; `cancelled` ended before it started
602
+ * (`QUEUED_WORK_ENDED`, or a `cancelled` START replay); `already_finished` is a
603
+ * START replay of work that already ended.
604
+ */
605
+ export type SingleTaskOutcome = 'running' | 'queued' | 'applied' | 'discarded' | 'apply_failed' | 'unresolved' | 'rejected' | 'cancelled' | 'already_finished';
606
+ /** P46b §3.3 — what a confirmed START did. */
607
+ export interface StartTaskResultInfo {
608
+ taskId: string;
609
+ outcome: 'running' | 'queued' | 'already_finished' | 'cancelled';
610
+ /** Present when queued: 1-based position in the account's queue. */
611
+ queuePosition?: number;
612
+ runningTracks?: number;
613
+ trackBudget?: number;
614
+ }
615
+ /**
616
+ * P46b §3.13 item 1 — a single task's lifecycle change the shell turns into a
617
+ * store update and a phone line: queued at START, started (immediately or when
618
+ * a slot freed), or cancelled before it started.
619
+ */
620
+ export type TaskLifecycleEvent = {
621
+ kind: 'queued';
622
+ taskId: string;
623
+ agent: AgentKind;
624
+ queuePosition?: number;
625
+ runningTracks?: number;
626
+ trackBudget?: number;
627
+ } | {
628
+ kind: 'started';
629
+ taskId: string;
630
+ agent: AgentKind;
631
+ fromQueue: boolean;
632
+ retryOf?: string;
633
+ } | {
634
+ kind: 'cancelled';
635
+ taskId: string;
636
+ reason: string;
637
+ };
638
+ /** P46b §3.6 — the desktop's slot-lease capability sent on every START. */
639
+ export declare const SLOT_LEASE_PROTOCOL = 1;
640
+ /** P46b §3.6 — one task lease heartbeat per held task every 2 minutes (the engine lease is 600 s). */
641
+ export declare const TASK_LEASE_HEARTBEAT_INTERVAL_MS: number;
642
+ /** P46b §3.3 — map the START answer's state label onto what the START did. */
643
+ export declare function startOutcomeFor(workflowState: string): StartTaskResultInfo['outcome'];
602
644
  /** P46a C3 — the desktop-local "could not apply" menu the loop asks the shell to present. */
603
645
  export interface ApplyConflictMenu {
604
646
  taskId: string;
@@ -609,6 +651,14 @@ export interface ApplyConflictMenu {
609
651
  byTaskLabel?: string;
610
652
  }>;
611
653
  autoRetried: boolean;
654
+ /** P46b §3.10 — `group` for a team promote (`taskId` is the taskGroupId). */
655
+ kind?: 'single' | 'group';
656
+ /** P46b §3.9 — counts only, per cause (the retry prompt's content). */
657
+ counts?: {
658
+ sessionTask: number;
659
+ external: number;
660
+ writeErrors: number;
661
+ };
612
662
  retryBrief: string;
613
663
  /** The ROOT user request (Stage 1 r3 F1) — the menu's retry carries it (web-access decision, `/status`). */
614
664
  request: string;
@@ -639,6 +689,19 @@ export declare class QuorumLoop {
639
689
  private latestInFlightStart;
640
690
  /** Stage 1 r1 F13 — every START still in flight, in issue order; `latestInFlightStart` is its last entry. */
641
691
  private readonly startOrderInFlight;
692
+ /**
693
+ * P46b §3.7 — a queued task's START answer (position, running tracks, budget)
694
+ * and, once it ended before starting, the reason. Kept for `/status`.
695
+ */
696
+ private readonly queueInfoByTask;
697
+ /** P46b §3.7 — single tasks whose implementor has been spawned at least once (`starting` ends). */
698
+ private readonly spawnedSingleTasks;
699
+ /** P46b §3.13 item 1 — the shell's lifecycle sink (store update + phone line). */
700
+ private taskLifecycleSink?;
701
+ /** P46b §3.6 — the one per-session task lease heartbeat timer (null while stopped). */
702
+ private taskLeaseHeartbeatTimer;
703
+ /** P46b §3.6 — tasks whose heartbeat request is still in flight (one at a time per task). */
704
+ private readonly taskLeaseHeartbeatsInFlight;
642
705
  /** Tasks whose next automatic revise round must start a fresh review baseline. */
643
706
  private readonly reviewScopeResetTasks;
644
707
  /** Non-terminal tasks for which `/review-reset` may arm the next revise round. */
@@ -818,8 +881,8 @@ export declare class QuorumLoop {
818
881
  * the stdin, NEVER the task_brief the reviewers see.
819
882
  * 2. The agentic class-2 resolver brief (≤ RATIONALE_RESOLVER_MAX_BYTES,
820
883
  * ungated — the resolver is intent-starved by design).
821
- * 3. Reviewer packets, ONLY behind the DEFAULT-OFF
822
- * OQ-REVIEWER-RATIONALE flag, as a labeled UNTRUSTED claim.
884
+ * 3. Reviewer packets NEVER receive implementor rationale (permanent
885
+ * diff-only review policy invariant).
823
886
  * Lifecycle mirrors `userNotesByTask` (promote / terminal discard / restart
824
887
  * clear; PRESERVED across the non-terminal quota-fork/takeover discards).
825
888
  */
@@ -1120,8 +1183,22 @@ export declare class QuorumLoop {
1120
1183
  */
1121
1184
  private recoverySeedingDepth;
1122
1185
  /**
1123
- * Recovery B.1 and B.2 can overlap. A per-call two-row batch is therefore
1124
- * insufficient: two concurrent calls could each launch two implementors.
1186
+ * P46b (E2E T-3) how many barrier holders are in their PASS-2 relaunch (each
1187
+ * counted within `recoverySeedingDepth`), and the tracks they relaunch
1188
+ * (refcounted task ids and brief slots). While EVERY holder is relaunching (none
1189
+ * is still querying, seeding briefs or binding a `/team` create), only packets
1190
+ * for a relaunched track keep buffering — the relaunch-before-drain dedup order
1191
+ * is unchanged for them. A packet for any other track (a queued track promoted
1192
+ * meanwhile, whose brief PASS 1 already seeded) is consumed live instead of
1193
+ * waiting for every relaunched implementor round to finish, which can outlast
1194
+ * its 15-minute lease.
1195
+ */
1196
+ private recoveryRelaunchPhaseDepth;
1197
+ private readonly recoveryRelaunchTaskIds;
1198
+ private readonly recoveryRelaunchSlots;
1199
+ /**
1200
+ * Recovery B.1 and B.2 can overlap. A per-call batch is therefore
1201
+ * insufficient: two concurrent calls could each launch a full batch.
1125
1202
  * These permits are owned by the QuorumLoop/session and cover the complete
1126
1203
  * `launchRecoveredTrack` operation across every recovery entry path.
1127
1204
  */
@@ -1325,6 +1402,43 @@ export declare class QuorumLoop {
1325
1402
  retryOf: string;
1326
1403
  agent: AgentKind;
1327
1404
  }) => void) | undefined): void;
1405
+ /**
1406
+ * P46b §3.10 — a team promote could not be applied (the team promote has no
1407
+ * per-path session attribution, so every changed path is `external`). A team
1408
+ * gets no automatic retry: it always offers the menu (retry reissues the
1409
+ * whole team from its retained launch spec).
1410
+ */
1411
+ reportTeamApplyConflict(info: {
1412
+ taskGroupId: string;
1413
+ conflicts: readonly string[];
1414
+ writeErrors: readonly string[];
1415
+ }): void;
1416
+ /** P46b §3.13 item 1 — install the task lifecycle sink (the shell). */
1417
+ setTaskLifecycleSink(sink: ((event: TaskLifecycleEvent) => void) | undefined): void;
1418
+ /** P46b — notify the lifecycle sink; a throwing sink never breaks the loop. */
1419
+ private emitTaskLifecycle;
1420
+ /**
1421
+ * P46b §3.6 — the first change of a queued task to `cancelled (reason)`. Shared
1422
+ * by the `QUEUED_WORK_ENDED` packet, the `/status` catch-up and a `cancelled`
1423
+ * answer to `/task cancel`, so whichever arrives first acts and the others do
1424
+ * nothing. Returns true only on that first change.
1425
+ */
1426
+ markQueuedTaskCancelled(taskId: string, reason: string): boolean;
1427
+ /** P46b §3.6 — the `QUEUED_WORK_ENDED` Class-B packet. */
1428
+ private handleQueuedWorkEnded;
1429
+ /** P46b §3.6 — start the per-session task lease heartbeat (idempotent). */
1430
+ private ensureTaskLeaseHeartbeat;
1431
+ /** P46b §3.6 — stop the task lease heartbeat (terminal: shutdown or no task left). */
1432
+ private stopTaskLeaseHeartbeat;
1433
+ /**
1434
+ * P46b §3.6 — one heartbeat for every single task this desktop holds in the
1435
+ * implement–review loop (queued, starting, implementing, in review, awaiting
1436
+ * you): `recordExecutionEvent(PROGRESS_EVENT, {kind: TASK_LEASE_HEARTBEAT,
1437
+ * taskId})`, which RENEWs the task's slot lease. A START still in flight is
1438
+ * skipped (its holder may not exist yet); a beat still in flight is not
1439
+ * doubled. Work this desktop no longer knows stops renewing and lapses.
1440
+ */
1441
+ sendTaskLeaseHeartbeats(): Promise<void>;
1328
1442
  /** P46a D1 — `#n` / `#n retry` / short id for any task id (never throws). */
1329
1443
  labelFor(taskId: string): string;
1330
1444
  /**
@@ -1338,9 +1452,15 @@ export declare class QuorumLoop {
1338
1452
  agent: AgentKind;
1339
1453
  brief: string;
1340
1454
  retryOf?: string;
1341
- outcome: 'running' | 'applied' | 'discarded' | 'apply_failed' | 'unresolved' | 'rejected';
1455
+ outcome: SingleTaskOutcome;
1342
1456
  implementorActive: boolean;
1343
1457
  focused: boolean;
1458
+ /** P46b §3.7 — START confirmed or dispatched, implementor not spawned yet. */
1459
+ starting?: boolean;
1460
+ /** P46b §3.7 — queue position from the START answer (queued tasks). */
1461
+ queuePosition?: number;
1462
+ /** P46b §3.6 — why a queued task ended before it started. */
1463
+ cancelReason?: string;
1344
1464
  }>;
1345
1465
  /** Stage 1 r1 F13 — a START confirmed or rejected: the newest START still in flight (if any) becomes the empty-envelope target. */
1346
1466
  private dropInFlightStart;
@@ -1444,6 +1564,7 @@ export declare class QuorumLoop {
1444
1564
  gateDispatch: (payload: GateDispatchPayload, envelopeTaskId?: string) => Promise<void>;
1445
1565
  spawnReviewers: (payload: ReviewerDispatchPayload) => Promise<void>;
1446
1566
  runReviseRound: (payload: ReviseFeedbackPayload) => Promise<void>;
1567
+ queuedWorkEnded: (payload: QueuedWorkEndedPayload) => Promise<void>;
1447
1568
  };
1448
1569
  private cachedSigningKey;
1449
1570
  private verifySignature;
@@ -1493,9 +1614,7 @@ export declare class QuorumLoop {
1493
1614
  * read this instead. Omitted for an ordinary task (its brief IS the request).
1494
1615
  */
1495
1616
  request?: string;
1496
- }): Promise<{
1497
- taskId: string;
1498
- } | null>;
1617
+ }): Promise<StartTaskResultInfo | null>;
1499
1618
  /**
1500
1619
  * CP-1.f Wave 3 FIX — consume a GATE_DISPATCH packet. The engine emits this
1501
1620
  * after `startTask` creates the round-0 `reviewer_quorum` gate; its
@@ -1675,8 +1794,8 @@ export declare class QuorumLoop {
1675
1794
  * `handleReviseFeedback` so the revise implementor does not re-derive its
1676
1795
  * own reasoning. Appended to the STDIN payload ONLY (self-visibility) —
1677
1796
  * NEVER to `args.brief`, which becomes the execute-first `task_brief`
1678
- * rendered into the reviewer prompts (OQ-REVIEWER-RATIONALE: GA reviewers
1679
- * see NO rationale; the flag-gated reviewer path is separate).
1797
+ * rendered into the reviewer prompts (OQ-REVIEWER-RATIONALE: reviewers
1798
+ * see NO rationale permanent diff-only review policy invariant).
1680
1799
  */
1681
1800
  priorRationale?: {
1682
1801
  round: number;
@@ -2096,8 +2215,7 @@ export declare class QuorumLoop {
2096
2215
  * path (that defeats the moat).
2097
2216
  *
2098
2217
  * TRI-STATE (decided here, mirroring the implementor LE):
2099
- * 1. No engager wired OR `CODEVIBE_SANDBOX_REVIEWERS=0` opted out (the gate
2100
- * DEFAULTS ON — R7 A4) → LEGACY (byte-identical non-regression). No badge.
2218
+ * 1. No engager wired LEGACY (byte-identical non-regression). No badge.
2101
2219
  * 1b. No resolvable taskId (cannot scope a per-task broker) → LEGACY + a
2102
2220
  * reduced-trust badge (Stage-2 r1 Codex LOW — a trust-downgrade must be
2103
2221
  * VISIBLE, never a silent ambient-cred fallback; see
@@ -2144,7 +2262,7 @@ export declare class QuorumLoop {
2144
2262
  private describeReviewShapeForDiagnostics;
2145
2263
  /**
2146
2264
  * Wave B §2.1 confinement TRI-STATE for a merge-gate model/reviewer invocation
2147
- * (LOCK #C14-MOAT). Path A: the CP-7 substrate/broker (not opted out + a Trusted
2265
+ * (LOCK #C14-MOAT). Path A: the CP-7 substrate/broker (a Trusted
2148
2266
  * agent + engage succeeds → a handle; no ambient vendor cred in the child). Path B: a
2149
2267
  * trusted-container boundary (legacy spawn confined by the OUTER container — the
2150
2268
  * dogfood/E2E path). Path C: neither → NOT confined. A path-A engage that
@@ -2188,13 +2306,11 @@ export declare class QuorumLoop {
2188
2306
  * unwired, keeping today's IMMEDIATE legacy human-gate instead of
2189
2307
  * converting it into a per-slot spawn-time fail-close.
2190
2308
  *
2191
- * Deliberately DECOUPLED from `engageWaveBConfinement` and its
2192
- * `CODEVIBE_SANDBOX_REVIEWERS` gate (default-ON, `=0` opts out — R7 A4):
2193
- * that flag gates REVIEWER sandboxing (per-seat, `makeConflictModelCaller`,
2194
- * Tier-2) exactly as before — riding
2195
- * it here made the A1d resolver row unreachable on the default bare host.
2196
- * The ladder select is side-effect-free (no launch, no broker); test seams
2197
- * via `deps.resolverProbeDeps`.
2309
+ * Deliberately DECOUPLED from `engageWaveBConfinement` reviewer sandboxing:
2310
+ * that mechanism gates REVIEWER sandboxing (per-seat, `makeConflictModelCaller`,
2311
+ * Tier-2) coupling it here made the A1d resolver row unreachable on the
2312
+ * default bare host. The ladder select is side-effect-free (no launch, no broker);
2313
+ * test seams via `deps.resolverProbeDeps`.
2198
2314
  */
2199
2315
  private probeResolverDispatchConfinement;
2200
2316
  /**
@@ -2208,11 +2324,9 @@ export declare class QuorumLoop {
2208
2324
  * MED): `probeResolverDispatchConfinement` wires the closure iff the
2209
2325
  * DISPATCH-time engage could establish a confining substrate on this
2210
2326
  * host (a real §6 Q2 ladder tier, or the preserved trusted-container
2211
- * posture). Deliberately DECOUPLED from `engageWaveBConfinement`'s
2212
- * `CODEVIBE_SANDBOX_REVIEWERS` gate (default-ON post-R7-A4) — that flag
2213
- * gates REVIEWER
2214
- * sandboxing (seat/`makeConflictModelCaller`/Tier-2, unchanged), and
2215
- * riding it here made the A1d write-confined resolver row unreachable
2327
+ * posture). Deliberately DECOUPLED from `engageWaveBConfinement` — that
2328
+ * mechanism gates REVIEWER sandboxing (seat/`makeConflictModelCaller`/Tier-2),
2329
+ * and coupling it here made the A1d write-confined resolver row unreachable
2216
2330
  * on the default bare-host path. Unwired → return `null` →
2217
2331
  * `resolveAgentic` is NOT set on the context → a fresh class-2 conflict
2218
2332
  * takes the legacy `caller` path (which on path C is the
@@ -2374,6 +2488,15 @@ export declare class QuorumLoop {
2374
2488
  * here; single-impl / non-team packets bypass this entirely.
2375
2489
  */
2376
2490
  bufferTeamPacketDuringSeeding(kind: 'trackAssigned' | 'gateDispatch', payload: unknown, envelopeTaskId?: string): boolean;
2491
+ /**
2492
+ * P46b (E2E T-3) — whether a live team packet belongs to a track a recovery is
2493
+ * relaunching right now. A packet that names no track this can match (no
2494
+ * envelope task id and no TrackAssigned ids) counts as one, so it keeps
2495
+ * buffering exactly as before.
2496
+ */
2497
+ private isRecoveryRelaunchPacket;
2498
+ private enterRecoveryRelaunchPhase;
2499
+ private leaveRecoveryRelaunchPhase;
2377
2500
  /**
2378
2501
  * §4.3 — drain the buffered live TEAM packets through the normal live handlers
2379
2502
  * AFTER the recovery seed barrier lifts (`recoverySeedingDepth === 0`). Each