pi-para 0.6.5 → 0.6.7

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 (49) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/CONFIGURATION.md +1 -1
  3. package/README.md +1 -1
  4. package/SETUP.md +2 -2
  5. package/STRUCTURE.md +1 -1
  6. package/dist/cli.d.ts +23 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +71 -83
  9. package/dist/cli.js.map +1 -1
  10. package/dist/commands.d.ts +2 -0
  11. package/dist/commands.d.ts.map +1 -1
  12. package/dist/commands.js +12 -5
  13. package/dist/commands.js.map +1 -1
  14. package/dist/context.d.ts.map +1 -1
  15. package/dist/context.js +2 -1
  16. package/dist/context.js.map +1 -1
  17. package/dist/credentials.d.ts +2 -2
  18. package/dist/credentials.d.ts.map +1 -1
  19. package/dist/credentials.js +6 -14
  20. package/dist/credentials.js.map +1 -1
  21. package/dist/doctor.js +19 -18
  22. package/dist/doctor.js.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +14 -10
  25. package/dist/index.js.map +1 -1
  26. package/dist/model-resolver.d.ts +13 -6
  27. package/dist/model-resolver.d.ts.map +1 -1
  28. package/dist/model-resolver.js +35 -19
  29. package/dist/model-resolver.js.map +1 -1
  30. package/dist/scheduler/index.d.ts.map +1 -1
  31. package/dist/scheduler/index.js +9 -3
  32. package/dist/scheduler/index.js.map +1 -1
  33. package/dist/store.d.ts +11 -0
  34. package/dist/store.d.ts.map +1 -1
  35. package/dist/store.js +48 -16
  36. package/dist/store.js.map +1 -1
  37. package/dist/wiki-tool-guidance.js +2 -2
  38. package/dist/wiki-tool-guidance.js.map +1 -1
  39. package/package.json +9 -9
  40. package/src/cli.ts +69 -81
  41. package/src/commands.ts +15 -6
  42. package/src/context.ts +2 -1
  43. package/src/credentials.ts +7 -14
  44. package/src/doctor.ts +18 -16
  45. package/src/index.ts +11 -10
  46. package/src/model-resolver.ts +45 -20
  47. package/src/scheduler/index.ts +9 -3
  48. package/src/store.ts +52 -15
  49. package/src/wiki-tool-guidance.ts +2 -2
package/src/store.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * re-indexing after wiki changes, and embedding lifecycle.
6
6
  */
7
7
 
8
+ import { AsyncLocalStorage } from "node:async_hooks";
8
9
  import { readFileSync, existsSync } from "node:fs";
9
10
  import { homedir } from "node:os";
10
11
  import { join } from "node:path";
@@ -73,6 +74,51 @@ const RAW_CONTEXT = "Immutable source material: articles, documents, notes. Not
73
74
 
74
75
  const pendingEmbeds = new WeakMap<QMDStore, Promise<void>>();
75
76
 
77
+ export type QmdEmbedOperationResult<T> =
78
+ | { ok: true; value: T; diagnostics: string[] }
79
+ | { ok: false; error: unknown; diagnostics: string[] };
80
+
81
+ // qmd-engine's API adapter logs recoverable provider failures directly to
82
+ // console.error before returning null. Capture only those known messages so
83
+ // best-effort vector search and scheduler retries do not flash raw errors in
84
+ // the Pi UI. Other console errors continue to pass through unchanged.
85
+ const qmdEmbedDiagnosticContext = new AsyncLocalStorage<string[]>();
86
+ let qmdEmbedCaptureCount = 0;
87
+ let qmdEmbedOriginalConsoleError: typeof console.error | null = null;
88
+ const qmdEmbedConsoleInterceptor = (...args: unknown[]): void => {
89
+ const sink = qmdEmbedDiagnosticContext.getStore();
90
+ const first = String(args[0] ?? "");
91
+ if (sink && /^embed(?:Batch)? error:/i.test(first)) {
92
+ sink.push(args.map((arg) => arg instanceof Error ? arg.message : String(arg)).join(" "));
93
+ return;
94
+ }
95
+ qmdEmbedOriginalConsoleError?.(...args);
96
+ };
97
+
98
+ /** Run a QMD embedding operation without leaking its recoverable raw stderr. */
99
+ export async function captureQmdEmbedErrors<T>(operation: () => Promise<T>): Promise<QmdEmbedOperationResult<T>> {
100
+ const diagnostics: string[] = [];
101
+ if (qmdEmbedCaptureCount++ === 0) {
102
+ qmdEmbedOriginalConsoleError = console.error;
103
+ console.error = qmdEmbedConsoleInterceptor;
104
+ }
105
+ try {
106
+ return await qmdEmbedDiagnosticContext.run(diagnostics, async () => {
107
+ try {
108
+ return { ok: true, value: await operation(), diagnostics };
109
+ } catch (error) {
110
+ return { ok: false, error, diagnostics };
111
+ }
112
+ });
113
+ } finally {
114
+ qmdEmbedCaptureCount--;
115
+ if (qmdEmbedCaptureCount === 0) {
116
+ if (console.error === qmdEmbedConsoleInterceptor && qmdEmbedOriginalConsoleError) console.error = qmdEmbedOriginalConsoleError;
117
+ qmdEmbedOriginalConsoleError = null;
118
+ }
119
+ }
120
+ }
121
+
76
122
  // -- Inert-provider tracking ---------------------------------------------------
