@quantiya/codevibe-claude-plugin 2.0.34 → 2.0.36

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 (20) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/node_modules/@quantiya/codevibe-core/dist/index.d.ts +2 -1
  3. package/node_modules/@quantiya/codevibe-core/dist/index.js +462 -425
  4. package/node_modules/@quantiya/codevibe-core/dist/local-model/ollama.d.ts +2 -0
  5. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/route-browse-multi-result.test.d.ts +1 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/runOrchestrationShell-browse-cancel.test.d.ts +1 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/brainstorm-quorum.d.ts +14 -5
  8. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +1107 -214
  9. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/InputBar.d.ts +5 -0
  10. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/components/OrchestrationApp.d.ts +5 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/context-compaction.d.ts +10 -3
  12. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +17 -0
  13. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/route-browse.d.ts +22 -2
  14. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/web/extract.d.ts +18 -0
  15. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/web/fetch.d.ts +5 -1
  16. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/web/search.d.ts +32 -3
  17. package/node_modules/@quantiya/codevibe-core/dist/planner/index.d.ts +1 -1
  18. package/node_modules/@quantiya/codevibe-core/dist/planner/local-advisory.d.ts +60 -1
  19. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  20. package/package.json +12 -3
@@ -124,5 +124,10 @@ export interface InputBarProps {
124
124
  * `@all` plus the local agents detected for this signed-in user/device.
125
125
  */
126
126
  mentionSuggestions?: MentionSuggestion[];
127
+ /**
128
+ * Cancellation callback (WEB-BROWSING-DESIGN.md §Cancellation).
129
+ * Fires on Escape key when dropdown is closed and gate prompt is not active.
130
+ */
131
+ onCancel?: () => void;
127
132
  }
128
133
  export declare function InputBar(props: InputBarProps): React.ReactElement;
@@ -50,6 +50,11 @@ export interface OrchestrationAppProps {
50
50
  * #C8M-11 and never reach `onUserInput`. `images` (IMAGE-ATTACHMENT-DESIGN.md
51
51
  * §13 Option 2) carries the resolved path(s) of `[Image #N]` input chips. */
52
52
  onUserInput: (text: string, images?: string[]) => void;
53
+ /**
54
+ * Cancellation callback (WEB-BROWSING-DESIGN.md line 61).
55
+ * Forwarded to <InputBar onCancel={props.onCancel}>.
56
+ */
57
+ onCancel?: () => void;
53
58
  /**
54
59
  * CP-8 min Stage 1 R1 closure + Stage 2 r1 Codex HIGH-1 (REQUIRED). The
55
60
  * desktop-side AppSync transport surface (codevibe-core's own
@@ -23,7 +23,8 @@ export declare const COMPACTION_KEEP_HOT_RECENT_ITEMS = 16;
23
23
  export declare const CONTEXT_ITEMS_RETENTION_MS: number;
24
24
  /**
25
25
  * The rendered session-context section's own budget (inside the F6 total) for
26
- * the implementor dispatch block and the brainstorm panel: 2,000 chars, every
26
+ * the implementor dispatch block (the brainstorm panel uses
27
+ * `SESSION_CONTEXT_SECTION_MAX_CHARS_BRAINSTORM` below): 2,000 chars, every
27
28
  * line capped by `capForRender`, stop at the first line that does not fit — the
28
29
  * shipped behaviour. The planner's classification render uses
29
30
  * `SESSION_CONTEXT_SECTION_MAX_CHARS_CLASSIFY` below (Stage-1 r1 F5 / r2 F2,
@@ -34,10 +35,16 @@ export declare const SESSION_CONTEXT_SECTION_MAX_CHARS = 2000;
34
35
  * The planner's CLASSIFICATION render (2026-09-12): hot turns render WHOLE inside
35
36
  * a 6,000-char section under the local runner's explicit 16K-token window.
36
37
  * Stage-1 r1 F5 — this applies ONLY to `purpose: 'classification'`: the implementor
37
- * dispatch block (hard 4,800-char total, `DISPATCH_CONTEXT_MAX_CHARS`) and the
38
- * brainstorm panel keep the 2,000-char section and the per-line cap above.
38
+ * dispatch block (hard 4,800-char total, `DISPATCH_CONTEXT_MAX_CHARS`) keeps the
39
+ * 2,000-char section and the per-line cap above.
39
40
  */
