@quantiya/codevibe-core 2.0.0 → 2.0.1

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 (170) hide show
  1. package/dist/__tests__/cp-5-baseline-invariants.test.d.ts +1 -0
  2. package/dist/adapter/__tests__/capabilities.test.d.ts +1 -0
  3. package/dist/adapter/__tests__/contract-conformance.test.d.ts +1 -0
  4. package/dist/adapter/__tests__/packets.test.d.ts +1 -0
  5. package/dist/adapter/__tests__/progress.test.d.ts +1 -0
  6. package/dist/adapter/__tests__/registry.test.d.ts +1 -0
  7. package/dist/adapter/__tests__/smoke/claude.smoke.test.d.ts +1 -0
  8. package/dist/adapter/__tests__/smoke/codex.smoke.test.d.ts +1 -0
  9. package/dist/adapter/__tests__/smoke/gemini.smoke.test.d.ts +1 -0
  10. package/dist/adapter/capabilities.d.ts +68 -0
  11. package/dist/adapter/index.d.ts +7 -0
  12. package/dist/adapter/packets.d.ts +129 -0
  13. package/dist/adapter/progress.d.ts +93 -0
  14. package/dist/adapter/registry.d.ts +24 -0
  15. package/dist/adapter/types.d.ts +22 -0
  16. package/dist/appsync/__tests__/appsync-client-apply-user-decision.test.d.ts +1 -0
  17. package/dist/appsync/__tests__/appsync-client-classb.test.d.ts +1 -0
  18. package/dist/appsync/__tests__/appsync-client-planner.test.d.ts +1 -0
  19. package/dist/appsync/__tests__/appsync-client.test.d.ts +1 -0
  20. package/dist/appsync/appsync-client.d.ts +412 -0
  21. package/dist/appsync/index.d.ts +1 -1
  22. package/dist/appsync/queries.d.ts +15 -0
  23. package/dist/auth/__tests__/auth-telemetry.test.d.ts +1 -0
  24. package/dist/auth/auth-telemetry.d.ts +98 -5
  25. package/dist/companion-mode/__tests__/persist-preference.test.d.ts +1 -0
  26. package/dist/companion-mode/__tests__/resolve-agent.test.d.ts +1 -0
  27. package/dist/companion-mode/agent-picker.d.ts +9 -0
  28. package/dist/companion-mode/index.d.ts +55 -0
  29. package/dist/companion-mode/persist-preference.d.ts +24 -0
  30. package/dist/companion-mode/resolve-agent.d.ts +41 -0
  31. package/dist/index.d.ts +11 -3
  32. package/dist/index.js +243 -42
  33. package/dist/keychain/keychain-manager.d.ts +16 -2
  34. package/dist/local-executor/__tests__/authority-symlink-fixture.d.ts +15 -0
  35. package/dist/local-executor/__tests__/authority.test.d.ts +1 -0
  36. package/dist/local-executor/__tests__/class-a-emit.test.d.ts +1 -0
  37. package/dist/local-executor/__tests__/class-b-consumer.integration.test.d.ts +1 -0
  38. package/dist/local-executor/__tests__/class-b-consumer.test.d.ts +1 -0
  39. package/dist/local-executor/__tests__/hook-bridge.test.d.ts +1 -0
  40. package/dist/local-executor/__tests__/local-executor.integration.test.d.ts +1 -0
  41. package/dist/local-executor/__tests__/spawn.test.d.ts +1 -0
  42. package/dist/local-executor/__tests__/verification-runner.test.d.ts +1 -0
  43. package/dist/local-executor/authority.d.ts +29 -0
  44. package/dist/local-executor/class-a-emit.d.ts +138 -0
  45. package/dist/local-executor/class-b-consumer.d.ts +121 -0
  46. package/dist/local-executor/hook-bridge.d.ts +36 -0
  47. package/dist/local-executor/index.d.ts +8 -0
  48. package/dist/local-executor/local-executor-impl.d.ts +83 -0
  49. package/dist/local-executor/spawn.d.ts +6 -0
  50. package/dist/local-executor/types.d.ts +183 -0
  51. package/dist/local-executor/verification-gates/build.d.ts +6 -0
  52. package/dist/local-executor/verification-gates/deploy-preflight.d.ts +6 -0
  53. package/dist/local-executor/verification-gates/diff-sanity.d.ts +6 -0
  54. package/dist/local-executor/verification-gates/hostile-grep.d.ts +6 -0
  55. package/dist/local-executor/verification-gates/lint.d.ts +6 -0
  56. package/dist/local-executor/verification-gates/shell-runner.d.ts +40 -0
  57. package/dist/local-executor/verification-gates/source-traceability.d.ts +6 -0
  58. package/dist/local-executor/verification-gates/tests.d.ts +6 -0
  59. package/dist/local-executor/verification-gates/typecheck.d.ts +6 -0
  60. package/dist/local-executor/verification-runner.d.ts +28 -0
  61. package/dist/orchestration/__tests__/setup-bootstrap.test.d.ts +1 -0
  62. package/dist/orchestration/__tests__/setup-failure-recourse.test.d.ts +1 -0
  63. package/dist/orchestration/__tests__/setup-save.test.d.ts +1 -0
  64. package/dist/orchestration/__tests__/setup-seat-picker.test.d.ts +1 -0
  65. package/dist/orchestration/__tests__/setup-telemetry.test.d.ts +1 -0
  66. package/dist/orchestration/__tests__/setup-test-agents.test.d.ts +1 -0
  67. package/dist/orchestration/__tests__/setup-types.test.d.ts +1 -0
  68. package/dist/orchestration/__tests__/setup-wizard.test.d.ts +1 -0
  69. package/dist/orchestration/__tests__/v1-options.test.d.ts +1 -0
  70. package/dist/orchestration/detect-agents.d.ts +2 -1
  71. package/dist/orchestration/index.d.ts +1 -0
  72. package/dist/orchestration/orchestration-cli.d.ts +4 -1
  73. package/dist/orchestration/setup-bootstrap.d.ts +146 -0
  74. package/dist/orchestration/setup-failure-recourse.d.ts +23 -0
  75. package/dist/orchestration/setup-save.d.ts +47 -0
  76. package/dist/orchestration/setup-seat-picker.d.ts +72 -0
  77. package/dist/orchestration/setup-telemetry.d.ts +54 -0
  78. package/dist/orchestration/setup-test-agents.d.ts +108 -0
  79. package/dist/orchestration/setup-types.d.ts +140 -0
  80. package/dist/orchestration/setup-wizard.d.ts +57 -0
  81. package/dist/orchestration/v1-options.d.ts +97 -0
  82. package/dist/orchestration-shell/__tests__/cli-authority-bridge.test.d.ts +1 -0
  83. package/dist/orchestration-shell/__tests__/cli-planner-stack.test.d.ts +1 -0
  84. package/dist/orchestration-shell/__tests__/cli-singleton-enforcement.test.d.ts +1 -0
  85. package/dist/orchestration-shell/__tests__/cli-stub-session-adapter.test.d.ts +1 -0
  86. package/dist/orchestration-shell/__tests__/components.test.d.ts +1 -0
  87. package/dist/orchestration-shell/__tests__/emit-shell-event.test.d.ts +1 -0
  88. package/dist/orchestration-shell/__tests__/gate-prompts.test.d.ts +1 -0
  89. package/dist/orchestration-shell/__tests__/hostile-grep.test.d.ts +1 -0
  90. package/dist/orchestration-shell/__tests__/mode-selection.test.d.ts +1 -0
  91. package/dist/orchestration-shell/__tests__/process-markers.test.d.ts +1 -0
  92. package/dist/orchestration-shell/__tests__/reducer.test.d.ts +1 -0
  93. package/dist/orchestration-shell/__tests__/runOrchestrationShell-classify-dispatch.test.d.ts +1 -0
  94. package/dist/orchestration-shell/__tests__/runOrchestrationShell-planner-wiring.test.d.ts +1 -0
  95. package/dist/orchestration-shell/__tests__/runOrchestrationShell-signal.test.d.ts +1 -0
  96. package/dist/orchestration-shell/__tests__/runOrchestrationShell.test.d.ts +1 -0
  97. package/dist/orchestration-shell/__tests__/slash-router.test.d.ts +1 -0
  98. package/dist/orchestration-shell/__tests__/sticky-preference.test.d.ts +1 -0
  99. package/dist/orchestration-shell/cli.d.ts +96 -0
  100. package/dist/orchestration-shell/cli.js +8309 -0
  101. package/dist/orchestration-shell/cohort-flag.d.ts +16 -0
  102. package/dist/orchestration-shell/components/CodeVibeLogo.d.ts +2 -0
  103. package/dist/orchestration-shell/components/ConversationPane.d.ts +7 -0
  104. package/dist/orchestration-shell/components/GatePromptEntry.d.ts +9 -0
  105. package/dist/orchestration-shell/components/InputBar.d.ts +41 -0
  106. package/dist/orchestration-shell/components/OrchestrationApp.d.ts +63 -0
  107. package/dist/orchestration-shell/components/StatusBar.d.ts +7 -0
  108. package/dist/orchestration-shell/components/nodes/AdvisoryEntry.d.ts +8 -0
  109. package/dist/orchestration-shell/components/nodes/GateStatusNode.d.ts +8 -0
  110. package/dist/orchestration-shell/components/nodes/PlannerDecisionEntry.d.ts +8 -0
  111. package/dist/orchestration-shell/components/nodes/ReviewerQuorumStatusNode.d.ts +8 -0
  112. package/dist/orchestration-shell/components/nodes/SlashOutputEntry.d.ts +8 -0
  113. package/dist/orchestration-shell/components/nodes/SubagentEventEntry.d.ts +8 -0
  114. package/dist/orchestration-shell/components/nodes/UserMessageEntry.d.ts +8 -0
  115. package/dist/orchestration-shell/emit-shell-event.d.ts +64 -0
  116. package/dist/orchestration-shell/gate-prompts.d.ts +123 -0
  117. package/dist/orchestration-shell/index.d.ts +100 -0
  118. package/dist/orchestration-shell/ink-runtime.d.ts +64 -0
  119. package/dist/orchestration-shell/mode-selection.d.ts +46 -0
  120. package/dist/orchestration-shell/non-tty-fallback.d.ts +46 -0
  121. package/dist/orchestration-shell/process-markers.d.ts +12 -0
  122. package/dist/orchestration-shell/reducer.d.ts +8 -0
  123. package/dist/orchestration-shell/slash-router.d.ts +45 -0
  124. package/dist/orchestration-shell/sticky-preference.d.ts +24 -0
  125. package/dist/orchestration-shell/store.d.ts +17 -0
  126. package/dist/orchestration-shell/types.d.ts +417 -0
  127. package/dist/planner/__tests__/cache-clarification-bypass.test.d.ts +1 -0
  128. package/dist/planner/__tests__/cache.test.d.ts +1 -0
  129. package/dist/planner/__tests__/client.test.d.ts +1 -0
  130. package/dist/planner/__tests__/health-machine-transitions.test.d.ts +1 -0
  131. package/dist/planner/__tests__/types-zod.test.d.ts +1 -0
  132. package/dist/planner/adapter.d.ts +16 -0
  133. package/dist/planner/cache.d.ts +35 -0
  134. package/dist/planner/client.d.ts +103 -0
  135. package/dist/planner/health-state.d.ts +24 -0
  136. package/dist/planner/index.d.ts +5 -0
  137. package/dist/planner/types.d.ts +113 -0
  138. package/dist/session/__tests__/session-resume-service-keys.test.d.ts +1 -0
  139. package/dist/session/session-rekey.d.ts +40 -0
  140. package/dist/session/session-resume.d.ts +25 -0
  141. package/dist/structural-summary/__tests__/__fixtures__/fixture-helpers.d.ts +11 -0
  142. package/dist/structural-summary/__tests__/assembler.test.d.ts +1 -0
  143. package/dist/structural-summary/__tests__/generator.test.d.ts +1 -0
  144. package/dist/structural-summary/__tests__/language-detect.test.d.ts +1 -0
  145. package/dist/structural-summary/__tests__/manifest-parsers/cargo.test.d.ts +1 -0
  146. package/dist/structural-summary/__tests__/manifest-parsers/gomod.test.d.ts +1 -0
  147. package/dist/structural-summary/__tests__/manifest-parsers/gradle.test.d.ts +1 -0
  148. package/dist/structural-summary/__tests__/manifest-parsers/index.test.d.ts +1 -0
  149. package/dist/structural-summary/__tests__/manifest-parsers/npm.test.d.ts +1 -0
  150. package/dist/structural-summary/__tests__/manifest-parsers/podfile.test.d.ts +1 -0
  151. package/dist/structural-summary/__tests__/manifest-parsers/pyproject.test.d.ts +1 -0
  152. package/dist/structural-summary/__tests__/opt-in-store.test.d.ts +1 -0
  153. package/dist/structural-summary/__tests__/privacy-filter.test.d.ts +1 -0
  154. package/dist/structural-summary/__tests__/safe-file-read.test.d.ts +1 -0
  155. package/dist/structural-summary/__tests__/user-ignore-matcher.test.d.ts +1 -0
  156. package/dist/structural-summary/__tests__/walker.test.d.ts +1 -0
  157. package/dist/structural-summary/generator.d.ts +8 -0
  158. package/dist/structural-summary/index.d.ts +7 -0
  159. package/dist/structural-summary/manifest-parsers/cargo.d.ts +3 -0
  160. package/dist/structural-summary/manifest-parsers/index.d.ts +7 -0
  161. package/dist/structural-summary/manifest-parsers/npm.d.ts +3 -0
  162. package/dist/structural-summary/manifest-parsers/other.d.ts +17 -0
  163. package/dist/structural-summary/opt-in-store.d.ts +24 -0
  164. package/dist/structural-summary/privacy-filter.d.ts +110 -0
  165. package/dist/structural-summary/safe-file-read.d.ts +11 -0
  166. package/dist/structural-summary/types.d.ts +215 -0
  167. package/dist/structural-summary/user-ignore-matcher.d.ts +9 -0
  168. package/dist/structural-summary/walker.d.ts +20 -0
  169. package/dist/types/events.d.ts +17 -2
  170. package/package.json +17 -3
