@quantiya/codevibe-claude-plugin 1.0.36 → 1.0.37

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 (59) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/server.js +13 -13
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/appsync-client.test.d.ts +1 -0
  4. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +139 -1
  5. package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.d.ts +5 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/audit-keys/__tests__/audit-keys-parity.test.d.ts +1 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/audit-keys/index.d.ts +41 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/auth/__tests__/auth-telemetry.test.d.ts +1 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/auth/auth-telemetry.d.ts +29 -8
  10. package/node_modules/@quantiya/codevibe-core/dist/index.d.ts +4 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/index.js +194 -33
  12. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-bootstrap.test.d.ts +1 -0
  13. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-failure-recourse.test.d.ts +1 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-save.test.d.ts +1 -0
  15. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-seat-picker.test.d.ts +1 -0
  16. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-telemetry.test.d.ts +1 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-test-agents.test.d.ts +1 -0
  18. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-types.test.d.ts +1 -0
  19. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/setup-wizard.test.d.ts +1 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/orchestration/__tests__/v1-options.test.d.ts +1 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/orchestration/detect-agents.d.ts +56 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/orchestration/index.d.ts +3 -0
  23. package/node_modules/@quantiya/codevibe-core/dist/orchestration/orchestration-cli.d.ts +12 -0
  24. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-bootstrap.d.ts +146 -0
  25. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-failure-recourse.d.ts +23 -0
  26. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-save.d.ts +47 -0
  27. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-seat-picker.d.ts +72 -0
  28. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-telemetry.d.ts +54 -0
  29. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-test-agents.d.ts +108 -0
  30. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-types.d.ts +140 -0
  31. package/node_modules/@quantiya/codevibe-core/dist/orchestration/setup-wizard.d.ts +57 -0
  32. package/node_modules/@quantiya/codevibe-core/dist/orchestration/v1-options.d.ts +108 -0
  33. package/node_modules/@quantiya/codevibe-core/dist/reviewer/__tests__/integration.test.d.ts +1 -0
  34. package/node_modules/@quantiya/codevibe-core/dist/reviewer/__tests__/mocks.test.d.ts +1 -0
  35. package/node_modules/@quantiya/codevibe-core/dist/reviewer/__tests__/output-parser.test.d.ts +1 -0
  36. package/node_modules/@quantiya/codevibe-core/dist/reviewer/__tests__/registry.test.d.ts +1 -0
  37. package/node_modules/@quantiya/codevibe-core/dist/reviewer/__tests__/subprocess.test.d.ts +1 -0
  38. package/node_modules/@quantiya/codevibe-core/dist/reviewer/index.d.ts +15 -0
  39. package/node_modules/@quantiya/codevibe-core/dist/reviewer/mocks.d.ts +80 -0
  40. package/node_modules/@quantiya/codevibe-core/dist/reviewer/output-parser.d.ts +95 -0
  41. package/node_modules/@quantiya/codevibe-core/dist/reviewer/provider.d.ts +153 -0
  42. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/claude-live-smoke.test.d.ts +1 -0
  43. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/claude.test.d.ts +1 -0
  44. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/codex-live-smoke.test.d.ts +1 -0
  45. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/codex.test.d.ts +1 -0
  46. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/gemini-live-smoke.test.d.ts +1 -0
  47. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/__tests__/gemini.test.d.ts +1 -0
  48. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/claude.d.ts +59 -0
  49. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/codex.d.ts +67 -0
  50. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/common.d.ts +25 -0
  51. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/gemini.d.ts +108 -0
  52. package/node_modules/@quantiya/codevibe-core/dist/reviewer/registry.d.ts +87 -0
  53. package/node_modules/@quantiya/codevibe-core/dist/reviewer/subprocess.d.ts +117 -0
  54. package/node_modules/@quantiya/codevibe-core/dist/reviewer/types.d.ts +101 -0
  55. package/node_modules/@quantiya/codevibe-core/dist/types/index.d.ts +2 -0
  56. package/node_modules/@quantiya/codevibe-core/dist/types/orchestration.d.ts +57 -0
  57. package/node_modules/@quantiya/codevibe-core/dist/types/reviewer.d.ts +67 -0
  58. package/node_modules/@quantiya/codevibe-core/dist/types/session.d.ts +16 -0
  59. package/package.json +1 -1