40
41
  export declare const SESSION_CONTEXT_SECTION_MAX_CHARS_CLASSIFY = 6000;
42
+ /**
43
+ * The BRAINSTORM panel render: hot turns render WHOLE inside a 4,800-char section
44
+ * (aligns with MAX_WORKFLOW_HANDOFF_TOTAL_CHARS). Prevents 400-char sentence-cutting
45
+ * between multi-agent deliberation turns while preserving the overall budget.
46
+ */
47
+ export declare const SESSION_CONTEXT_SECTION_MAX_CHARS_BRAINSTORM = 4800;
41
48
  /**
42
49
  * A1f Stage-1 R1 HIGH + Stage-2 R1 (H1/M2/M3/M5) — one distilled FACT:
43
50
  * - `body` — the source item's COMPLETE typed payload, verbatim (M3:
@@ -265,6 +265,7 @@ export { renderEntryAsLine, runLineLogFallback } from './non-tty-fallback';
265
265
  export { initInkRuntime, getInkRuntime } from './ink-runtime';
266
266
  export { reducer } from './reducer';
267
267
  export { createOrchestrationStore, type OrchestrationStore } from './store';
268
+ export { routeBrowse, type RouteBrowseDeps } from './route-browse';
268
269
  export type { Mode, Tier, PlannerDecision, AgentKind, PlannerHealthState, RunningTaskState, ReviewerSeatState, GateState, ExecutionEventEntry, RefusalEventEntry, BypassEventEntry, MobileEventEntry, EventStreamEntry, ConversationEntry, QueuedTask, PendingClarification, OrchestrationState, OrchestrationAction, LastModeFile, TeamState, TeamTrackEntry, TeamTrackState, TeamMergeGateStatus, LiveProgress, } from './types';
269
270
  export { formatElapsed, isSpinnerPhase, renderProgressLine, type TaskProgressEvent, type OnProgress, } from './task-progress';
270
271
  export { runDeclaredTestSurfaces, DECLARED_TEST_TIMEOUT_MS, type DeclaredTestResult, type DeclaredTestReason, type RunDeclaredTestSurfacesArgs, } from './declared-test-runner';
@@ -488,6 +489,7 @@ export declare function routeReadOnlyAgentMention(args: {
488
489
  shellArgs: RunOrchestrationShellArgs;
489
490
  intent: AgentMentionIntent;
490
491
  advisoryBrief: string;
492
+ retainedPage?: RetainedBrowsePage | null;
491
493
  /**
492
494
  * [#637] This turn's validated image attachments + the ordered `[Image #N]`
493
495
  * chip-path list — the SAME `turnAttachments`/`turnImagePaths` pair the /task
@@ -919,6 +921,10 @@ export interface HandleShellUserInputDeps extends StructuralSummaryRunDeps {
919
921
  * buffered replies. Optional — direct unit callers/tests may omit it.
920
922
  */
921
923
  turnOwnership?: BrainstormTurnOwnership;
924
+ /**
925
+ * Optional AbortSignal for in-flight web browsing cancellation (Esc-abort).
926
+ */
927
+ browseSignal?: AbortSignal;
922
928
  }
923
929
  /**
924
930
  * Slash + natural-language dispatch. Extracted from `runOrchestrationShell`
@@ -993,8 +999,19 @@ export declare function tokenizeCommandLine(input: string): {
993
999
  error?: string;
994
1000
  };
995
1001
  export declare function handleShellUserInput(deps: HandleShellUserInputDeps): Promise<void>;
1002
+ /**
1003
+ * TTL bounding for retained browse pages (15 minutes).
1004
+ * Follow-up mentions within 15 minutes receive the retained page; older pages
1005
+ * expire and are cleared to prevent leaking stale web data into unrelated future turns.
1006
+ */
1007
+ export declare const RETAINED_PAGE_MAX_AGE_MS: number;
1008
+ export declare function getFreshRetainedBrowsePage(pages: Map<string, RetainedBrowsePage>, sessionId: string, now?: number): RetainedBrowsePage | null;
996
1009
  /** Test seam — clear the per-session retained pages between cases. */