@@ -1,4 +1,5 @@
1
1
  import { CreateEventInput, CreateSessionInput, UpdateSessionInput, UpdateEventStatusInput, Event, Session, EventSource, DeviceKey, GrantSessionKeyInput, UpdateReviewerPolicyInput, UserReviewerPolicySnapshot } from '../types';
2
+ import type { PostDecisionAction } from '../orchestration-shell/types';
2
3
  /**
3
4
  * Download URL response
4
5
  */
@@ -6,6 +7,47 @@ export interface DownloadUrlResponse {
6
7
  downloadUrl: string;
7
8
  expiresAt: string;
8
9
  }
10
+ /**
11
+ * CP-1.b IMPL r2 M-4 — Typed GraphQL error that preserves AppSync's
12
+ * `errorType` and `extensions` fields, alongside the human-readable
13
+ * `message`. Stage 2 r1's `graphqlRequest()` threw a plain
14
+ * `Error("GraphQL error: <message>")` and silently dropped
15
+ * `errorType`, which broke `BackendPlannerClient.classifyTransportError`
16
+ * (it discriminates on `err.errorType` to separate
17
+ * `BudgetExceeded` / `TierGateRejected` from provider-health failures).
18
+ *
19
+ * Callers may catch this class to inspect `errorType` directly. Code
20
+ * paths that still parse error.message will continue to work because
21
+ * the message is unchanged ("GraphQL error: <message>").
22
+ */
23
+ export declare class AppSyncGraphQLError extends Error {
24
+ readonly errorType?: string;
25
+ readonly extensions?: unknown;
26
+ readonly path?: unknown;
27
+ constructor(opts: {
28
+ message: string;
29
+ errorType?: string;
30
+ extensions?: unknown;
31
+ path?: unknown;
32
+ });
33
+ }
34
+ /**
35
+ * Discriminator for the most recent `authenticateWithStoredTokens()`
36
+ * failure. Lets callers distinguish a genuine "no tokens / refresh
37
+ * rejected" outcome from a transient network failure during the
38
+ * Cognito refresh-token POST.
39
+ *
40
+ * Stage 2 round-1 Codex M1: the production refresh path returns
41
+ * `false` on every error (including network blow-ups inside
42
+ * `callCognitoRefresh`'s catch block), so the wizard's auth-vs-network
43
+ * heuristic — which only ran on caught throws — never fired in
44
+ * production. Recording the kind on every false-return path lets
45
+ * `setup-bootstrap.ts:defaultClientFactory()` route network-shaped
46
+ * refresh failures to `subscription_status_network` (per §6 row
47
+ * "transient 5xx during refresh") instead of misleading the user with
48
+ * a "not signed in" abort.
49
+ */
50
+ export type AuthFailureKind = 'no_tokens' | 'refresh_auth_rejected' | 'refresh_network';
9
51
  /**
10
52
  * AppSync GraphQL client with WebSocket subscriptions
11
53
  */