77
123
 
78
124
  /** Stores opened with the NO_LOCAL_PROVIDERS shim (no real API provider). */
@@ -184,22 +230,10 @@ export async function openStore(wikiDir: string, opts: OpenStoreOptions = {}): P
184
230
  if (opts.backgroundEmbed !== false && opts.paraConfig?.qmd.embedEnabled !== false) {
185
231
  // Schedule embedding in background (non-blocking).
186
232
  // BM25 search works immediately; hybrid search improves once embed completes.
187
- // Suppress console.error from qmd's internal embed failures.
188
- const origError = console.error;
189
- const embedPromise = store.embed().then(() => {
190
- pendingEmbeds.delete(store);
191
- }).catch(() => {
192
- // Embedding failures are non-fatal — hybrid search degrades to BM25
233
+ const embedPromise = captureQmdEmbedErrors(() => store.embed()).then(() => {
234
+ // Embedding failures are non-fatal — hybrid search degrades to BM25.
193
235
  pendingEmbeds.delete(store);
194
- }).finally(() => {
195
- console.error = origError;
196
236
  });
197
- // Temporarily suppress embed error noise during background embed
198
- console.error = (...args: unknown[]) => {
199
- const msg = String(args[0] ?? "");
200
- if (msg.includes("embed error") || msg.includes("fetch failed")) return;
201
- origError(...args);
202
- };
203
237
  pendingEmbeds.set(store, embedPromise);
204
238
  }
205
239
 
@@ -317,7 +351,10 @@ async function mergeLexAndVectorResults(
317
351
  const llm = (store.internal as unknown as { llm?: unknown }).llm;
318
352
  const embedModel = (llm as { embedModelName?: string } | undefined)?.embedModelName;
319
353
  if (status.hasVectorIndex && llm && embedModel) {
320
- vecResults = await store.internal.searchVec(query, embedModel, limit, "wiki", llm as any) as RawQmdSearchResult[];
354
+ const vectorAttempt = await captureQmdEmbedErrors(
355
+ () => store.internal.searchVec(query, embedModel, limit, "wiki", llm as any) as Promise<RawQmdSearchResult[]>,
356
+ );
357
+ if (vectorAttempt.ok) vecResults = vectorAttempt.value;
321
358
  }
322
359
  } catch {
323
360
  // Semantic search is best-effort; lexical BM25 remains the reliable fallback.
@@ -39,7 +39,7 @@ const TOOL_SPECIFIC_GUIDELINES: Record<WikiToolName, readonly string[]> = {
39
39
  "After ingesting, synthesize durable knowledge with wiki_write/wiki_edit when the source contains reusable facts.",
40
40
  ],
41
41
  wiki_query: [
42
- "Use wiki_query before answering questions that may depend on past architecture decisions, debugging history, conventions, or domain knowledge.",
42
+ "Call wiki_query FIRST — before other tools — for any non-trivial planning, implementation, debugging, architecture, or review request; the wiki may hold prior decisions, root causes, and conventions.",
43
43
  "Use global=true only when the answer should search across project scopes.",
44
44
  ],
45
45
  wiki_edit: [
@@ -76,7 +76,7 @@ export function buildWikiToolGuidanceSection(): string {
76
76
  return [
77
77
  "## pi-para Wiki Tool Guidance",
78
78
  "",
79
- "- Search with wiki_query when past architecture, debugging, conventions, domain facts, or prior discussion may matter.",
79
+ "- Start non-trivial work with wiki_query using keywords from the request; past architecture, debugging, conventions, domain facts, or prior discussion may already be documented.",
80
80
  "- Treat AGING/STALE/VERY STALE code/config/API claims as untrusted until verified against source; fix wrong pages with wiki_edit.",
81
81
  "- Persist major decisions/debugging/conventions without being asked. Use wiki_edit for surgical fixes; wiki_write create/append for new/additive knowledge; replace only for deliberate full rewrites.",
82
82
  "- Prefer resources/, kebab-case scope/tags, [[wikilinks]], and no secrets.",