997
1010
  export declare function resetRetainedBrowsePagesForTests(): void;
1011
+ /** Test seam — set a per-session retained page for tests. */
1012
+ export declare function setRetainedBrowsePageForTests(sessionId: string, page: RetainedBrowsePage): void;
1013
+ /** Test seam — get a per-session retained page for tests. */
1014
+ export declare function getRetainedBrowsePageForTests(sessionId: string): RetainedBrowsePage | null;
998
1015
  /**
999
1016
  * [A1g] Stage-2 round-4 (Codex round-3 MEDIUM) — the EXPLICIT per-turn panel-
1000
1017
  * ownership token. `serializeShellSubmissions` mints ONE fresh object per
@@ -1,5 +1,7 @@
1
1
  import type { OrchestrationStore } from './store';
2
2
  import type { LocalGemmaAdvisoryRunner } from '../planner/local-advisory';
3
+ import { guardedFetch } from './web/fetch';
4
+ import { webSearch } from './web/search';
3
5
  /**
4
6
  * A page the shell read this session, retained (2026-09-12) so a follow-up
5
7
  * ("that sounds like a summary, I am looking for your thoughts") can be answered
@@ -27,10 +29,28 @@ export interface RouteBrowseDeps {
27
29
  signal?: AbortSignal;
28
30
  /** Called once readable text was extracted from a fetched page (before summarizing). */
29
31
  onPageRead?: (page: RetainedBrowsePage) => void;
32
+ /** Dependency injection for guardedFetch (for tests / isolation). */
33
+ guardedFetchFn?: typeof guardedFetch;
34
+ /** Dependency injection for webSearch (for tests / isolation). */
35
+ webSearchFn?: typeof webSearch;
36
+ /**
37
+ * Optional delegation callback when an `@agent` mention is routed through browse.
38
+ * If provided, browse fetches the page(s) and delegates answering to the mentioned agent.
39
+ * Returns true if successfully handled.
40
+ */
41
+ delegateAnswer?: (args: {
42
+ header: string;
43
+ page?: RetainedBrowsePage;
44
+ }) => Promise<boolean>;
30
45
  }
31
46
  /**
32
- * Orchestrate a `browse` route. Fetch an explicit URL, or run a keyless web
33
- * search and read the top result, then answer via local Gemma. Mirrors
47
+ * Strip leading agent mention tokens, common command/browse noise, redact paths,
48
+ * terminal-sanitize, and cap at 200 characters for fallback public web search queries.
49
+ */
50
+ export declare function deriveSanitizedFallbackSearchQuery(prompt: string): string;
51
+ /**
52
+ * Orchestrate a `browse` route. Fetch an explicit URL, or run a web
53
+ * search and read the top results in parallel, then answer via local Gemma. Mirrors
34
54
  * routeBrainstorm/routeFamiliarize (no-model advisory, clean failures, no crash).
35
55
  */
36
56
  export declare function routeBrowse(deps: RouteBrowseDeps): Promise<void>;
@@ -9,3 +9,21 @@ export declare function __resetExtractCacheForTest(): void;
9
9
  * NEVER throws — returns `{ title:'', text:'' }` on the size cap / load / parse failure.
10
10
  */
11
11
  export declare function htmlToText(html: string): Promise<ExtractResult>;
