@quantiya/codevibe-claude-plugin 2.0.72 → 2.0.74

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 (34) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +41 -0
  3. package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.d.ts +2 -0
  4. package/node_modules/@quantiya/codevibe-core/dist/appsync/sync-source-cursor.d.ts +3 -0
  5. package/node_modules/@quantiya/codevibe-core/dist/index.d.ts +1 -1
  6. package/node_modules/@quantiya/codevibe-core/dist/index.js +564 -552
  7. package/node_modules/@quantiya/codevibe-core/dist/local-model/ollama-gemma-runner.d.ts +18 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/local-model/ollama.d.ts +26 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/attachments.d.ts +8 -14
  10. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +49 -63
  11. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +2452 -2429
  12. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/OrchestrationApp.d.ts +3 -1
  13. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/git-status-summary.d.ts +5 -10
  14. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +29 -46
  15. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/route-browse.d.ts +4 -4
  16. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/runtime-labels.d.ts +63 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-progress-relay.d.ts +9 -1
  18. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-progress.d.ts +7 -0
  19. package/node_modules/@quantiya/codevibe-core/dist/planner/cache.d.ts +7 -0
  20. package/node_modules/@quantiya/codevibe-core/dist/planner/gemma-errors.d.ts +61 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/planner/gemma-pipeline.d.ts +219 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/planner/gemma-runner.d.ts +66 -0
  23. package/node_modules/@quantiya/codevibe-core/dist/planner/hosted-gemma-runner.d.ts +83 -0
  24. package/node_modules/@quantiya/codevibe-core/dist/planner/index.d.ts +5 -3
  25. package/node_modules/@quantiya/codevibe-core/dist/planner/local-advisory.d.ts +3 -6
  26. package/node_modules/@quantiya/codevibe-core/dist/planner/local-gemma.d.ts +82 -21
  27. package/node_modules/@quantiya/codevibe-core/dist/planner/types.d.ts +4 -4
  28. package/node_modules/@quantiya/codevibe-core/dist/reviewer/verdict-classifier.d.ts +18 -5
  29. package/node_modules/@quantiya/codevibe-core/dist/tool-activity/consolidator.d.ts +3 -0
  30. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  31. package/package.json +2 -2
  32. package/node_modules/@quantiya/codevibe-core/dist/planner/composite-adapter.d.ts +0 -39
  33. package/node_modules/@quantiya/codevibe-core/dist/planner/composite-advisory.d.ts +0 -26
  34. package/node_modules/@quantiya/codevibe-core/dist/planner/hosted-advisory.d.ts +0 -40
@@ -1,3 +1,4 @@
1
+ import type { GemmaCallPipeline } from '../../planner/gemma-pipeline';
1
2
  import * as React from 'react';
2
3
  import { OrchestrationStore } from '../store';
3
4
  import { ConversationEntry, PostDecisionAction, Tier } from '../types';
@@ -38,7 +39,8 @@ export interface OrchestrationAppProps {
38
39
  */
39
40
  plannerRuntimeKind?: PlannerRuntimeKind;
40
41
  plannerLabel?: string;
41
- plannerAdapter?: unknown;
42
+ /** The session's Gemma pipeline; the badge subscribes to its source changes (§3.5). */
43
+ plannerPipeline?: GemmaCallPipeline;
42
44
  /**
43
45
  * TUI redesign (2026-06-29) — the PLAINTEXT cwd for the status bar, threaded
44
46
  * from the shell entry (process.cwd()). Sourced here rather than from
@@ -1,4 +1,4 @@
1
- import { type AdvisoryRunner } from '../planner/local-advisory';
1
+ import type { GemmaCallPipeline } from '../planner/gemma-pipeline';
2
2
  export interface GitExecResult {
3
3
  stdout: string;
4
4
  stderr?: string;
@@ -13,7 +13,8 @@ export interface ExecuteGitStatusSummaryOptions {
13
13
  cwd: string;
14
14
  prompt: string;
15
15
  hasActiveTask: boolean;
16
- advisoryRunner?: AdvisoryRunner;
16
+ /** The session's Gemma pipeline (parity §3.5); absent → the deterministic parser only. */
17
+ advisoryRunner?: GemmaCallPipeline;
17
18
  execFileFn?: GitExecFileFn;
18
19
  }