@@ -0,0 +1,140 @@
1
+ import type { ReviewerRole as PolicyReviewerRole } from '../reviewer/types.js';
2
+ import type { AgentKind } from '../reviewer/types.js';
3
+ import { AgentType } from '../types/session.js';
4
+ import { ReviewerRole as SchemaReviewerRole } from '../types/reviewer.js';
5
+ /**
6
+ * Tier returned by `getSubscriptionStatus`. Drives seat budget +
7
+ * tier-gate at bootstrap.
8
+ *
9
+ * UPPERCASE wire format matches `codevibe-backend/graphql/schema.graphql:209`
10
+ * (`enum SubscriptionTier { FREE PRO MAX }`).
11
+ */
12
+ export type WizardTier = 'FREE' | 'PRO' | 'MAX';
13
+ /**
14
+ * Tier-aware seat budget. Mirrors
15
+ * `codevibe-backend/lambda/user-management/index.ts:184-188`'s
16
+ * `REVIEWER_SEAT_COUNT` map. Free has no seats — orchestration is not
17
+ * permitted at that tier; the wizard short-circuits at bootstrap with
18
+ * the §6 upgrade interstitial.
19
+ */
20
+ export declare const SEAT_BUDGET: Record<WizardTier, number | null>;
21
+ /**
22
+ * The 3-role lock from PHASE-3-A-DESIGN.md: the wizard offers ONLY
23
+ * `architecture` / `correctness` / `security`. The schema's
24
+ * `ReviewerRole` enum at `codevibe-backend/graphql/schema.graphql:167-177`
25
+ * still has 9 variants for backward compat / 2.0.x custom-roles
26
+ * (#80) — but the wizard NEVER sends a non-3-role seat to
27
+ * `updateReviewerPolicy`.
28
+ *
29
+ * snake_case matches `codevibe-core/src/reviewer/types.ts:52-61` (the
30
+ * reviewer-module wire). The wizard converts to AppSync UPPERCASE at
31
+ * the save boundary via `roleToAppSync()`.
32
+ */
33
+ export declare const WIZARD_ROLES: readonly ["architecture", "correctness", "security"];
34
+ export type WizardRole = (typeof WIZARD_ROLES)[number];
35
+ /**
36
+ * Tier-aware default role for each seat (§1 "Default-role fallback").
37
+ * Seat 0 = architecture, Seat 1 = correctness, Seat 2 = security.
38
+ * When the canonical default is already taken by a previous seat, the
39
+ * wizard falls back to the first remaining role in canonical order.
40
+ */
41
+ export declare const DEFAULT_ROLE_BY_SEAT: WizardRole[];
42
+ /**
43
+ * Canonical agent ordering used for wizard defaults. The first
44
+ * installed agent in this order is the default for each seat unless
45
+ * the user picks otherwise.
46
+ */
47
+ export declare const AGENT_PRIORITY: AgentKind[];
48
+ /**
49
+ * The wizard's terminal outcomes. Drives `wizard_completed` /
50
+ * `wizard_aborted` GA4 events and the exit code.
51
+ */
52
+ export type WizardOutcome = {
53
+ kind: 'ok';
54
+ saved: true;
55
+ seats: ReadonlyArray<WizardSeatPick>;
56
+ } | {
57
+ kind: 'saved_after_test_warning';
58
+ saved: true;
59
+ seats: ReadonlyArray<WizardSeatPick>;
60
+ } | {
61
+ kind: 'aborted';
62
+ reason: WizardAbortReason;
63
+ lastStep: WizardStep;
64
+ };
65
+ /**
66
+ * Reasons the wizard exited without saving. Mirror the
67
+ * `wizard_aborted.reason` taxonomy in PHASE-3-A-DESIGN.md §7.
68
+ */
69
+ export type WizardAbortReason = 'ctrl_c' | 'tier_gate_free' | 'no_clis' | 'step_user_exit' | 'step_save_failed_exit' | 'auth_expired' | 'bootstrap_failure';
70
+ /**
71
+ * Wizard step taxonomy — used for `wizard_step_started`/`completed`/
72
+ * `failed` and `wizard_aborted.last_step`.
73
+ */
74
+ export type WizardStep = 'bootstrap' | 'seat_assignment' | 'test_my_agents' | 'save';
75
+ /**
76
+ * Per-step `wizard_step_failed.reason` codes. Mirror the table in
77
+ * PHASE-3-A-DESIGN.md §7. Each step has its own bounded reason set;
78
+ * `seat_assignment` has no failure modes (the picker filters out
79
+ * invalid choices UI-side).
80
+ */
81
+ export type WizardStepFailureReason = 'tier_gate_free' | 'not_signed_in' | 'subscription_status_network' | 'no_clis_installed' | 'test_revise' | 'test_reject' | 'test_escalate' | 'test_parse_failure' | 'test_spawn_failure' | 'test_timeout' | 'update_policy_network' | 'update_policy_5xx' | 'update_policy_throttle' | 'auth_token_expired';
82
+ /**
83
+ * One seat the user picked in Step 1. The wizard converts these to
84
+ * `ReviewerAgentSpecInput[]` at save time by uppercase-mapping role +
85
+ * agent (the schema's wire form).
86
+ */
87
+ export interface WizardSeatPick {
88
+ seatId: number;
89
+ agent: AgentKind;
90
+ role: WizardRole;
91
+ }
92
+ /**
93
+ * Latency bucket for GA4 `latency_bucket_s` / `total_latency_bucket_s`
94
+ * dimensions. Bounded set — keeps GA4 cardinality tractable per R2
95
+ * round-1 MEDIUM (PHASE-3-A-DESIGN.md §7).
96
+ */
97
+ export type LatencyBucket = '<2' | '2-5' | '5-10' | '10-30' | '30+';
98
+ /**
99
+ * Categorical bucket for "how many agents are installed" /
100
+ * "how many seats". Locked at "1" / "2" / "3" — there is no "4+"
101
+ * because Max is the ceiling.
102
+ */
103
+ export type CountBucket = '1' | '2' | '3';
104
+ /**
105
+ * Entry point identifier — distinguishes meta CLI invocation from
106
+ * each plugin's alias (`codevibe-{claude,gemini,codex} orchestration setup`).
107
+ */
108
+ export type WizardEntry = 'meta_cli' | 'claude_alias' | 'gemini_alias' | 'codex_alias';
109
+ /**
110
+ * Bucket a wall-clock duration into one of the 5 latency-bucket values.
111
+ * Used by telemetry for per-step + total-wizard timing dimensions.
112
+ */
113
+ export declare function bucketLatencySeconds(seconds: number): LatencyBucket;
114
+ /**
115
+ * Bucket an integer count into "1" / "2" / "3", clamping anything
116
+ * higher to "3". Used for `seats_bucket`, `agents_distinct_bucket`,
117
+ * `roles_distinct_bucket`, `installed_agents_bucket`.
118
+ */
119
+ export declare function bucketCount(n: number): CountBucket;
120
+ /**
121
+ * Map a wizard role (snake_case) to the reviewer-module's snake_case
122
+ * `ReviewerRole` literal type — used when building a `ReviewerSpec`
123
+ * for `createSubprocessReviewerRegistry().evaluate()`. Pass-through
124
+ * because `WizardRole` is a strict subset of `PolicyReviewerRole`.
125
+ */
126
+ export declare function roleToReviewerSpec(role: WizardRole): PolicyReviewerRole;
127
+ /**
128
+ * Map a wizard role (snake_case) to the AppSync schema's UPPERCASE
129
+ * `ReviewerRole` enum — used when building a `ReviewerAgentSpecInput`
130
+ * for the `updateReviewerPolicy` mutation. The wizard locks to 3
131
+ * roles, so this mapping is exhaustive over `WizardRole` and never
132
+ * produces one of the 6 non-3-role variants.
133
+ */
134
+ export declare function roleToAppSync(role: WizardRole): SchemaReviewerRole;
135
+ /**
136
+ * Map AgentKind (lowercase reviewer-module wire) to the AppSync
137
+ * `AgentType` enum (UPPERCASE). Used at save time when building the
138
+ * `ReviewerAgentSpecInput[]` for `updateReviewerPolicy`.
139
+ */
140
+ export declare function agentToAppSync(agent: AgentKind): AgentType;
@@ -0,0 +1,57 @@
1
+ import { DetectableAgent } from './detect-agents.js';
2
+ import { WizardEntry } from './setup-types.js';
3
+ import { PickerIO } from './setup-seat-picker.js';
4
+ import { AppSyncClient } from '../appsync/appsync-client.js';
5
+ import type { ReviewerSpec, ReviewerVerdict } from '../reviewer/index.js';
6
+ /**
7
+ * Parse `--entry=<value>` from argv. Defaults to `meta_cli` if absent
8
+ * or invalid. Plugin wrappers pass `--entry=claude_alias` /
9
+ * `gemini_alias` / `codex_alias` per the §4 alias contract.
10
+ */
11
+ export declare function parseEntry(argv: string[]): WizardEntry;
12
+ /**
13
+ * Injectable dependencies for `runSetupWizardWithDeps`. Factored out
14
+ * so unit tests can drive the whole state machine deterministically
15
+ * (no real readline, no real subprocess spawn, no real network).
16
+ *
17
+ * Production callers use `runSetupWizard`, which wires the production
18
+ * deps + calls `process.exit(result.exitCode)`.
19
+ */
20
+ export interface SetupWizardDeps {
21
+ /** Builds an authenticated AppSyncClient or returns null if unauthenticated. */
22
+ clientFactory: () => Promise<AppSyncClient | null>;
23
+ /** Returns the subset of agents present on PATH. */
24
+ agentDetector: () => DetectableAgent[];
25
+ /** Optional reviewer registry (Step 2). Default: real subprocess registry. */
26
+ registryFactory?: () => {
27
+ evaluate: (spec: ReviewerSpec, gateId: string) => Promise<ReviewerVerdict>;
28
+ };
29
+ /** Output writer — writes the line as-is (caller controls newlines). */
30
+ write: (line: string) => void;
31
+ /** Build a fresh PickerIO + close handle for the next interactive prompt. */
32
+ createPickerIO: () => {
33
+ io: PickerIO;
34
+ close: () => void;
35
+ };
36
+ }
37
+ /**
38
+ * Result of the wizard's state machine. The public `runSetupWizard`
39
+ * wrapper translates this to a `process.exit(exitCode)`.
40
+ */
41
+ export interface SetupWizardResult {
42
+ exitCode: number;
43
+ }
44
+ /**
45
+ * Production entry point. Called from `runOrchestrationCli` when
46
+ * `argv[3] === 'setup'`. Builds production deps + calls process.exit
47
+ * with the wizard's exit code. Drives the full state machine via
48
+ * `runSetupWizardWithDeps`.
49
+ */
50
+ export declare function runSetupWizard(argv: string[]): Promise<void>;
51
+ /**
52
+ * Testable wizard core. Exits with code 0 on success or any user-
53
+ * initiated abort; exit 1 on a failure the user couldn't recover
54
+ * from in the recourse loop. SIGINT is registered + cleaned up
55
+ * around the call (no-op for tests when SIGINT is never fired).
56
+ */
57
+ export declare function runSetupWizardWithDeps(argv: string[], deps: SetupWizardDeps): Promise<SetupWizardResult>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Discriminator value emitted by the engine on
3
+ * `metadata.prompt_kind` for orchestration-escalated gate prompts.
4
+ *
5
+ * Sources of truth (any drift breaks the V1 bridge):
6
+ * - Engine emit: codevibe-core-rs `appsync_emit.rs:847-862`
7
+ * (`build_metadata_json` — emits exactly 5 keys: `tool_name`,
8
+ * `payload`, `prompt_kind`, `summary`, `timeline`).
9
+ * - iOS branch: MessageBubble.swift `extractInteractivePromptOptions`.
10
+ * - Android branch: MessageBubble.kt `extractOptions`.
11
+ */
12
+ export declare const V1_ORCHESTRATION_PROMPT_KIND: "orchestration_escalated_gate";
13
+ /**
14
+ * V1 user-decision kind (3-position subset of the 5-kind enum).
15
+ *
16
+ * Lowercase snake_case matches `@quantiya/quorum-core`'s `UserDecisionKind`
17
+ * caller-facing type at `codevibe-core-rs/packages/quorum-core/src/types.ts:232-238`.
18
+ * The quorum-core SDK's `applyUserDecision` mutator accepts this lowercase
19
+ * form and internally uppercases via `toWireEnum` before AppSync wire-send
20
+ * (`client.ts:244-246` + line 474). Plugins pass lowercase; the SDK is the
21
+ * conversion layer.
22
+ *
23
+ * iOS + Android pin UPPERCASE in their own enums because they call AppSync
24
+ * GraphQL directly via Amplify (no SDK conversion layer in their path).
25
+ * Both are correct at their respective layers.
26
+ *
27
+ * The two `_WITH_NOTES` variants from the full 5-kind enum are V2 — they
28
+ * require a `notes: EncryptedPayloadInput` value alongside the kind, and
29
+ * V1's "type a number" UX has no notes-input surface. They are deliberately
30
+ * EXCLUDED from this union; `mapOptionNumberToUserDecisionKind` cannot
31
+ * return them.
32
+ */
33
+ export type V1UserDecisionKind = 'accept' | 'reject_restart' | 'abort_task';
34
+ /**
35
+ * One row of the V1 orchestration option table.
36
+ *
37
+ * `number` is a string (`"1"` / `"2"` / `"3"`) to match iOS' tuple shape
38
+ * `(number: String, text: String)` and the existing
39
+ * `InteractivePromptOption` shape elsewhere in the mobile renderers.
40
+ */
41
+ export interface V1OrchestrationOption {
42
+ readonly number: string;
43
+ readonly label: string;
44
+ readonly kind: V1UserDecisionKind;
45
+ }
46
+ /**
47
+ * The V1 hardcoded 3-position option table.
48
+ *
49
+ * Order is LOAD-BEARING — `V1_ORCHESTRATION_OPTIONS[i].kind` is the kind
50
+ * sent to the server when the user types option-number `i+1`. Drift from
51
+ * iOS / Android = wrong `UserDecisionKind` on the wire (engine has no
52
+ * server-side validation).
53
+ *
54
+ * position 1 -> accept -> "Accept"
55
+ * position 2 -> reject_restart -> "Reject (restart proposal)"
56
+ * position 3 -> abort_task -> "Abort task"
57
+ *
58
+ * (Kind values are lowercase to match the SDK caller-facing type — see
59
+ * the header comment on the SDK conversion layer. iOS + Android pin
60
+ * UPPERCASE in their own constant tables because they hit AppSync
61
+ * directly without the SDK.)
62
+ *
63
+ * Frozen with `as const` so callers cannot mutate the array nor swap
64
+ * entries in place at runtime.
65
+ */
66
+ export declare const V1_ORCHESTRATION_OPTIONS: readonly [
67
+ V1OrchestrationOption,
68
+ V1OrchestrationOption,
69
+ V1OrchestrationOption
70
+ ];
71
+ /**
72
+ * Map a 1-based option number typed by the user to the matching V1
73
+ * `UserDecisionKind`. Returns `null` for ANY number outside 1..3 —
74
+ * callers MUST surface an error and abort the submit on null (the
75
+ * engine would reject `_WITH_NOTES` kinds without a notes payload in
76
+ * V1, and ANY out-of-range number means the user hit a typo, an
77
+ * accidental keystroke, or a malformed prompt).
78
+ *
79
+ * Out-of-range MUST return null — NOT fall back to `ACCEPT` or any
80
+ * default. A silent fallback would silently destroy or accept the
81
+ * user's work without their consent. Pinned by the
82
+ * `out-of-range returns null` test.
83
+ *
84
+ * @param optionNumber 1-based option position as typed by the user.
85
+ * @returns The matching `V1UserDecisionKind`, or `null` when out of range.
86
+ */
87
+ export declare function mapOptionNumberToUserDecisionKind(optionNumber: number): V1UserDecisionKind | null;
88
+ /**
89
+ * Shorter alias of `mapOptionNumberToUserDecisionKind`. Matches the helper
90
+ * name used in `PHASE-3-B-MOBILE-V1-BRIDGE-DESIGN.md` §3.1 + §6 Test 9 +
91
+ * §8 source-map row (`mapOptionToUserDecisionKind`). Both names point at
92
+ * the same function — pick whichever reads cleaner at the call site.
93
+ *
94
+ * Plugins can `import { mapOptionToUserDecisionKind }` per the design
95
+ * doc's call-site example at §3.1.
96
+ */
97
+ export declare const mapOptionToUserDecisionKind: typeof mapOptionNumberToUserDecisionKind;
98
+ /**
99
+ * Lowercase-to-uppercase conversion: `V1UserDecisionKind` (caller-facing,
100
+ * matches `@quantiya/quorum-core` SDK) → `UserDecisionKind` wire enum
101
+ * (matches AppSync schema). Plugins that call `AppSyncClient.applyUserDecision`
102
+ * directly (no SDK in path) use this helper to bridge the V1 option table's
103
+ * lowercase kind to the wire form.
104
+ *
105
+ * Total over the 3 V1 kinds — never returns null for a valid
106
+ * `V1UserDecisionKind` input.
107
+ */
108
+ export declare function mapV1KindToWire(kind: V1UserDecisionKind): 'ACCEPT' | 'REJECT_RESTART' | 'ABORT_TASK';
@@ -0,0 +1,15 @@
1
+ export type { AgentKind, ReviewerRole, ReviewerVerdict, VerdictId, VerdictKind, } from './types.js';
2
+ export type { ReviewerError, ReviewerProvider, ReviewerSpec, } from './provider.js';
3
+ export { ReviewerErrorClass } from './provider.js';
4
+ export type { ParseResult, ParsedVerdict, VerdictParseError, } from './output-parser.js';
5
+ export { parseVerdictOutput, VerdictParseErrorClass } from './output-parser.js';
6
+ export type { RunReviewerOptions, SubprocessError, SubprocessOutcome, } from './subprocess.js';
7
+ export { runReviewer, SubprocessErrorClass } from './subprocess.js';
8
+ export type { ClaudeReviewerProviderOptions } from './providers/claude.js';
9
+ export { ClaudeReviewerProvider } from './providers/claude.js';
10
+ export type { GeminiEnvelope, GeminiModelStats, GeminiReviewerProviderOptions, GeminiStats, } from './providers/gemini.js';
11
+ export { GeminiReviewerProvider } from './providers/gemini.js';
12
+ export type { CodexReviewerProviderOptions } from './providers/codex.js';
13
+ export { CodexReviewerProvider } from './providers/codex.js';
14
+ export { ReviewerRegistry, createSubprocessReviewerRegistry, } from './registry.js';
15
+ export { MockReviewerSpawner, StaticReviewerMock } from './mocks.js';
@@ -0,0 +1,80 @@
1
+ import { type ReviewerProvider, type ReviewerSpec } from './provider.js';
2
+ import type { AgentKind, ReviewerVerdict, VerdictKind } from './types.js';
3
+ import { type ReviewerError } from './provider.js';
4
+ /**
5
+ * Scripted `ReviewerProvider` for tests. Each `evaluate(spec, gateId)`
6
+ * pops the next response from the FIFO queue keyed by
7
+ * `(spec.agent, gateId)`. If no script remains, returns a SpawnFailed
8
+ * error with a diagnostic so test setup bugs surface loudly.
9
+ *
10
+ * Use the `script_*` methods to queue responses before tests call
11
+ * `evaluate`.
12
+ */
13
+ export declare class MockReviewerSpawner implements ReviewerProvider {
14
+ private readonly scripts;
15
+ private static key;
16
+ /**
17
+ * Script a verdict (with empty `suggested_changes`) for the next
18
+ * `evaluate(agent, gateId)` call. Use `scriptVerdictWithChanges`
19
+ * when `kind === 'REVISE'` (the parser-locked invariant requires
20
+ * non-empty changes for REVISE).
21
+ */
22
+ scriptVerdict(agent: AgentKind, gateId: string, kind: VerdictKind, reasoning: string): void;
23
+ /**
24
+ * Script a verdict with explicit `suggested_changes`. Required when
25
+ * `kind === 'REVISE'`.
26
+ */
27
+ scriptVerdictWithChanges(agent: AgentKind, gateId: string, kind: VerdictKind, reasoning: string, suggested_changes: string[]): void;
28
+ /** Script an error to return on the next `evaluate(agent, gateId)` call. */
29
+ scriptError(agent: AgentKind, gateId: string, error: ReviewerError): void;
30
+ /**
31
+ * How many scripted responses remain for the given key. Useful for
32
+ * test post-conditions ("all scripts were consumed").
33
+ */
34
+ remaining(agent: AgentKind, gateId: string): number;
35
+ evaluate(spec: ReviewerSpec, gateId: string): Promise<ReviewerVerdict>;
36
+ }
37
+ /**
38
+ * Stateless `ReviewerProvider` for engine-level integration tests and
39
+ * smoke tests. Ignores `gateId`; returns a fixed verdict per agent
40
+ * (or a global default).
41
+ *
42
+ * Construct via static factories:
43
+ * - `StaticReviewerMock.allApprove()` / `.allReject()` / `.allRevise(changes)`
44
+ * / `.allEscalate()` — global default, every agent returns the same.
45
+ * - `StaticReviewerMock.allError(err)` — every agent returns the same error.
46
+ *
47
+ * Stack per-agent overrides on top:
48
+ * - `.withAgentVerdict(agent, kind)` — override one agent's verdict.
49
+ * - `.withAgentError(agent, err)` — override one agent's error.
50
+ *
51
+ * Stacking models mixed-verdict scenarios: e.g.
52
+ * `StaticReviewerMock.allApprove().withAgentVerdict('gemini', 'REJECT')`
53
+ * → Claude approves + Gemini rejects + Codex approves → engine escalates.
54
+ */
55
+ export declare class StaticReviewerMock implements ReviewerProvider {
56
+ private defaultResponse;
57
+ private readonly perAgent;
58
+ /** Empty mock. Returns SpawnFailed with a diagnostic on every
59
+ * `evaluate` call until a default or per-agent override is set. */
60
+ static new(): StaticReviewerMock;
61
+ /** All reviewers return APPROVE. */
62
+ static allApprove(): StaticReviewerMock;
63
+ /** All reviewers return REJECT. */
64
+ static allReject(): StaticReviewerMock;
65
+ /** All reviewers return REVISE with the given suggested changes.
66
+ * Empty `changes` falls back to a placeholder. */
67
+ static allRevise(changes: string[]): StaticReviewerMock;
68
+ /** All reviewers return ESCALATE. */
69
+ static allEscalate(): StaticReviewerMock;
70
+ /** All reviewers return the given error. */
71
+ static allError(err: ReviewerError): StaticReviewerMock;
72
+ /**
73
+ * Override the response for one agent. Stacks on top of whatever
74
+ * default was configured. Returns `this` for fluent chaining.
75
+ */
76
+ withAgentVerdict(agent: AgentKind, kind: VerdictKind): this;
77
+ /** Override to return an error for one specific agent. */
78
+ withAgentError(agent: AgentKind, err: ReviewerError): this;
79
+ evaluate(spec: ReviewerSpec, gateId: string): Promise<ReviewerVerdict>;
80
+ }
@@ -0,0 +1,95 @@
1
+ import type { VerdictKind } from './types.js';
2
+ /**
3
+ * Successfully parsed reviewer reply. The engine's fan-out layer lifts
4
+ * these fields into a `ReviewerVerdict` alongside telemetry (agent, tokens,
5
+ * latency, model) supplied by the subprocess layer.
6
+ */
7
+ export interface ParsedVerdict {
8
+ /** The verdict the reviewer returned. */
9
+ kind: VerdictKind;
10
+ /**
11
+ * Free-form reasoning text (joined with blank-line paragraph separators
12
+ * preserved). Never empty by the time we reach here — reviewers that
13
+ * return only a verdict line fail parsing via `reasoning_missing`.
14
+ */
15
+ reasoning: string;
16
+ /**
17
+ * Ordered list of specific changes. Always non-empty when
18
+ * `kind === 'REVISE'`; always empty otherwise (enforced by the parser
19
+ * via `revise_missing_changes` / `suggested_changes_require_revise`).
20
+ */
21
+ suggested_changes: string[];
22
+ }
23
+ /**
24
+ * Why a reviewer's reply failed the locked-format check. Discriminated
25
+ * union; the subprocess layer maps any of these into `ReviewerError` with
26
+ * `kind: 'parse_failure'` and the raw output attached.
27
+ *
28
+ * Mirrors Rust's `VerdictParseError` enum 1:1.
29
+ */
30
+ export type VerdictParseError = {
31
+ /** The entire reply was whitespace or empty. A reviewer that wrote
32
+ * nothing has effectively timed out. */
33
+ kind: 'empty_output';
34
+ } | {
35
+ /**
36
+ * The first non-blank line did not start with one of the four
37
+ * verdict keywords (case-insensitive). Includes any leading markdown
38
+ * formatting, quotation, or `VERDICT:` prefix that makes the line
39
+ * diverge from the locked contract.
40
+ *
41
+ * Also fired when a non-blank, non-indented line of prose appears
42
+ * after the bullet section starts (the format reserves post-bullet
43
+ * lines for blank lines or indented continuations only).
44
+ */
45
+ kind: 'invalid_verdict';
46
+ /** The offending line, trimmed but otherwise verbatim. */
47
+ found: string;
48
+ } | {
49
+ /** Verdict was parsed but no reasoning followed. The locked contract
50
+ * requires reasoning text so the audit record and user-facing
51
+ * disagreement UI have something to show; a bare APPROVE / REJECT
52
+ * line is a parse failure. */
53
+ kind: 'reasoning_missing';
54
+ } | {
55
+ /** Verdict was REVISE but no bulleted suggested-changes list followed.
56
+ * Design-locked: REVISE requires at least one concrete change. */
57
+ kind: 'revise_missing_changes';
58
+ } | {
59
+ /** A non-REVISE verdict was followed by a bulleted list, which the
60
+ * locked contract reserves for REVISE only. */
61
+ kind: 'suggested_changes_require_revise';
62
+ /** The non-REVISE verdict that erroneously had bullets. */
63
+ found: VerdictKind;
64
+ };
65
+ /**
66
+ * Class wrapper around a `VerdictParseError` so callers can `throw`/`catch`
67
+ * structured errors via JS idioms. `.detail` carries the discriminated
68
+ * union; `Error.message` is a human-readable formatting.
69
+ */
70
+ export declare class VerdictParseErrorClass extends Error {
71
+ readonly detail: VerdictParseError;
72
+ constructor(detail: VerdictParseError);
73
+ }
74
+ /**
75
+ * Result type for `parseVerdictOutput`. Discriminated by `ok`. Mirrors
76
+ * Rust's `Result<ParsedVerdict, VerdictParseError>` without forcing TS
77
+ * callers to `try/catch` — most call sites want to inspect failure types
78
+ * directly to feed the audit log.
79
+ */
80
+ export type ParseResult = {
81
+ ok: true;
82
+ verdict: ParsedVerdict;
83
+ } | {
84
+ ok: false;
85
+ error: VerdictParseError;
86
+ };
87
+ /**
88
+ * Parse a reviewer reply. Strict: any deviation from the locked format
89
+ * returns `{ ok: false, error: ... }` which the subprocess layer routes to
90
+ * a parse-failure `ReviewerError`.
91
+ *
92
+ * Mirrors Rust's `parse_verdict_output` byte-for-byte. Test coverage
93
+ * matches the Rust unit-test set verbatim.
94
+ */
95
+ export declare function parseVerdictOutput(raw: string): ParseResult;
@@ -0,0 +1,153 @@
1
+ import type { AgentKind, ReviewerRole, ReviewerVerdict } from './types.js';
2
+ /**
3
+ * Spec for spawning one reviewer at one gate. Constructed by the engine
4
+ * from `ReviewerAgentSpec` (from `PolicySnapshot`) + the context bundle for
5
+ * the gate.
6
+ *
7
+ * # Identity
8
+ *
9
+ * Per the 2026-04-23 seat/role pivot, the spec's primary identity is
10
+ * `seat_id` — position in the review panel — NOT `agent`. `agent` may
11
+ * repeat across seats within a single gate (single-vendor case: two Claude
12
+ * seats with different roles). Downstream consensus + verdict validation
13
+ * dedup on `seat_id`, never on `agent`. Providers echo `seat_id` + `role`
14
+ * back on the returned `ReviewerVerdict` so the audit trail can attribute
15
+ * verdicts by lens rather than by agent kind.
16
+ */
17
+ export interface ReviewerSpec {
18
+ /**
19
+ * Position in the review panel, 0-indexed. For an N-seat policy this is
20
+ * in `0..N`. The primary identity key for this reviewer across the
21
+ * engine, audit log, and FFI — providers MUST echo this value back on
22
+ * the produced `ReviewerVerdict.seat_id`.
23
+ */
24
+ seat_id: number;
25
+ /**
26
+ * The lens this seat reviews through — Architecture / Correctness /
27
+ * Security for code; Accuracy / Clarity / Completeness for docs;
28
+ * composites for mixed. Drives the role-specific prompt prefix and is
29
+ * echoed back on the verdict for audit attribution. Unique within a
30
+ * policy (a duplicate role defeats the orthogonality purpose).
31
+ */
32
+ role: ReviewerRole;
33
+ /**
34
+ * Which agent to spawn. MAY repeat across seats when roles differ — the
35
+ * single-vendor case the 2026-04-23 pivot enables.
36
+ */
37
+ agent: AgentKind;
38
+ /**
39
+ * Tool names the reviewer is allowed to invoke. Typically
40
+ * `["Read", "Grep", "Glob"]` per the read-only reviewer invariant. Shape
41
+ * is agent-agnostic here; each provider enforces the sandbox in its own
42
+ * way:
43
+ * - Claude: `--allowed-tools Read,Grep,Glob` CLI flag.
44
+ * - Gemini: `--approval-mode plan` (Gemini's first-class read-only mode).
45
+ * The `tool_allowlist` is intentionally unused by the Gemini provider;
46
+ * plan mode is the sandbox contract. 2.0.x task #62 tracks adding
47
+ * `--policy <tempfile>` as defense-in-depth.
48
+ * - Codex: `--sandbox read-only --skip-git-repo-check` flags + per-spawn
49
+ * ephemeral output file.
50
+ */
51
+ tool_allowlist: string[];
52
+ /**
53
+ * Pre-rendered reviewer prompt. Engine renders per artifact type
54
+ * (code / docs / mixed) with a role-specific prefix prepended before
55
+ * calling `evaluate`.
56
+ */
57
+ prompt_template: string;
58
+ /**
59
+ * Wall-clock timeout for one reviewer's verdict. After this elapses the
60
+ * provider should throw `ReviewerError` with `kind: 'timeout'` and
61
+ * cancel the underlying process.
62
+ */
63
+ timeout_ms: number;
64
+ /**
65
+ * Optional per-reviewer model preference. `null` defers to the agent's
66
+ * CLI default.
67
+ */
68
+ model_hint: string | null;
69
+ }
70
+ /**
71
+ * Typed error thrown by `ReviewerProvider.evaluate`. Discriminated union
72
+ * matching Rust's `#[serde(tag = "kind", rename_all = "snake_case")]` enum.
73
+ *
74
+ * The engine's consensus path treats any thrown error as equivalent to a
75
+ * `VerdictKind::Escalate` for routing (so safety-defaults-to-escalation
76
+ * holds), but the distinct variants matter for the audit log and the
77
+ * user-facing error message.
78
+ */
79
+ export type ReviewerError = {
80
+ /** Reviewer exceeded its `timeout_ms` budget. */
81
+ kind: 'timeout';
82
+ /** Which agent timed out (carried so the audit entry can attribute
83
+ * cost / reliability back to the specific agent). */
84
+ agent: AgentKind;
85
+ /** Wall-clock ms elapsed before timeout fired. */
86
+ elapsed_ms: number;
87
+ } | {
88
+ /** Reviewer process could not be launched (CLI missing, spawn syscall
89
+ * failed, etc.). */
90
+ kind: 'spawn_failed';
91
+ agent: AgentKind;
92
+ /** Human-readable cause. */
93
+ reason: string;
94
+ } | {
95
+ /** Reviewer returned but its output couldn't be parsed into a valid
96
+ * verdict. Raw output is preserved for the audit log. */
97
+ kind: 'parse_failure';
98
+ agent: AgentKind;
99
+ /** Raw output (truncated to a reasonable length by the caller if
100
+ * needed). */
101
+ raw_output: string;
102
+ } | {
103
+ /** Reviewer was cancelled by the engine before completing (e.g.,
104
+ * user abort, a sibling reviewer already hard-rejected). No
105
+ * per-agent attribution because cancellation can fire on any
106
+ * reviewer in flight. */
107
+ kind: 'cancelled';
108
+ } | {
109
+ /** A reviewer's task panicked or was aborted before returning a
110
+ * verdict. Mirror of the Rust `InternalJoinFailure` variant. The
111
+ * variant deliberately has no `agent` field — attributing a panic
112
+ * to a specific agent would be a telemetry lie. */
113
+ kind: 'internal_join_failure';
114
+ reason: string;
115
+ };
116
+ /**
117
+ * Class wrapper around a `ReviewerError` so callers can `throw` typed
118
+ * errors and `try { ... } catch (e) { if (e instanceof ReviewerErrorClass) {
119
+ * /* type-narrow on e.detail.kind *\/ } }` from JS-idiom code paths.
120
+ *
121
+ * The `.detail` property carries the discriminated union; `Error.message`
122
+ * is a human-readable formatting matching Rust's `#[error(...)]` strings.
123
+ */
124
+ export declare class ReviewerErrorClass extends Error {
125
+ /** The structured error detail. Use `.kind` to narrow. */
126
+ readonly detail: ReviewerError;
127
+ constructor(detail: ReviewerError);
128
+ }
129
+ /**
130
+ * Engine's contract with reviewer implementations.
131
+ *
132
+ * **Engine owns lifecycle:** implementations must spawn, await verdict,
133
+ * enforce timeout, cancel, and capture logs internally. The engine calls
134
+ * `evaluate` and awaits — it never holds a handle that could outlive the
135
+ * call.
136
+ *
137
+ * Errors are thrown as `ReviewerErrorClass` instances; callers narrow via
138
+ * the `.detail.kind` discriminator.
139
+ */
140
+ export interface ReviewerProvider {
141
+ /**
142
+ * Spawn one reviewer per the spec, collect its verdict, enforce timeout.
143
+ *
144
+ * @param spec - the reviewer to spawn (seat_id + role + agent + prompt + timeout)
145
+ * @param gateId - the `ReviewGate` UUID this verdict attaches to. Stored
146
+ * on the returned `ReviewerVerdict.gate_id`.
147
+ * @returns the parsed `ReviewerVerdict` on success.
148
+ * @throws `ReviewerErrorClass` on timeout, spawn failure, parse failure,
149
+ * cancellation, or internal join failure. Use `e.detail.kind` to
150
+ * narrow.
151
+ */
152
+ evaluate(spec: ReviewerSpec, gateId: string): Promise<ReviewerVerdict>;
153
+ }