12
+ export interface ExtractQuerySnippetsOptions {
13
+ maxChars?: number;
14
+ leadChars?: number;
15
+ }
16
+ /**
17
+ * Tokenize query into search keywords (>= 3 chars for Latin/digits, >= 2 chars for CJK, plus CJK bigrams).
18
+ * Discards stopwords (Simplified + Traditional CJK and common Latin function words) and strips URLs.
19
+ */
20
+ export declare function extractQueryTokens(query: string): string[];
21
+ /**
22
+ * Extract query-relevant snippets from extracted page text.
23
+ * Always retains the lead (context / introduction, ~500 chars), plus highest-scoring
24
+ * paragraphs matching query keywords across the entire document (e.g. pricing,
25
+ * release dates, or specs at the bottom of long pages).
26
+ * Returns paragraphs stitched in original document order with `[...]` omission markers.
27
+ * NEVER throws.
28
+ */
29
+ export declare function extractQueryRelevantSnippets(fullText: string, query: string, options?: ExtractQuerySnippetsOptions): string;
@@ -8,8 +8,12 @@ export interface FetchResult {
8
8
  contentType: string;
9
9
  body: string;
10
10
  }
11
+ export interface GuardedFetchOptions {
12
+ headers?: Record<string, string>;
13
+ allowJson?: boolean;
14
+ }
11
15
  /**
12
16
  * Guarded GET with manual redirect following (full guard re-run per hop). Returns
13
17
  * decoded text or throws FetchError. `signal` lets the caller (Esc) abort.
14
18
  */
15
- export declare function guardedFetch(rawUrl: string, signal?: AbortSignal): Promise<FetchResult>;
19
+ export declare function guardedFetch(rawUrl: string, signal?: AbortSignal, options?: GuardedFetchOptions): Promise<FetchResult>;
@@ -1,6 +1,15 @@
1
1
  export interface SearchResult {
2
2
  url: string;
3
3
  title: string;
4
+ snippet?: string;
5
+ source?: 'google' | 'brave' | 'duckduckgo';
6
+ }
7
+ export interface WebSearchOptions {
8
+ limit?: number;
9
+ provider?: 'auto' | 'google' | 'brave' | 'duckduckgo';
10
+ googleApiKey?: string;
11
+ googleCx?: string;
12
+ braveApiKey?: string;
4
13
  }
5
14
  /**
6
15
  * Parse DDG HTML result anchors. DDG wraps targets as
@@ -10,7 +19,27 @@ export interface SearchResult {
10
19
  */
11
20
  export declare function parseDdgResults(html: string, limit?: number): SearchResult[];
12
21
  /**
13
- * Run a keyless web search. Returns ≤limit results, or [] on any failure
14
- * (network/parse) the caller decides the advisory. `signal` lets Esc abort.
22
+ * Query Google Custom Search JSON API when configured.
23
+ * Returns empty array on missing config or error (never throws).
24
+ */
25
+ export declare function fetchGoogleCseResults(query: string, signal?: AbortSignal, limit?: number, config?: {
26
+ apiKey?: string;
27
+ cx?: string;
28
+ }): Promise<SearchResult[]>;
29
+ /**
30
+ * Query Brave Search API when configured.
31
+ * Returns empty array on missing config or error (never throws).
32
+ */
33
+ export declare function fetchBraveSearchResults(query: string, signal?: AbortSignal, limit?: number, config?: {
34
+ apiKey?: string;
35
+ }): Promise<SearchResult[]>;
36
+ /**
37
+ * Run a multi-provider web search with waterfall fallback.
38
+ * Priority: Google CSE -> Brave API -> DuckDuckGo HTML.
39
+ * Returns ≤limit results, or [] on total failure. NEVER throws.
40
+ */
41
+ /**
42
+ * Fetch results from DuckDuckGo HTML endpoint as zero-config default.
15
43
  */
16
- export declare function webSearch(query: string, signal?: AbortSignal, limit?: number): Promise<SearchResult[]>;
44
+ export declare function fetchDuckDuckGoResults(query: string, signal?: AbortSignal, limit?: number): Promise<SearchResult[]>;
45
+ export declare function webSearch(query: string, signal?: AbortSignal, limitOrOptions?: number | WebSearchOptions): Promise<SearchResult[]>;
@@ -4,4 +4,4 @@ export { PlannerCacheLayer } from './cache';
4
4
  export { PlannerHealthMachine } from './health-state';