19
20
  export interface GitStatusParsedIntent {
@@ -51,21 +52,15 @@ export declare function extractComparisonBaseRefInfo(prompt: string, options?: {
51
52
  export declare function extractComparisonBaseRef(prompt: string): string | undefined;
52
53
  export declare function isConceptualGitQuestion(prompt: string): boolean;
53
54
  export declare function isStagedDiffIntent(prompt: string): boolean;
54
- export declare function isQualifiedAdvisoryRunner(runner?: AdvisoryRunner): boolean;
55
- /**
56
- * Resolves the preferred advisory runner for intent classification per Pure LLM policy:
57
- * "If there is local LLM, use it over hosted LLM".
58
- */
59
- export declare function resolvePreferredAdvisoryRunner(runner?: AdvisoryRunner): AdvisoryRunner | null;
60
55
  export declare function renderLocalGemmaGitStatusQueryPrompt(args: {
61
56
  userPrompt: string;
62
57
  }): string;
63
58
  export declare function parseGemmaGitStatusResponse(raw: string): GitStatusParsedIntent | null;
64
- export declare function parseGitStatusQueryWithGemma(runner: AdvisoryRunner, prompt: string, timeoutMs?: number): Promise<GitStatusParsedIntent | null>;
59
+ export declare function parseGitStatusQueryWithGemma(runner: GemmaCallPipeline, prompt: string, timeoutMs?: number): Promise<GitStatusParsedIntent | null>;
65
60
  export declare function parseGitStatusQuerySemantic(prompt: string): GitStatusParsedIntent;
66
61
  export declare function resolveGitStatusIntent(options: {
67
62
  prompt: string;
68
- advisoryRunner?: AdvisoryRunner;
63
+ advisoryRunner?: GemmaCallPipeline;
69
64
  }): Promise<GitStatusParsedIntent>;
70
65
  /**
71
66
  * Safely format code blocks using collision-safe backtick fences (R2-F3).
@@ -1,3 +1,4 @@
1
+ import { GemmaCallPipeline } from '../planner/gemma-pipeline';
1
2
  import { AsyncLocalStorage } from 'node:async_hooks';
2
3
  import { AppSyncClient } from '../appsync';
3
4
  import type { MobileInputStatusUpdater } from '../appsync';
@@ -37,9 +38,7 @@ import { type GatePromptEnvelope, type DecryptedReviewSummaryInput } from './gat
37
38
  import type { Event } from '../types/events';
38
39
  import { type StructuralSummaryGenerator } from '../structural-summary';
39
40
  import type { PlannerAdapter } from '../planner/adapter';
40
- import type { PlannerCacheLayer } from '../planner/cache';
41
41
  import type { PlannerInput, PlannerDecision as PlannerDecisionRuntime } from '../planner/types';
42
- import type { LocalGemmaAdvisoryRunner } from '../planner/local-advisory';
43
42
  import type { LocalExecutor } from '../local-executor';
44
43
  import type { WorkItemInput, DisjointnessRejection, RevertManifestStore } from '../local-executor';
45
44
  import type { QuorumLoop } from './quorum-loop';
@@ -125,29 +124,11 @@ export interface RunOrchestrationShellArgs {
125
124
  */
126
125
  plannerAdapter?: PlannerAdapter;
127
126
  /**
128
- * CP-1.b IMPL r2 M-3 — Optional planner cache layer. With LOCK #18
129
- * write-through removed, normal session exit (no LRU eviction, no
130
- * TTL deletion) would lose pending in-memory inserts. When
131
- * supplied, `runOrchestrationShell` calls `cache.flush()` from
132
- * every teardown path (`/quit`, non-TTY EOF, SIGINT/SIGTERM,
133
- * finally-block cleanup).
134
- */
135
- plannerCache?: PlannerCacheLayer;
136
- /**
137
- * Local Gemma planner M1/M2 — disables automatic structural-summary refreshes
138
- * at shell startup and before natural-language classification. The local
139
- * planner path must not scan or summarize the repository before route-source
140
- * safety and an accepted local route exist. Accepted local routes such as
141
- * `familiarize` may still run their own route-scoped refresh.
142
- */
143
- disableStructuralContextRefresh?: boolean;
144
- /**
145
- * Local Gemma planner migration state. `hosted` keeps the legacy backend
146
- * planner semantics. `local_gemma_qat` means natural-language decisions came
147
- * from the local runtime and need shell-owned M1 safety/milestone gates.
148
- * `local_unavailable` means the user opted into local-model orchestration but
149
- * the verified runtime is not usable; for Pro/Max sessions it falls back to
150
- * hosted Bedrock planner, while Free tier operates in Companion Mode.
127
+ * Planner availability (parity §3.5). `local_unavailable` means the user opted
128
+ * into local-model orchestration but no planner source is usable, so the
129
+ * session runs in Companion Mode. The other values are display-only (the
130
+ * status badge's initial state); no shell behaviour branches on them — every
131
+ * shared rule runs identically on the local and hosted runtimes.
151
132
  */
152
133
  plannerRuntimeKind?: 'hosted' | 'local_gemma_qat' | 'local_unavailable';
153
134
  /**
@@ -165,12 +146,13 @@ export interface RunOrchestrationShellArgs {
165
146
  */
166
147
  cwd?: string;
167
148
  /**
168
- * Local Gemma M3 advisory runtime. This is intentionally separate from the
169
- * planner adapter interface: classification remains schema-only, while
170
- * familiarization uses a bounded read-only advisory prompt after route
171
- * safety, local runtime eligibility, and repository context refresh pass.
149
+ * The session's Gemma pipeline (planner parity §3), the ONLY caller of a Gemma
150
+ * runner: every advisory call (answers, brainstorm, browse readers, git-status
151
+ * intent) goes through it, with identical logic on the local and hosted
152
+ * runtimes. The planner adapter classifies through the same instance. The name
153
+ * is kept for continuity; absent when the session has no model source.
172
154
  */
173
- localAdvisoryRunner?: LocalGemmaAdvisoryRunner;
155
+ localAdvisoryRunner?: GemmaCallPipeline;
174
156
  /**
175
157
  * CP-12 W3 §3.5 — the team-mode Local Executor. Present ONLY for an
176
158
  * Agent-Teams session: `cli.ts` constructs ONE per session (honoring the
@@ -877,8 +859,11 @@ export declare function routeBrainstorm(deps: {
877
859
  userPrompt: string;
878
860
  needsRepositoryContext?: boolean;
879
861
  priorTurns?: string[];
880
- /** RAW base64 images (IMAGE-ATTACHMENT-DESIGN.md §6) — Ollama-runtime only; text-mode. */
881
- images?: string[];
862
+ /**
863
+ * The turn's image attachments (IMAGE-ATTACHMENT-DESIGN.md §6). Read fd-safe and selected
864
+ * per attempt by the pipeline under the routed runtime's capability (parity §3.7).
865
+ */
866
+ attachments?: ImageAttachment[];
882
867
  onProgress?: (event: TaskProgressEvent) => void;
883
868
  }): Promise<void>;
884
869
  /**
@@ -888,17 +873,18 @@ export declare function routeBrainstorm(deps: {
888
873
  * classifier's `advisory_summary` — but that path is blind to the image. When an
889
874
  * image is attached, the reducer's inline render is suppressed (strip
890
875
  * summary+clarifying_question+rationale, §6/rev-13) and THIS produces the sole
891
- * answer: text-mode `generateAdvisory` with the RAW base64 `images[]` to the LOCAL
892
- * Ollama model, image framed as untrusted data, output `sanitizeForTerminal`-scrubbed.
893
- * 100% on-device. `browse`/`familiarize` are excluded (their answer is grounded on
894
- * other context). All-images-failed → the caller passes an empty `images` and we
895
- * proceed text-only.
876
+ * answer: a text-mode `pipeline.advise` with the turn's attachments, image framed as
877
+ * untrusted data, output `sanitizeForTerminal`-scrubbed. `browse`/`familiarize` are
878
+ * excluded (their answer is grounded on other context). Parity §3.7: the pipeline reads
879
+ * the attachments fd-safe and selects them per attempt for the routed runtime; the prompt
880
+ * states the number actually included, and the shared omitted-images note follows the call.
896
881
  */
897
882
  export declare function routeAdvisory(deps: {
898
883
  store: OrchestrationStore;
899
884
  args: RunOrchestrationShellArgs;
900
885
  userPrompt: string;
901
- images: string[];
886
+ /** The turn's image attachments (empty for a text-only answer). */
887
+ attachments: ImageAttachment[];
902
888
  /**
903
889
  * #619 — recent conversation turns for the TEXT-ONLY answerer case. When an
904
890
  * `advisory_response` lands mid-conversation, the classifier's inline
@@ -926,13 +912,6 @@ export declare function routeAdvisory(deps: {
926
912
  * R1-F3 — classifier-emitted inline summary to use as fallback if generateAdvisory throws, times out, or returns empty.
927
913
  */
928
914
  fallbackSummary?: string;
929
- /**
930
- * #619 Stage-2 (Codex LOW) — how many images the USER attached this turn,
931
- * regardless of how many survived base64 read. When >0 but `images` is empty
932
- * (all unreadable), the prompt keeps the image framing so the model says the
933
- * image couldn't be used instead of answering as if none was attached.
934
- */
935
- attemptedImages?: number;
936
915
  /**
937
916
  * 2026-09-12 (updated 2026-09-23) — the page the `browse` route read most recently in this session.
938
917
  * Rendered as untrusted source material injected ONLY when the shell determines via
@@ -940,6 +919,11 @@ export declare function routeAdvisory(deps: {
940
919
  * over-grounding and false refusals on unrelated technical follow-ups like A* algorithm).
941
920
  */
942
921
  retainedPage?: RetainedBrowsePage;
922
+ /**
923
+ * Planner parity §3.5 / P-21 (Stage 2 codex S2-codex-F3): the turn's progress sink (the shell
924
+ * passes the dispatcher that also relays to mobile). Absent → the store only.
925
+ */
926
+ onProgress?: (event: TaskProgressEvent) => void;
943
927
  }): Promise<void>;
944
928
  export declare function parseTeamWorkItems(rawArg: string): {
945
929
  ok: true;
@@ -1142,7 +1126,6 @@ export declare function serializeShellSubmissions(handler: (text: string, images
1142
1126
  * slash route then reports "unavailable").
1143
1127
  */
1144
1128
  export declare function buildContinuationActionDeps(args: RunOrchestrationShellArgs, store: OrchestrationStore): Partial<ContinuationCliDeps>;
1145
- export declare function shouldSuppressStructuralContextRefresh(args: RunOrchestrationShellArgs): boolean;
1146
1129
  /**
1147
1130
  * P46a C3 — "Retry as a new task": start a NEW linked task (`retryOf`) on a
1148
1131
  * fresh snapshot from the stashed retry brief (the original request + the
@@ -1,5 +1,5 @@
1
1
  import type { OrchestrationStore } from './store';
2
- import type { LocalGemmaAdvisoryRunner } from '../planner/local-advisory';
2
+ import { type GemmaCallPipeline } from '../planner/gemma-pipeline';
3
3
  import { guardedFetch } from './web/fetch';
4
4
  import { webSearch } from './web/search';
5
5
  import type { TaskProgressEvent } from './task-progress';
@@ -19,8 +19,8 @@ export interface RetainedBrowsePage {
19
19
  export declare const RETAINED_PAGE_MAX_CHARS = 120000;
20
20
  export interface RouteBrowseDeps {
21
21
  store: OrchestrationStore;
22
- /** Undefined when the local model isn't installed/enabled → no-model advisory. */
23
- localAdvisoryRunner?: LocalGemmaAdvisoryRunner;
22
+ /** The session's Gemma pipeline; undefined when no model source exists → no-model advisory. */
23
+ localAdvisoryRunner?: GemmaCallPipeline;
24
24
  userPrompt: string;
25
25
  browseUrls?: string[];
26
26
  browseQuery?: string;
@@ -55,7 +55,7 @@ export interface RouteBrowseDeps {
55
55
  export declare function deriveSanitizedFallbackSearchQuery(prompt: string): string;
56
56
  /**
57
57
  * Orchestrate a `browse` route. Fetch an explicit URL, or run a web
58
- * search and read the top results in parallel, then answer via local Gemma. Mirrors
58
+ * search and read the top results in parallel, then answer via the Gemma pipeline. Mirrors
59
59
  * routeBrainstorm/routeFamiliarize (no-model advisory, clean failures, no crash).
60
60
  */
61
61
  export declare function routeBrowse(deps: RouteBrowseDeps): Promise<void>;
@@ -0,0 +1,63 @@
1
+ import { GemmaCallError } from '../planner/gemma-errors';
2
+ import type { GemmaCallPipeline, PipelineResult } from '../planner/gemma-pipeline';
3
+ import type { PlannerRuntimeKind } from './status-format';
4
+ type Outcome = PipelineResult | GemmaCallError | unknown;
5
+ /** "Brainstorming with local Gemma…" (spinner, before the call). */
6
+ export declare function brainstormingLabel(pipeline: GemmaCallPipeline): string;
7
+ /** The answer spinner, before the call. */
8
+ export declare function answeringLabel(pipeline: GemmaCallPipeline, hasImages: boolean): string;
9
+ /**
10
+ * While subscribed, `onLabel` receives the answer spinner text for the runtime of every attempt
11
+ * that starts in the caller's turn (§3.5, P-21 / Codex R2-F4), so the failover retry re-labels
12
+ * the line for the runtime actually answering. Returns the unsubscribe.
13
+ */
14
+ export declare function followAnsweringLabel(pipeline: GemmaCallPipeline, hasImages: boolean, onLabel: (label: string) => void): () => void;
15
+ /** "Hosted Gemma" / "Local Gemma" for the runtime that produced (or last attempted) the outcome. */
16
+ export declare function modelLabel(pipeline: GemmaCallPipeline, outcome: Outcome): string;
17
+ /** "The model" (hosted) / "The local model" (local). */
18
+ export declare function modelNoun(pipeline: GemmaCallPipeline, outcome: Outcome): string;
19
+ /** "the model" (hosted) / "the local model" (local), mid-sentence. */
20
+ export declare function modelNounInSentence(pipeline: GemmaCallPipeline, outcome: Outcome): string;
21
+ /** The note under a concise fallback answer when the detailed generation came back empty. */
22
+ export declare function emptyDetailedAnswerNote(pipeline: GemmaCallPipeline, outcome: Outcome): string;
23
+ /** The note under a concise fallback answer when the detailed generation failed. */
24
+ export declare function unavailableDetailedAnswerNote(pipeline: GemmaCallPipeline, outcome: Outcome, reason: string): string;
25
+ /**
26
+ * The trust line. "No hosted model was called and no code was changed." only when neither the
27
+ * turn (TurnScope: classify, every advisory call, every retry) nor the given outcomes attempted
28
+ * hosted; otherwise "No code was changed." (R4-F5, R5-F1, Codex R2-F1).
29
+ */
30
+ export declare function trustLine(pipeline: GemmaCallPipeline | null | undefined, ...outcomes: Outcome[]): string;
31
+ /**
32
+ * "local Gemma" / "hosted Gemma" for the runtime a progress phase starts on (§3.5: "when a
33
+ * progress phase starts, from `currentRuntime()`"). Display text, carried on the progress event
34
+ * and rendered only by {@link progressPhaseLabel}.
35
+ */
36
+ export declare function progressRuntimeNoun(pipeline: GemmaCallPipeline): string;
37
+ /**
38
+ * While subscribed, `onRuntimeNoun` receives "local Gemma" / "hosted Gemma" for every attempt
39
+ * that starts in the caller's turn, so an active progress phase re-labels when a hosted retry
40
+ * starts (§3.5, Codex R2-F4). Returns the unsubscribe.
41
+ */
42
+ export declare function followProgressRuntimeNoun(pipeline: GemmaCallPipeline, onRuntimeNoun: (runtimeNoun: string) => void): () => void;
43
+ /**
44
+ * Progress-phase labels (desktop progress line and the mobile relay, §3.5). The runtime noun
45
+ * comes from the event (set when the phase starts and re-set when an attempt starts on another
46
+ * runtime); without one the label is runtime-neutral.
47
+ */
48
+ export declare function progressPhaseLabel(phase: 'classifying' | 'browse_synthesizing' | 'brainstorming' | 'answering' | 'analyzing_images', runtimeNoun?: string): string;
49
+ /**
50
+ * The status badge follows the session source (failover and recovery, §3.5): `onKind` receives
51
+ * the badge's display kind each time the source switches. The kind is read only by the badge
52
+ * formatter (`status-format.ts`). Returns the unsubscribe.
53
+ */
54
+ export declare function followBadgeKind(pipeline: GemmaCallPipeline, onKind: (kind: Exclude<PlannerRuntimeKind, 'local_unavailable'>) => void): () => void;
55
+ /**
56
+ * The shared note for images left out of an answer (§3.7). Posted after the call, before the
57
+ * answer — and with the empty-answer message when the attempt that reached its runner answered
58
+ * nothing (N6). Returns undefined when nothing was left out or no attempt reached a runner.
59
+ */
60
+ export declare function omittedImagesNote(outcome: {
61
+ images?: Pick<PipelineResult, 'images'>['images'];
62
+ }, attempted: number): string | undefined;
63
+ export {};
@@ -9,13 +9,21 @@ export interface TaskProgressRelay {
9
9
  relay(event: TaskProgressEvent): Promise<boolean>;
10
10
  reset(): void;
11
11
  }
12
- export declare const TERMINAL_MILESTONES: Set<"preparing_workspace" | "shadow_created" | "implementor_running" | "diff_captured" | "submitting_diff" | "reviewers_dispatched" | "seat_update" | "verdicts_progress" | "revise_round" | "round_failed" | "continuation_offered" | "declared_tests_skipped" | "promoting" | "promoted" | "discarding" | "discarded" | "not_applied" | "waiting_user" | "planner_classifying" | "familiarizing" | "agent_advisory" | "web_formulating_query" | "web_searching" | "web_fetching" | "web_synthesizing" | "advisory_dispatched" | "advisory_seat_update" | "advisory_synthesizing" | "familiarize_scanning" | "familiarize_generating" | "brainstorming" | "team_decomposing" | "progress_cleared" | "halt_notice">;
12
+ export declare const TERMINAL_MILESTONES: Set<"brainstorming" | "answering" | "preparing_workspace" | "shadow_created" | "implementor_running" | "diff_captured" | "submitting_diff" | "reviewers_dispatched" | "seat_update" | "verdicts_progress" | "revise_round" | "round_failed" | "continuation_offered" | "declared_tests_skipped" | "promoting" | "promoted" | "discarding" | "discarded" | "not_applied" | "waiting_user" | "planner_classifying" | "familiarizing" | "agent_advisory" | "web_formulating_query" | "web_searching" | "web_fetching" | "web_synthesizing" | "advisory_dispatched" | "advisory_seat_update" | "advisory_synthesizing" | "familiarize_scanning" | "familiarize_generating" | "team_decomposing" | "progress_cleared" | "halt_notice">;
13
13
  /**
14
14
  * Milestones that must be emitted immediately to mobile without debounce delay.
15
15
  * Includes terminal task outcomes, discrete reviewer verdict submissions, and
16
16
  * verdict collection progress, ensuring mobile notifications always precede gate prompts.
17
17
  */
18
18
  export declare function isImmediateMilestone(event: TaskProgressEvent): boolean;
19
+ /**
20
+ * Planner parity §3.5 / P-11 / P-21 (Stage 2 codex S2-codex-F3): the phases whose mobile label
21
+ * names the runtime doing the work. Each carries the SAME text as the desktop line (from
22
+ * `runtime-labels.ts`), and a re-label of the phase in progress (the hosted retry after a
23
+ * failover, or recovery) is relayed at once rather than debounced, so the phone names the
24
+ * runtime actually working while that attempt is still running.
25
+ */
26
+ export declare const RUNTIME_LABELLED_PHASES: ReadonlySet<TaskProgressEvent['phase']>;
19
27
  export declare const MAX_MOBILE_PROGRESS_MESSAGE_LENGTH = 120;
20
28
  export declare const DEFAULT_MIN_RELAY_INTERVAL_MS = 2500;
21
29
  /**
@@ -76,6 +76,7 @@ export type TaskProgressEvent = ({
76
76
  phase: 'waiting_user';
77
77
  } | {
78
78
  phase: 'planner_classifying';
79
+ runtimeNoun?: string;
79
80
  } | {
80
81
  phase: 'familiarizing';
81
82
  } | {
@@ -91,6 +92,7 @@ export type TaskProgressEvent = ({
91
92
  url?: string;
92
93
  } | {
93
94
  phase: 'web_synthesizing';
95
+ runtimeNoun?: string;
94
96
  } | {
95
97
  phase: 'advisory_dispatched';
96
98
  agents?: string[];
@@ -109,6 +111,11 @@ export type TaskProgressEvent = ({
109
111
  agent?: string;
110
112
  } | {
111
113
  phase: 'brainstorming';
114
+ runtimeNoun?: string;
115
+ } | {
116
+ phase: 'answering';
117
+ images?: boolean;
118
+ runtimeNoun?: string;
112
119
  } | {
113
120
  phase: 'team_decomposing';
114
121
  } | {
@@ -33,3 +33,10 @@ export declare class PlannerCacheLayer {
33
33
  private ensureDir;
34
34
  private rmRecursiveSafe;
35
35
  }
36
+ /**
37
+ * Planner parity §3.5 / P-20 — the planner keeps no cache on either runtime, so
38
+ * `/cache-clear` only removes what an earlier version may have left for THIS user: the
39
+ * workflow cache file and its `.bak` / `.tmp` siblings, by exact path (never a directory
40
+ * walk; `unlink` refuses a directory and removes a symlink itself, not its target).
41
+ */
42
+ export declare function clearRetiredPlannerCache(userId: string, cacheRoot?: string): Promise<string>;
@@ -0,0 +1,61 @@
1
+ import type { OmittedImage } from './gemma-pipeline';
2
+ export type GemmaRuntime = 'local' | 'hosted';
3
+ export declare abstract class GemmaCallError extends Error {
4
+ /** Every runtime the call attempted, in order (set by the pipeline). */
5
+ attemptedRuntimes: GemmaRuntime[];
6
+ /**
7
+ * The image selection of the last attempt that reached its runner (set by the pipeline), so
8
+ * an answer that fails after dispatch can still say which images were left out (§3.7, N6).
9
+ */
10
+ images?: {
11
+ included: number[];
12
+ omitted: OmittedImage[];
13
+ };
14
+ protected constructor(name: string, message: string);
15
+ }
16
+ /**
17
+ * The runtime could not be reached or did not answer: connection refused/reset, timeout,
18
+ * 5xx, throttling, a malformed transport response, or ANY unrecognised runner failure (the
19
+ * catch-all keeps today's composite failover behaviour, `composite-adapter.ts:170-190`).
20
+ * The only class a composite source fails over on.
21
+ */
22
+ export declare class GemmaTransportError extends GemmaCallError {
23
+ readonly cause?: unknown | undefined;
24
+ constructor(message: string, cause?: unknown | undefined);
25
+ }
26
+ /** The prompt does not fit the routed runtime's context window (D-13 capacity). */
27
+ export declare class GemmaContextFullError extends GemmaCallError {
28
+ constructor(message: string);
29
+ }
30
+ /** The model produced no visible text and stopped normally. */
31
+ export declare class GemmaEmptyOutputError extends GemmaCallError {
32
+ constructor(message?: string);
33
+ }
34
+ /** The model produced no visible text for its whole output budget (or a watchdog cut it). */
35
+ export declare class GemmaHiddenTokenLoopError extends GemmaCallError {
36
+ constructor(message?: string);
37
+ }
38
+ /**
39
+ * The hosted server refused the call on policy: budget, tier, ownership, image, size,
40
+ * provider, malformed request; or the client has no session key yet. Never a failover.
41
+ */
42
+ export type GemmaPolicyCode = 'BudgetExceeded' | 'TierGateRejected' | 'OwnershipRejected' | 'ImageRejected' | 'MalformedRequest' | 'RequestTooLarge' | 'ProviderUnsupported' | 'SessionUnavailable';
43
+ export declare class GemmaPolicyError extends GemmaCallError {
44
+ readonly code: GemmaPolicyCode;
45
+ constructor(code: GemmaPolicyCode, message: string);
46
+ }
47
+ /** The hosted backend predates the client-rendered contract (§7). */
48
+ export declare class GemmaBackendTooOldError extends GemmaCallError {
49
+ constructor(message?: string);
50
+ }
51
+ /** The caller abandoned the call. Never a failover or a source switch (§3.2, R3-F4). */
52
+ export declare class GemmaAbortError extends GemmaCallError {
53
+ constructor(message?: string);
54
+ }
55
+ export declare function isGemmaCallError(err: unknown): err is GemmaCallError;
56
+ /**
57
+ * The user-facing cause for a typed failure (§3.2 "Shell handling", identical on both
58
+ * runtimes). Returns `undefined` for an untyped error (the caller keeps its own text).
59
+ * Empty / hidden-token output keeps each caller's own wording.
60
+ */
61
+ export declare function describeGemmaError(err: unknown): string | undefined;
@@ -0,0 +1,219 @@
1
+ import { GemmaCallError, GemmaEmptyOutputError, type GemmaRuntime } from './gemma-errors';
2
+ import { type GemmaRunner, type ImageFormat, type RunnerImage, type RunnerImageCapability } from './gemma-runner';
3
+ /** §3.3 — every rendered prompt, both runtimes, before any runner call (= the server caps). */
4
+ export declare const GEMMA_PROMPT_BYTE_BOUND = 100000;
5
+ /** §3.3 — output budgets are capped at the hosted server ceiling on both runtimes. */
6
+ export declare const GEMMA_MAX_OUTPUT_TOKENS = 1536;
7
+ /**
8
+ * §3.3 / §4 — a classify call's budget is capped at the rendered-classify contract's ceiling
9
+ * (`maxOutputTokens: Int! # 1..1024`), on both runtimes, so no caller can send hosted a request
10
+ * the server refuses while local accepts it (Stage 1 R1-F7).
11
+ */
12
+ export declare const GEMMA_CLASSIFY_MAX_OUTPUT_TOKENS = 1024;
13
+ /** Marker appended to a text answer that ended early (moved here from `ollama.ts`, §3.3). */
14
+ export declare const PIPELINE_CUT_OFF_MARKER = " [\u2026]";
15
+ /** Composite recovery probe interval (unchanged from the composite adapter). */
16
+ export declare const RECOVERY_PROBE_INTERVAL_MS = 30000;
17
+ /**
18
+ * The runtimes attempted by ONE admitted natural-language turn. Each turn owns its scope;
19
+ * nothing can reset or replace another turn's record. Implemented with AsyncLocalStorage:
20
+ * every pipeline call made inside the turn's async context records into it, including the
21
+ * classify (the adapter never handles runtime facts) — and a slash command, which runs
22
+ * outside that context (it bypasses the submission queue), never touches it.
23
+ *
24
+ * Inheritance (Stage 1 N7): AsyncLocalStorage follows every async continuation, timers and
25
+ * un-awaited promises included, so background work a turn STARTS inherits its scope. Every
26
+ * pipeline call the shell makes for a turn is awaited inside it (classify, the answer, the
27
+ * brainstorm, the browse readers, the git-status intent). The one background caller — the
28
+ * verdict classifier, running inside a task the turn started — opts out with
29
+ * {@link GemmaCallPipeline.runOutsideTurn}; a new background entry point must do the same.
30
+ */
31
+ export declare class TurnScope {
32
+ private readonly attempted;
33
+ /** Recorded when an attempt STARTS, before dispatch, so a pending hosted call counts. */
34
+ record(runtime: GemmaRuntime): void;
35
+ attemptedRuntimes(): GemmaRuntime[];
36
+ }
37
+ export interface RunnerSource {
38
+ current(): GemmaRunner;
39
+ /**
40
+ * True when a transport failure of `failed` (the runner THIS attempt used) can be retried
41
+ * on another runner. Decided by the failed runner, never by the source's current state:
42
+ * one source serves every concurrent call, so another call or the recovery probe may have
43
+ * moved it meanwhile (§3.2: failover is local → hosted only; §3.4: never hosted → local).
44
+ */
45
+ canFailOver(failed: GemmaRunner): boolean;
46
+ /** Switch to the fallback runner (a no-op switch when already there) and return it. */
47
+ failOver(failed: GemmaRunner, reason: GemmaCallError): GemmaRunner;
48
+ onSourceChange(listener: (runtime: GemmaRuntime) => void): () => void;
49
+ runners(): GemmaRunner[];
50
+ dispose(): void;
51
+ /** Sources that switch runtimes post a notice per switch (§3.4). */
52
+ bindNotice?(sink: (text: string) => void): void;
53
+ }
54
+ export declare class SingleRunnerSource implements RunnerSource {
55
+ private readonly runner;
56
+ constructor(runner: GemmaRunner);
57
+ current(): GemmaRunner;
58
+ canFailOver(_failed: GemmaRunner): boolean;
59
+ failOver(_failed: GemmaRunner, _reason: GemmaCallError): GemmaRunner;
60
+ onSourceChange(): () => void;
61
+ runners(): GemmaRunner[];
62
+ dispose(): void;
63
+ }
64
+ /**
65
+ * Pro/Max with a local model: local first, hosted on a TRANSPORT failure, back to local when
66
+ * the 30 s probe succeeds. One instance per session serves classify, advisory, git-status and
67
+ * the verdict classifier, so every consumer sees the same runtime at the same moment.
68
+ */
69
+ export declare class CompositeRunnerSource implements RunnerSource {
70
+ private readonly local;
71
+ private readonly hosted;
72
+ private readonly notice;
73
+ private readonly probeIntervalMs;
74
+ private source;
75
+ private probeTimer;
76
+ private probing;
77
+ private readonly listeners;
78
+ private disposed;
79
+ private boundNotice;
80
+ constructor(local: GemmaRunner, hosted: GemmaRunner, notice?: (text: string) => void, probeIntervalMs?: number);
81
+ /** The pipeline binds its notice sink here, so the shell's advisory line gets the switch notices. */
82
+ bindNotice(sink: (text: string) => void): void;
83
+ private say;
84
+ current(): GemmaRunner;
85
+ canFailOver(failed: GemmaRunner): boolean;
86
+ failOver(_failed: GemmaRunner, reason: GemmaCallError): GemmaRunner;
87
+ onSourceChange(listener: (runtime: GemmaRuntime) => void): () => void;
88
+ runners(): GemmaRunner[];
89
+ dispose(): void;
90
+ private emit;
91
+ private startProbe;
92
+ /** Test seam + timer body. */
93
+ tick(): Promise<void>;
94
+ }
95
+ export interface PipelineImage {
96
+ /** Position in the user's attachment order (stable across attempts). */
97
+ index: number;
98
+ /** Reads the bytes (fd-safe in the shell). May throw: the image is then "unreadable". */
99
+ read(): {
100
+ buf: Buffer;
101
+ mime: string;
102
+ };
103
+ }
104
+ export type OmittedReason = 'over-limit' | 'format' | 'unreadable';
105
+ export interface OmittedImage {
106
+ index: number;
107
+ reason: OmittedReason;
108
+ /** For a `format` omission of a known format (the note names it, e.g. WEBP on local). */
109
+ format?: ImageFormat;
110
+ }
111
+ export interface LoadedImage {
112
+ index: number;
113
+ image?: RunnerImage;
114
+ unreadable?: boolean;
115
+ /** The bytes are not one of the five image formats any runtime reads. */
116
+ unknownFormat?: boolean;
117
+ }
118
+ /** First-N-under-cap in attachment order (today's local rule, `attachments.ts:953-976`), per capability. */
119
+ export declare function selectImagesForCapability(loaded: LoadedImage[], cap: RunnerImageCapability): {
120
+ selected: Array<{
121
+ index: number;
122
+ image: RunnerImage;
123
+ }>;
124
+ omitted: OmittedImage[];
125
+ };
126
+ /** Read each image once per call; selection is redone per attempt from these (§3.7). */
127
+ export declare function loadPipelineImages(images: PipelineImage[]): LoadedImage[];
128
+ export interface PipelineResult {
129
+ text: string;
130
+ finishReason: 'stop' | 'length';
131
+ runtime: GemmaRuntime;
132
+ attemptedRuntimes: GemmaRuntime[];
133
+ images: {
134
+ included: number[];
135
+ omitted: OmittedImage[];
136
+ };
137
+ callId: number;
138
+ }
139
+ export interface PipelineClassifyRequest {
140
+ prompt: string;
141
+ /** Absent → JSON-object mode (the verdict classifier). */
142
+ jsonSchema?: object;
143
+ maxOutputTokens: number;
144
+ timeoutMs?: number;
145
+ signal?: AbortSignal;
146
+ }
147
+ export interface PipelineAdviseRequest {
148
+ /** Rendered per attempt, with the number of images that attempt includes (§3.7). */
149
+ buildPrompt: (ctx: {
150
+ includedImageCount: number;
151
+ }) => string;
152
+ images?: PipelineImage[];
153
+ responseFormat?: 'json' | 'text';
154
+ numPredict?: number;
155
+ timeoutMs?: number;
156
+ signal?: AbortSignal;
157
+ numCtx?: number;
158
+ think?: boolean;
159
+ }
160
+ /** The shape today's text-only callers use (`AdvisoryOptions`), minus runtime facts. */
161
+ export interface PipelineAdvisoryOptions {
162
+ responseFormat?: 'json' | 'text';
163
+ numPredict?: number;
164
+ timeoutMs?: number;
165
+ signal?: AbortSignal;
166
+ numCtx?: number;
167
+ think?: boolean;
168
+ /** Ignored (§3.3: temperature is always 0). */
169
+ temperature?: number;
170
+ /** Raw base64 images (legacy shape). Image callers should use `advise()` instead. */
171
+ images?: string[];
172
+ }
173
+ /**
174
+ * A caller that rejects a returned answer as empty (after its own sanitization) reports it as the
175
+ * empty answer of the call that produced it: the error carries that call's attempted runtimes and
176
+ * image selection, so trust lines and notes stay truthful (§3.2). Runtime facts stay in the
177
+ * pipeline (core Stage 1 R1-F1).
178
+ */
179
+ export declare function emptyOutputErrorFor(result: PipelineResult, message?: string): GemmaEmptyOutputError;
180
+ export declare class GemmaCallPipeline {
181
+ private readonly source;
182
+ private callSeq;
183
+ private readonly turnAttemptListeners;
184
+ private noticeSink;
185
+ constructor(source: RunnerSource);
186
+ /** The shell's advisory line for the source's switch notices (§3.4); null detaches it. */
187
+ setNoticeSink(sink: ((text: string) => void) | null): void;
188
+ notify(text: string): void;
189
+ currentRuntime(): GemmaRuntime;
190
+ onSourceChange(listener: (runtime: GemmaRuntime) => void): () => void;
191
+ /**
192
+ * Progress re-labelling (§3.5, Codex R2-F4): while subscribed, `listener` hears the runtime
193
+ * of every attempt that STARTS in the caller's turn (outside a turn: every attempt), so an
194
+ * active progress phase re-labels when a hosted retry starts. Calls outside the turn (the
195
+ * verdict classifier) never re-label a turn's phase.
196
+ */
197
+ onTurnAttemptStart(listener: (runtime: GemmaRuntime) => void): () => void;
198
+ openTurn(): TurnScope;
199
+ runInTurn<T>(scope: TurnScope, fn: () => Promise<T>): Promise<T>;
200
+ /** Calls that are not part of the user's turn (the verdict classifier) never record into it. */
201
+ runOutsideTurn<T>(fn: () => Promise<T>): Promise<T>;
202
+ currentTurn(): TurnScope | undefined;
203
+ setActiveSession(sessionId: string | null): void;
204
+ warm(): Promise<boolean>;
205
+ unload(): Promise<boolean>;
206
+ dispose(): void;
207
+ /** Classify-mode call: the planner (via the adapter) and the verdict classifier. */
208
+ classify(req: PipelineClassifyRequest): Promise<PipelineResult>;
209
+ /** Advisory-mode call with per-attempt image selection and prompt rendering (§3.4, §3.7). */
210
+ advise(req: PipelineAdviseRequest): Promise<PipelineResult>;
211
+ /** Text-only callers' shape (browse readers, git-status intent). Returns the text. */
212
+ generateAdvisory(promptText: string, options?: PipelineAdvisoryOptions): Promise<string>;
213
+ private assertPromptBound;
214
+ /** §3.3 — the same rules on both runtimes. */
215
+ private postProcessAdvisory;
216
+ /** The attempt loop: failover on transport errors only, never after the caller aborted. */
217
+ private run;
218
+ private tag;
219
+ }