@@ -15,7 +57,29 @@ export declare class AppSyncClient {
15
57
  private currentEmail;
16
58
  private tokens;
17
59
  private activeSubscriptions;
60
+ /**
61
+ * Set by `authenticateWithStoredTokens()` on every false-return path
62
+ * (and reset to `null` on success). Read by callers (e.g., the
63
+ * wizard's `defaultClientFactory`) to discriminate auth-rejection
64
+ * vs network-failure without re-running the auth call. Stage 2
65
+ * round-1 Codex M1.
66
+ */
67
+ private lastAuthFailureKind;
68
+ /**
69
+ * Sentinel set inside `performRefresh` / `callCognitoRefresh` when
70
+ * the refresh round-trip fails with a network-shaped error (DNS,
71
+ * socket reset, fetch failed, 5xx). Reset to `false` at the start
72
+ * of each `performRefresh`. Read by `authenticateWithStoredTokens`
73
+ * to classify a `refreshTokens()=false` return as
74
+ * `refresh_network` vs `refresh_auth_rejected`. Internal — never
75
+ * exposed.
76
+ */
77
+ private lastRefreshNetworkError;
78
+ private pendingRefresh;
79
+ private lastRefreshFailureAt;
80
+ private static readonly REFRESH_BACKOFF_MS;
18
81
  private deviceKeyWatcher;
82
+ private sessionUpdateWatchers;
19
83
  private environment;
20
84
  constructor();
21
85
  /**
@@ -26,6 +90,30 @@ export declare class AppSyncClient {
26
90
  * Get the current authenticated user email
27
91
  */
28
92
  getCurrentUserEmail(): string | null;
93
+ /**
94
+ * Returns the kind of the most recent
95
+ * `authenticateWithStoredTokens()` failure, or `null` if the call
96
+ * succeeded (or has never been called).
97
+ *
98
+ * Stage 2 round-1 Codex M1. Callers (today: `setup-bootstrap.ts
99
+ * :defaultClientFactory`) use this to distinguish network blow-ups
100
+ * during the Cognito refresh-token POST from genuine auth
101
+ * rejections. The wizard maps `'refresh_network'` to
102
+ * `subscription_status_network` (don't tell a signed-in user to
103
+ * re-login when their tokens are valid and the network is broken)
104
+ * and the other two kinds to `not_signed_in` (preserves
105
+ * pre-Codex-M1 behavior).
106
+ */
107
+ getLastAuthFailureKind(): AuthFailureKind | null;
108
+ /**
109
+ * Heuristic — does the error message look like a transient network
110
+ * failure rather than an auth-token rejection? Mirrors
111
+ * `setup-bootstrap.ts:isNetworkLikeError` byte-for-byte so the same
112
+ * classifier runs both inside the client (for refresh-path
113
+ * discrimination) and at the bootstrap boundary (for caught-throw
114
+ * routing). Stage 2 round-1 Codex M1.
115
+ */
116
+ private static isNetworkLikeMessage;
29
117
  /**
30
118
  * Authenticate using stored OAuth tokens from keychain
31
119
  */
@@ -34,6 +122,47 @@ export declare class AppSyncClient {
34
122
  * Refresh expired tokens
35
123
  */
36
124
  private refreshTokens;
125
+ /**
126
+ * Do the work of refreshing tokens. Tries the caller-supplied tokens
127
+ * first; on failure, re-reads from storage and retries once with a
128
+ * potentially-fresher refresh token. This is the self-healing path
129
+ * that lets `codevibe login` (which writes new tokens to the
130
+ * keychain) recover running daemons without requiring a restart:
131
+ * the in-memory copy the daemon cached at boot may be invalid, but
132
+ * whatever the user just wrote from the login flow is still valid.
133
+ *
134
+ * Splitting the two attempts into a pure-network helper keeps the
135
+ * orchestration readable without duplicating the fetch + body-shape
136
+ * plumbing.
137
+ */
138
+ private performRefresh;
139
+ /**
140
+ * POST to Cognito's /oauth2/token with a refresh_token grant.
141
+ * Returns the parsed body on 200, or null on any failure (network
142
+ * error, non-2xx response, JSON parse failure). Caller decides how
143
+ * to proceed — this helper is side-effect-free beyond logging.
144
+ */
145
+ private callCognitoRefresh;
146
+ /**
147
+ * Apply a successful refresh response: update in-memory cache first,
148
+ * clear the backoff sentinel, then persist to storage. Success here
149
+ * is defined as "the process has usable fresh tokens in memory" —
150
+ * storage persistence is degraded-success, not a failure mode.
151
+ *
152
+ * Ordering matters. The API call to Cognito already succeeded, which
153
+ * means we hold valid access/id tokens right now. If we delayed the
154
+ * in-memory update until after persistence and the keychain write
155
+ * threw (keychain locked, disk full, file-backend permission error),
156
+ * we'd be stuck with stale-and-known-dead tokens in memory while
157
+ * holding valid fresh tokens in local scope that vanish at the end
158
+ * of this function. That would re-break both guarantees this hotfix
159
+ * makes: no-restart recovery becomes "restart required to escape
160
+ * the keychain-lock window," and backoff stays unarmed so the
161
+ * caller hot-loops against a working Cognito endpoint — R1's MEDIUM
162
+ * on round 1 of this review. Persistence-failure is loud-logged
163
+ * so operators can see degraded durability without losing availability.
164
+ */
165
+ private applyRefreshedTokens;
37
166
  /**
38
167
  * Check if authenticated
39
168
  */
@@ -70,10 +199,69 @@ export declare class AppSyncClient {
70
199
  * List events for a session
71
200
  */
72
201
  listEvents(sessionId: string, source?: EventSource, limit?: number): Promise<Event[]>;
202
+ /**
203
+ * List the authenticated user's sessions. Paginates automatically
204
+ * via nextToken so callers always get the complete set.
205
+ */
206
+ listSessions(limit?: number): Promise<Array<{
207
+ sessionId: string;
208
+ agentType: string;
209
+ status: string;
210
+ lastHeartbeatAt: string | null;
211
+ }>>;
212
+ /**
213
+ * Mark stale ACTIVE sessions of a given agentType INACTIVE so they
214
+ * stop appearing in the mobile app's session list. Called at daemon
215
+ * startup to clean up after daemons that died without running their
216
+ * graceful shutdown (crash, auth-loop death, force-kill, power loss).
217
+ *
218
+ * Staleness rule: lastHeartbeatAt is older than `staleThresholdMs`
219
+ * (default 15 min — a conservative ~7.5× the 2-min heartbeat
220
+ * interval, giving legitimately-active daemons on other machines
221
+ * ample margin before we consider their session abandoned).
222
+ *
223
+ * Safety:
224
+ * - Only sessions with status === 'ACTIVE' are candidates.
225
+ * - Sessions explicitly listed in `excludeSessionIds` are skipped
226
+ * (caller can pass the session the daemon is about to attach to
227
+ * if the ID is known before the sweep).
228
+ * - Absent `lastHeartbeatAt` (never-heartbeated sessions — should
229
+ * only happen for rows created within the last few seconds)
230
+ * treats the session as fresh and skips.
231
+ * - updateSession failures are logged as warnings and don't abort
232
+ * the sweep — best-effort cleanup.
233
+ *
234
+ * Returns the number of sessions actually marked INACTIVE.
235
+ */
236
+ sweepOrphanSessions(opts: {
237
+ agentType: string;
238
+ staleThresholdMs?: number;
239
+ excludeSessionIds?: string[];
240
+ }): Promise<number>;
73
241
  /**
74
242
  * List user device keys
75
243
  */
76
244
  listUserDeviceKeys(): Promise<DeviceKey[]>;
245
+ /**
246
+ * CP-1.b IMPL r4 H-2 — list BACKEND_SERVICE device keys per design
247
+ * §4.9 schema delta bullet 5 + §6.1 service-device flow.
248
+ *
249
+ * Returns every device row with `kind = BACKEND_SERVICE` regardless of
250
+ * the caller's `userId` (the only such row in CP-1.b is the
251
+ * planner-proxy's ECDH P-256 keypair under `deviceId =
252
+ * planner-proxy-{env}`). Callers union this list with
253
+ * `listUserDeviceKeys()` when minting `encryptedKeys[]` for new
254
+ * sessions so the planner-proxy Lambda can decrypt session-bound
255
+ * payloads via ECDH+HKDF against its own private key.
256
+ *
257
+ * The backend resolver is auth-mode widened (`@aws_cognito_user_pools
258
+ * @aws_iam`) — Cognito callers query for the planner-proxy public key
259
+ * at session-creation time; IAM callers (planner-proxy bootstrap) may
260
+ * also call. The CDK-backed GSI `kind-deviceId-index` partitions the
261
+ * device-keys table by `kind`, so this is a single GSI query, not a
262
+ * full scan.
263
+ */
264
+ listServiceDeviceKeys(): Promise<DeviceKey[]>;
77
265
  /**
78
266
  * Register device key
79
267
  */
@@ -101,6 +289,18 @@ export declare class AppSyncClient {
101
289
  * derive tier-default reviewer seat assignments.
102
290
  */
103
291
  updateAvailableAgents(agents: Array<'CLAUDE' | 'GEMINI' | 'CODEX'>): Promise<UserReviewerPolicySnapshot>;
292
+ /**
293
+ * CP-5.c — push the per-agent capability snapshot. Dual-write
294
+ * alongside `updateAvailableAgents` per LOCK #C5-C-4 (the legacy
295
+ * field stays for V1 client backward compat; this surface carries
296
+ * the full 9-field record per AgentKind).
297
+ *
298
+ * `capabilities` is serialized to JSON before transit (AppSync
299
+ * accepts `AWSJSON` as a stringified payload). Backend Lambda
300
+ * persists to `Users.adapterCapabilities` and returns the updated
301
+ * User row.
302
+ */
303
+ updateAdapterCapabilities(capabilities: unknown[]): Promise<UserReviewerPolicySnapshot>;
104
304
  /**
105
305
  * Persist the user's orchestration opt-in default and/or custom
106
306
  * reviewer panel. Backend validates seat-count against tier, seat_id
@@ -109,12 +309,140 @@ export declare class AppSyncClient {
109
309
  * configure-reviewers wizard).
110
310
  */
111
311
  updateReviewerPolicy(input: UpdateReviewerPolicyInput): Promise<UserReviewerPolicySnapshot>;
312
+ /**
313
+ * Fetch the user's subscription tier + status. Used by the Phase 3.a
314
+ * setup wizard (#190) at bootstrap to gate Free → upgrade interstitial
315
+ * and to size the seat budget (Pro=2, Max=3).
316
+ *
317
+ * Backend resolver returns a default FREE row when the user has no
318
+ * Users-table entry yet (Lambda resolver — lambda/subscription/index.ts).
319
+ * Network failure / auth expiry surface as graphqlRequest exceptions.
320
+ */
321
+ getSubscriptionStatus(): Promise<{
322
+ tier: 'FREE' | 'PRO' | 'MAX';
323
+ status: 'ACTIVE' | 'EXPIRED' | 'GRACE_PERIOD' | 'BILLING_RETRY';
324
+ expiresAt: string | null;
325
+ }>;
326
+ /**
327
+ * CP-1.b IMPL r4 H-1 — `classifyPlannerPrompt` AppSync mutation.
328
+ *
329
+ * Implements `PlannerAppSyncTransport.classifyPlannerPrompt` from
330
+ * `codevibe-core/src/planner/client.ts`. Encrypted-input wire shape
331
+ * is locked at design §4.10 + §4.11: `prompt` is raw base64 AES-GCM
332
+ * ciphertext under the session key; `clarifications`, `sessionContext`,
333
+ * `budgetHint` are AWSJSON envelope strings of the form
334
+ * `'{"encrypted":"<base64 ciphertext>"}'`. Response `decision` is RAW
335
+ * base64 ciphertext (NOT envelope-wrapped) per LOCK #27.
336
+ *
337
+ * Typed errors (`BudgetExceeded`, `TierGateRejected`, etc.) surface
338
+ * via `AppSyncGraphQLError.errorType` so the planner client can
339
+ * discriminate policy events from provider-health failures per
340
+ * Stage 2 r2 M-3.
341
+ */
342
+ classifyPlannerPrompt(input: {
343
+ sessionId: string;
344
+ prompt: string;
345
+ clarifications: string;
346
+ sessionContext: string;
347
+ budgetHint: string;
348
+ }): Promise<{
349
+ decision: string;
350
+ cacheHit: boolean;
351
+ cacheKind: string | null;
352
+ serverLatencyMs: number;
353
+ providerUsed: string;
354
+ }>;
355
+ /**
356
+ * CP-1.b IMPL r4 H-1 — `pingPlanner` AppSync mutation.
357
+ *
358
+ * Implements `PlannerAppSyncTransport.pingPlanner`. Separate mutation
359
+ * from `classifyPlannerPrompt` per Stage A LOCK #31. Input carries
360
+ * only `sessionId` (no encryption, no clarifications, no budgetHint).
361
+ * Response shape `{ ok, ms }` ONLY (no `providerHealthState` —
362
+ * shell-side `PlannerHealthMachine` is the sole authority).
363
+ */
364
+ pingPlanner(input: {
365
+ sessionId: string;
366
+ }): Promise<{
367
+ ok: boolean;
368
+ ms: number;
369
+ }>;
370
+ /**
371
+ * CP-8 min Stage 1 R1 HIGH-1 closure (Hendry-approved 2026-05-19).
372
+ *
373
+ * Submits an orchestration gate-prompt decision (Path A FinalApproval
374
+ * or Path B Halted-resolution) to the engine and returns the typed
375
+ * `PostDecisionAction` reply.
376
+ *
377
+ * **Implementation deviation from `PHASE-CP-8-MIN-DESIGN.md` #C8M-6
378
+ * literal wording:** the design says "use `@quantiya/quorum-core`'s
379
+ * `applyUserDecision` SDK method." codevibe-core deliberately does
380
+ * NOT depend on `@quantiya/quorum-core` at runtime — see the
381
+ * structural-typing pattern at `src/orchestration/v1-options.ts:44-47`.
382
+ * The locked INVARIANT of #C8M-6 ("single typed transport surface,
383
+ * no direct GraphQL in the orchestration-shell tree") is honored by
384
+ * exposing this method here on codevibe-core's own AppSyncClient,
385
+ * which is THE desktop-side transport surface. Shape mirrors the
386
+ * quorum-core SDK at `client.ts:464-504` byte-for-byte:
387
+ * - Wraps `notes` (when present) in `EncryptedPayloadInput`
388
+ * (AES-256-GCM under the session key, base64 ciphertext +
389
+ * `sessionId` + `keyVersion`).
390
+ * - Uppercases `decision` to UPPER_SNAKE wire enum (matches the
391
+ * SDK's `toWireEnum` pattern).
392
+ * - Parses the `payload` AWSJSON string to the 4-variant
393
+ * `PostDecisionAction` discriminated union.
394
+ *
395
+ * See `PHASE-CP-8-MIN-DESIGN.md` §4 #C8M-6 deviation footer for the
396
+ * sealed rationale.
397
+ *
398
+ * @param input Caller passes the lowercase TS `decision` kind (one of
399
+ * the 7 `UserDecisionKind` values). Encoding to wire
400
+ * UPPER_SNAKE happens here.
401
+ * @param sessionKeyBase64 Optional. REQUIRED only when `input.notes`
402
+ * is provided — used to AES-GCM-encrypt the notes plaintext
403
+ * under the session key. When `notes` is undefined, this
404
+ * parameter is ignored.
405
+ * @returns Parsed `{ decision, postAction }` per the engine's reply.
406
+ * `decision` is echoed in lowercase TS form for caller
407
+ * ergonomics (e.g. dispatcher routing without re-mapping the
408
+ * UPPER_SNAKE wire value).
409
+ */
410
+ applyUserDecision(input: {
411
+ gateId: string;
412
+ taskId: string;
413
+ sessionId: string;
414
+ currentRound: number;
415
+ decision: string;
416
+ notes?: string;
417
+ }, sessionKeyBase64?: string): Promise<{
418
+ decision: string;
419
+ postAction: PostDecisionAction;
420
+ }>;
112
421
  /**
113
422
  * Subscribe to events for a session
114
423
  */
115
424
  subscribeToEvents(sessionId: string, onEvent: (event: Event) => void, onError?: (error: Error) => void): () => void;
116
425
  /**
117
426
  * Build WebSocket URL
427
+ *
428
+ * AppSync exposes the realtime endpoint two different ways depending on
429
+ * the GraphQL endpoint shape:
430
+ *
431
+ * 1. Direct AppSync URL (`<id>.appsync-api.<region>.amazonaws.com`):
432
+ * realtime is on a sibling subdomain — swap `appsync-api` →
433
+ * `appsync-realtime-api` and keep the `/graphql` path.
434
+ * → wss://<id>.appsync-realtime-api.<region>.amazonaws.com/graphql
435
+ *
436
+ * 2. Custom domain (`api.codevibe.quantiya.ai` and friends):
437
+ * realtime is path-based on the SAME hostname — append `/realtime`
438
+ * to the GraphQL path.
439
+ * → wss://api.codevibe.quantiya.ai/graphql/realtime
440
+ * AWS docs:
441
+ * https://docs.aws.amazon.com/appsync/latest/devguide/custom-domain-name.html
442
+ *
443
+ * The naive `.replace('appsync-api', 'appsync-realtime-api')` only works
444
+ * for path 1; on a custom-domain URL it doesn't match anything and the
445
+ * resulting `wss://<custom>/graphql` URL never completes the handshake.
118
446
  */
119
447
  private buildRealtimeUrl;
120
448
  /**
@@ -153,6 +481,53 @@ export declare class AppSyncClient {
153
481
  private sendDeviceKeyWatcherStart;
154
482
  private resetDeviceKeyWatcherKeepAlive;
155
483
  private handleDeviceKeyWatcherError;
484
+ /**
485
+ * Subscribe to status changes on our own session and invoke
486
+ * onMobileEndRequested when status transitions to INACTIVE — typically
487
+ * meaning the user tapped "End Session" in the mobile app.
488
+ *
489
+ * The callback is invoked from the WebSocket message handler. The
490
+ * helper catches and logs WARN any error thrown by the callback so it
491
+ * does not propagate to the subscription handler (would tear down the
492
+ * WebSocket).
493
+ *
494
+ * Returns a stop() function the plugin MUST call BEFORE issuing its
495
+ * own updateSession({ status: INACTIVE }) mutation in the per-session
496
+ * cleanup handler — otherwise the desktop's own end-of-session
497
+ * mutation echoes back through this subscription as a self-caused
498
+ * callback. See §D.3.a in the design doc. Calling stop() more than
499
+ * once is idempotent.
500
+ *
501
+ * Replace-on-duplicate-sessionId semantics: calling watchForMobileEnd
502
+ * twice with the same sessionId stops the prior watcher and replaces
503
+ * it with the new one, so at most one watcher is alive per sessionId.
504
+ * Matches subscribeToEvents (above). Plugins should still call once
505
+ * per session lifecycle; the replace path exists to make resume /
506
+ * re-entry safe.
507
+ *
508
+ * Initial state assumed = ACTIVE: AppSync subscriptions are
509
+ * mutation-triggered (NOT current-state snapshots), and plugins only
510
+ * call this for sessions just created or resumed in ACTIVE state. The
511
+ * first delivered status=INACTIVE payload IS the real signal and
512
+ * fires the callback.
513
+ *
514
+ * Idempotent firing: callback fires AT MOST ONCE per helper instance.
515
+ * Subsequent INACTIVE payloads (or any payload after fire) are
516
+ * ignored. Plugin must rebuild the helper for a new session.
517
+ */
518
+ watchForMobileEnd(sessionId: string, onMobileEndRequested: () => Promise<void> | void): {
519
+ stop: () => void;
520
+ };
521
+ private createSessionUpdateWatcherConnection;
522
+ /**
523
+ * Process a delivered onSessionUpdated payload. Decides whether to
524
+ * fire the callback per the locked rules in §A.2 of the design doc.
525
+ */
526
+ private handleSessionUpdatePayload;
527
+ private sendSessionUpdateWatcherStart;
528
+ private resetSessionUpdateWatcherKeepAlive;
529
+ private handleSessionUpdateWatcherError;
530
+ private cleanupSessionUpdateWatcherState;
156
531
  private heartbeatTimers;
157
532
  /**
158
533
  * Start periodic heartbeat for a session.
@@ -171,4 +546,41 @@ export declare class AppSyncClient {
171
546
  * Cleanup all subscriptions and heartbeats
172
547
  */
173
548
  cleanupSubscriptions(): void;
549
+ /**
550
+ * Parse a raw `ClassBPacket.packetJson` AWSJSON string into the typed
551
+ * discriminated-union packet. Throws `Error` on malformed input — the caller
552
+ * (subscription handler in CP-2) bridges this to `onError` per §7.2.
553
+ *
554
+ * Exposed for unit testing per design §7.4 row 1
555
+ * (`appsync_client_subscribeToClassBPackets_parses_packetJson_into_typed_packet`).
556
+ */
557
+ parseClassBPacketPayload(packetJson: string): unknown;
558
+ /**
559
+ * Subscribe to AppSync `onClassBPacket(userId)` and dispatch typed packets
560
+ * to `onPacket`. Errors during parsing OR transport are forwarded to
561
+ * `onError`. Returns an unsubscribe handle.
562
+ *
563
+ * CP-1.e ships the handler surface + parser; full WebSocket wiring on the
564
+ * `subscribeToEvents`-style two-phase reconnect engine completes in CP-2
565
+ * Stage A when the hosted producer side comes online. Until then the method
566
+ * registers the handler and returns a working unsubscribe — callers (the
567
+ * orchestration shell) can wire it during CP-1.e bootstrap with no
568
+ * behavioral change once CP-2 lights up the wire.
569
+ */
570
+ subscribeToClassBPackets(userId: string, handlers: {
571
+ onPacket: (packet: unknown) => void | Promise<void>;
572
+ onError?: (err: Error) => void;
573
+ }): {
574
+ unsubscribe: () => Promise<void>;
575
+ };
576
+ /**
577
+ * Test seam — deliver a raw envelope payload to the registered handler.
578
+ * Used by §7.4 unit tests to assert parse + forward semantics without
579
+ * standing up a real WebSocket. Production code path (CP-2) will call the
580
+ * same internal flow.
581
+ *
582
+ * @internal
583
+ */
584
+ _deliverClassBPacketForTests(userId: string, rawPacketJson: string): Promise<void>;
585
+ private readonly classBPacketHandlers;
174
586
  }
@@ -1,2 +1,2 @@
1
- export { AppSyncClient, DownloadUrlResponse } from './appsync-client';
1
+ export { AppSyncClient, AppSyncGraphQLError, DownloadUrlResponse } from './appsync-client';
2
2
  export { queries, mutations, subscriptions } from './queries';
@@ -2,6 +2,16 @@ export declare const queries: {
2
2
  getSession: string;
3
3
  listEvents: string;
4
4
  listUserDeviceKeys: string;
5
+ listServiceDeviceKeys: string;
6
+ /**
7
+ * Minimal session listing used by the orphan-sweep path. Only the
8
+ * fields needed to decide whether a session row is stale — sessionId
9
+ * for the INACTIVE mutation, agentType for the per-plugin filter,
10
+ * status to skip non-ACTIVE rows, and lastHeartbeatAt for the age
11
+ * check.
12
+ */
13
+ listSessions: string;
14
+ getSubscriptionStatus: string;
5
15
  };
6
16
  export declare const mutations: {
7
17
  createSession: string;
@@ -12,9 +22,14 @@ export declare const mutations: {
12
22
  grantSessionKey: string;
13
23
  getAttachmentDownloadUrl: string;
14
24
  updateAvailableAgents: string;
25
+ updateAdapterCapabilities: string;
15
26
  updateReviewerPolicy: string;
27
+ classifyPlannerPrompt: string;
28
+ pingPlanner: string;
29
+ applyUserDecision: string;
16
30
  };
17
31
  export declare const subscriptions: {
18
32
  onEventCreated: string;
19
33
  onDeviceKeyRegistered: string;
34
+ onSessionUpdated: string;
20
35
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * Typed taxonomy of auth-flow failure reasons.
3
3
  *
4
- * Every value is a sanitized enum literal — never a raw error message,
5
- * never a truncated user input, never any byte from Cognito's response.
6
- * Adding a new failure mode means extending this union; the compiler
7
- * then forces every emit site to either use a known value or produce
8
- * a type error.
4
+ * Every value in THIS union is a sanitized enum literal — never a raw
5
+ * error message, never a truncated user input, never any byte from
6
+ * Cognito's response. Adding a new failure mode means extending this
7
+ * union; the compiler then forces every emit site to either use a known
8
+ * value or produce a type error.
9
+ *
10
+ * (Note: the `error_fragment` parameter on the auth_failed beacon DOES
11
+ * carry redacted error material — see `sanitizeAuthErrorFragment`. The
12
+ * `reason` taxonomy below remains the analytics-safe primary key.)
9
13
  *
10
14
  * Keep values snake_case to match GA4 custom-dimension conventions
11
15
  * used elsewhere in the codebase (`step`, `source`, `reason`).
@@ -29,6 +33,20 @@ export type AuthStage = 'server_start' | 'browser_open' | 'awaiting_callback' |
29
33
  * address.
30
34
  */
31
35
  export declare function fireAuthCompletedBeacon(userId: string): Promise<void>;
36
+ /**
37
+ * Bytes 0-99 of the sanitized fragment (GA4 dim: error_fragment).
38
+ * See `sanitizeAuthErrorFragmentFull` for the redaction contract.
39
+ */
40
+ export declare function sanitizeAuthErrorFragment(msg: string): string;
41
+ /**
42
+ * Bytes 100-199 of the sanitized fragment (GA4 dim: error_fragment_2).
43
+ * Returns empty string when the redacted message is ≤100 chars (the
44
+ * typical short-error case), which is what we want — no
45
+ * `error_fragment_2` param fires unless there's something past byte
46
+ * 100 worth carrying. Works around GA4 Measurement Protocol's 100-char
47
+ * per-param limit.
48
+ */
49
+ export declare function sanitizeAuthErrorFragmentTail(msg: string): string;
32
50
  /**
33
51
  * Fire the `auth_failed` failure beacon with a sanitized `reason`
34
52
  * literal. Optional `httpStatus` captures the numeric HTTP status
@@ -40,10 +58,29 @@ export declare function fireAuthCompletedBeacon(userId: string): Promise<void>;
40
58
  * `reason` is constrained to the `AuthFailureReason` union — this
41
59
  * is the ONLY input path; passing a raw error message is a compile
42
60
  * error.
61
+ *
62
+ * Optional `errorFragment` is a diagnostic dimension reserved for
63
+ * `reason: 'unknown'`. The outer `auth-cli` catch passes the first
64
+ * portion of `error.message` here so the next analytics pass can
65
+ * see what's hiding in `unknown` and we can ship a typed reason in
66
+ * a follow-up. Sanitized inside via `sanitizeAuthErrorFragment` (head)
67
+ * + `sanitizeAuthErrorFragmentTail` (tail) — ANSI stripped, $HOME /
68
+ * USERPROFILE / `/Users/<name>/` / `/home/<name>/` / email substrings
69
+ * redacted (Windows backslashes normalized first so `C:\Users\Alice\…`
70
+ * matches the same path regex as POSIX form), control + non-ASCII
71
+ * bytes dropped, then split into two GA4 dimensions to work around the
72
+ * Measurement Protocol 100-char per-param limit:
73
+ * - `error_fragment` = bytes 0-99
74
+ * - `error_fragment_2` = bytes 100-199 (only set when input >100 chars)
75
+ * Reports concatenate both dims for display. Mirrors the install.sh
76
+ * `sanitize_fragment` / `sanitize_fragment_tail` shell helpers so
77
+ * fragments fired from either path follow the same redaction +
78
+ * slicing contract.
43
79
  */
44
80
  export declare function fireAuthFailedBeacon(reason: AuthFailureReason, extra?: {
45
81
  httpStatus?: number;
46
82
  stage?: AuthStage;
83
+ errorFragment?: string;
47
84
  }): Promise<void>;
48
85
  /**
49
86
  * Attach the reason + beaconed marker to an Error. Non-enumerable so
@@ -54,3 +91,59 @@ export declare function markErrorBeaconed(err: Error, reason: AuthFailureReason)
54
91
  export declare function errorWasBeaconed(err: unknown): boolean;
55
92
  /** Retrieve the tagged reason from a previously-marked error, if any. */
56
93
  export declare function getErrorReason(err: unknown): AuthFailureReason | undefined;
94
+ export type DeviceCountBucket = '0' | '1' | '2-5' | '6+';
95
+ /**
96
+ * Bucket a raw device count into a coarse range to prevent device-count
97
+ * fingerprinting via GA4 client_id stickiness. The `'0'` bucket exists as
98
+ * a safe default for callers that may pass 0; no current emitter actually
99
+ * fires with count 0 in production paths.
100
+ */
101
+ export declare function bucketDeviceCount(count: number): DeviceCountBucket;
102
+ /**
103
+ * SHA-256-truncate a sessionId for cross-beacon correlation without leaking
104
+ * the raw UUID. 8 hex chars = 32 bits ⇒ no practical reverse lookup absent
105
+ * a GA4 + DDB join.
106
+ */
107
+ export declare function hashSessionId(sessionId: string): string;
108
+ /**
109
+ * Bug B per-device: fired inside `createSessionKey`'s per-device catch.
110
+ * `session_hash` is optional because keychain-manager intentionally does
111
+ * not know which session is being keyed — the per-session aggregate in
112
+ * `fireSessionEncryptionPartialSuccessBeacon` provides the correlation.
113
+ */
114
+ export declare function fireSessionEncryptionDeviceSkippedBeacon(params: {
115
+ skipped_count_bucket: DeviceCountBucket;
116
+ session_hash?: string;
117
+ }): Promise<void>;
118
+ /**
119
+ * Bug B per-session: fired once per session creation when at least one
120
+ * device was skipped. Caller MUST gate on `skippedDeviceIds.length > 0`.
121
+ */
122
+ export declare function fireSessionEncryptionPartialSuccessBeacon(params: {
123
+ session_hash: string;
124
+ encrypted_count_bucket: DeviceCountBucket;
125
+ skipped_count_bucket: DeviceCountBucket;
126
+ }): Promise<void>;
127
+ /**
128
+ * Bug A2: fired when `rekeySessionForNewDevices` grants ≥1 device on resume.
129
+ * Caller MUST gate on `grantedCount > 0`.
130
+ */
131
+ export declare function fireSessionEncryptionCatchUpGrantBeacon(params: {
132
+ session_hash: string;
133
+ granted_count_bucket: DeviceCountBucket;
134
+ }): Promise<void>;
135
+ /** Bug A1: fired when we enter the self-rekey path (our deviceId missing). */
136
+ export declare function fireSessionEncryptionSelfRekeyRequestBeacon(params: {
137
+ session_hash: string;
138
+ other_device_count_bucket: DeviceCountBucket;
139
+ }): Promise<void>;
140
+ /** Bug A1: fired when polling resolves with our entry + decrypt succeeds. */
141
+ export declare function fireSessionEncryptionSelfRekeySuccessBeacon(params: {
142
+ session_hash: string;
143
+ attempt_count: number;
144
+ }): Promise<void>;
145
+ /** Bug A1: fired when polling exhausts maxAttempts without our entry. */
146
+ export declare function fireSessionEncryptionSelfRekeyTimeoutBeacon(params: {
147
+ session_hash: string;
148
+ attempt_count: number;
149
+ }): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { DetectedAgent } from './resolve-agent';
2
+ export interface PickAgentArgs {
3
+ healthy: DetectedAgent[];
4
+ defaultIndex: number;
5
+ /** Test seam — override stdin/stdout. */
6
+ input?: NodeJS.ReadableStream;
7
+ output?: NodeJS.WritableStream;
8
+ }
9
+ export declare function pickAgent(args: PickAgentArgs): Promise<DetectedAgent>;