5
5
  export { BackendPlannerClient, PlannerBudgetExceededError, PlannerTierGateRejectedError, type PlannerAppSyncTransport, type PlannerCryptoBridge, type SessionKeyResolver, type ShellEventEmit, type EmitShellEventFn, } from './client';
6
6
  export { LocalGemmaPlannerAdapter, parseLocalGemmaPlannerDecision, PlannerOutputUnparseableError, renderLocalGemmaPlannerPrompt, type LocalGemmaPlannerRunner, } from './local-gemma';
7
- export { parseLocalGemmaAdvisorySummary, renderLocalGemmaFamiliarizePrompt, type LocalGemmaAdvisoryRunner, } from './local-advisory';
7
+ export { parseLocalGemmaAdvisorySummary, renderLocalGemmaFamiliarizePrompt, renderLocalGemmaMultiBrowsePrompt, renderLocalGemmaBrowsePrompt, MAX_MULTI_BROWSE_TOTAL_CONTENT_CHARS, MAX_MULTI_BROWSE_RENDERED_PROMPT_CHARS, type LocalGemmaAdvisoryRunner, } from './local-advisory';
@@ -27,12 +27,25 @@ export interface LocalGemmaAdvisoryOptions {
27
27
  * on the classifier (forced-JSON) or on `browse`/`familiarize`.
28
28
  */
29
29
  images?: string[];
30
+ /**
31
+ * Explicit context window length (tokens) for Ollama `/api/generate options.num_ctx`.
32
+ * For multi-source browsing, callers pass numCtx: 8192 to expand the active context
33
+ * window to accommodate multi-source prompts alongside output token generation.
34
+ */
35
+ numCtx?: number;
36
+ /**
37
+ * Control hidden reasoning/thinking tokens in Ollama models (e.g. Gemma 4).
38
+ * Omitted unless set; browse callers pass false (R8-F2, R9-F4).
39
+ */
40
+ think?: boolean;
30
41
  }
31
42
  export declare function redactAbsoluteLocalPaths(text: string): string;
32
43
  export declare function renderLocalGemmaFamiliarizePrompt(args: {
33
44
  userPrompt: string;
34
45
  summary: StructuralSummary;
35
46
  }): string;
47
+ export declare const MAX_BROWSE_CONTENT_CHARS = 12000;
48
+ export declare const MAX_MULTI_BROWSE_TOTAL_CONTENT_CHARS = 6000;
36
49
  /**
37
50
  * WEB-BROWSING (docs/WEB-BROWSING-DESIGN.md): build the advisory prompt for a
38
51
  * fetched web page. The model ANSWERS THE USER'S QUESTION grounded in the fetched
@@ -46,7 +59,7 @@ export declare function renderLocalGemmaFamiliarizePrompt(args: {
46
59
  * tolerates either prose or JSON. The title/content stay in the JSON DATA payload (escaped
47
60
  * — also defeats label-spoofing) and are NEVER interpolated into the instruction prose.
48
61
  *
49
- * Budget: cap title/url/userPrompt + content (lead), then SHRINK on ACTUAL rendered length
62
+ * Budget: cap title/url/userPrompt + content, then SHRINK on ACTUAL rendered length
50
63
  * (JSON.stringify can expand quote/backslash-heavy text) so the emitted JSON stays valid.
51
64
  */
52
65
  export declare function renderLocalGemmaBrowsePrompt(args: {
@@ -57,6 +70,52 @@ export declare function renderLocalGemmaBrowsePrompt(args: {
57
70
  };
58
71
  content: string;
59
72
  }): string;
73
+ export interface BrowseSourceInput {
74
+ url: string;
75
+ title: string;
76
+ content: string;
77
+ id?: number | string;
78
+ }
79
+ export declare const MAX_MULTI_BROWSE_RENDERED_PROMPT_CHARS = 8000;
80
+ export declare const MAX_MULTI_BROWSE_PER_SOURCE_URL_CHARS = 500;
81
+ export declare const MAX_MULTI_BROWSE_PER_SOURCE_TITLE_CHARS = 150;
82
+ /**
83
+ * Compute the per-source snippet extraction budget so that the resulting rendered
84
+ * prompt stays within MAX_MULTI_BROWSE_RENDERED_PROMPT_CHARS (8,000 chars) upfront.
85
+ *
86
+ * Framing overhead breakdown:
87
+ * - Fixed instructions / prose: ~1,140 chars
88
+ * - Top-level JSON scaffolding: ~40 chars
89
+ * - Per-source JSON scaffolding (keys, id, spacing): ~85 chars per source
90
+ * - User question: userPromptLength (capped to 1,000)
91
+ * - Source metadata: URLs (capped to perSourceUrlBudget) + titles (capped to perSourceTitleBudget)
92
+ * - JSON escaping safety margin: 200 chars
93
+ *
94
+ * Exported so route-browse.ts extracts snippets at the exact budget needed, ensuring
95
+ * the shrink loop does not need to cut content or discard query-relevant snippets (R2-F2 & R3-F3).
96
+ */
97
+ export declare function computeMultiBrowseContentBudget(params: {
98
+ survivingCount: number;
99
+ userPrompt?: string;
100
+ sourcesMetadata?: Array<{
101
+ url?: string;
102
+ title?: string;
103
+ }>;
104
+ }): number;
105
+ /**
106
+ * WEB-BROWSING (multi-source synthesis): build an advisory prompt for MULTIPLE
107
+ * fetched web sources (1 to 5). The local model answers the user's question,
108
+ * synthesizing facts across all sources, citing sources by number/title, and noting
109
+ * any discrepancies.
110
+ *
111
+ * Budget: Total rendered prompt budget strictly capped to MAX_MULTI_BROWSE_RENDERED_PROMPT_CHARS (8,000 chars, ~2,000 estimated input tokens)
112
+ * with combined source content bounded to MAX_MULTI_BROWSE_TOTAL_CONTENT_CHARS (6,000 chars, ~1,500 estimated tokens).
113
+ * Character budget bounds prompt size before dispatch; runtime tokenization and context capacity depend on the local model runner's configuration.
114
+ */
115
+ export declare function renderLocalGemmaMultiBrowsePrompt(args: {
116
+ userPrompt: string;
117
+ sources: BrowseSourceInput[];
118
+ }): string;
60
119
  export declare function boundedPriorBrainstormTurns(turns: string[] | undefined): string[];
61
120
  /**
62
121
  * WEB-BROWSING (search-route): turn the user's request + recent conversation into a
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "2.0.29",
3
+ "version": "2.0.31",
4
4
  "description": "Core library for CodeVibe plugins - shared keychain, crypto, AppSync, and auth functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-claude-plugin",
3
- "version": "2.0.34",
3
+ "version": "2.0.36",
4
4
  "description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
5
5
  "main": "dist/server.js",
6
6
  "codevibe": {
@@ -48,7 +48,7 @@
48
48
  "node": ">=22.0.0"
49
49
  },
50
50
  "dependencies": {
51
- "@quantiya/codevibe-core": "^2.0.29",
51
+ "@quantiya/codevibe-core": "^2.0.31",
52
52
  "@quantiya/quorum-core": "^1.0.1",
53
53
  "dotenv": "^16.6.1",
54
54
  "express": "^5.1.0",
@@ -76,5 +76,14 @@
76
76
  "ts-jest": "^29.4.9",
77
77
  "ts-node": "^10.9.2",
78
78
  "typescript": "^5.9.3"
79
- }
79
+ },
80
+ "bundleDependencies": [
81
+ "@quantiya/codevibe-core",
82
+ "@quantiya/quorum-core",
83
+ "dotenv",
84
+ "express",
85
+ "graphql",
86
+ "uuid",
87
+ "ws"
88
+ ]
80
89
  }