@oh-my-pi/pi-coding-agent 15.1.8 → 15.2.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 (71) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/dist/types/cli/update-cli.d.ts +18 -0
  3. package/dist/types/config/settings-schema.d.ts +10 -0
  4. package/dist/types/eval/py/kernel.d.ts +6 -0
  5. package/dist/types/goals/state.d.ts +1 -1
  6. package/dist/types/goals/tools/goal-tool.d.ts +4 -0
  7. package/dist/types/hashline/parser.d.ts +6 -2
  8. package/dist/types/internal-urls/memory-protocol.d.ts +6 -0
  9. package/dist/types/main.d.ts +25 -1
  10. package/dist/types/modes/theme/shimmer.d.ts +27 -0
  11. package/dist/types/slash-commands/helpers/format.d.ts +4 -1
  12. package/dist/types/tools/ast-edit.d.ts +3 -0
  13. package/dist/types/tools/ast-grep.d.ts +3 -0
  14. package/dist/types/tools/find.d.ts +3 -0
  15. package/dist/types/tools/search.d.ts +3 -0
  16. package/dist/types/tui/file-list.d.ts +6 -0
  17. package/dist/types/tui/hyperlink.d.ts +42 -0
  18. package/dist/types/tui/index.d.ts +1 -0
  19. package/dist/types/utils/tool-choice.d.ts +2 -1
  20. package/dist/types/web/search/providers/utils.d.ts +27 -1
  21. package/package.json +7 -7
  22. package/src/cli/update-cli.ts +78 -36
  23. package/src/config/model-registry.ts +23 -12
  24. package/src/config/settings-schema.ts +12 -0
  25. package/src/config/settings.ts +28 -5
  26. package/src/edit/renderer.ts +5 -3
  27. package/src/eval/py/executor.ts +12 -1
  28. package/src/eval/py/kernel.ts +24 -8
  29. package/src/extensibility/plugins/legacy-pi-compat.ts +2 -2
  30. package/src/goals/runtime.ts +9 -3
  31. package/src/goals/state.ts +1 -1
  32. package/src/goals/tools/goal-tool.ts +12 -2
  33. package/src/hashline/diff.ts +1 -1
  34. package/src/hashline/execute.ts +2 -2
  35. package/src/hashline/parser.ts +87 -12
  36. package/src/internal-urls/memory-protocol.ts +1 -1
  37. package/src/main.ts +13 -2
  38. package/src/modes/interactive-mode.ts +29 -1
  39. package/src/modes/theme/shimmer.ts +79 -0
  40. package/src/prompts/agents/oracle.md +15 -16
  41. package/src/prompts/tools/goal.md +7 -2
  42. package/src/session/agent-session.ts +12 -75
  43. package/src/slash-commands/helpers/format.ts +23 -3
  44. package/src/task/executor.ts +115 -19
  45. package/src/tools/ast-edit.ts +39 -6
  46. package/src/tools/ast-grep.ts +38 -6
  47. package/src/tools/find.ts +13 -2
  48. package/src/tools/read.ts +46 -6
  49. package/src/tools/search.ts +447 -265
  50. package/src/tui/file-list.ts +10 -2
  51. package/src/tui/hyperlink.ts +126 -0
  52. package/src/tui/index.ts +1 -0
  53. package/src/utils/tool-choice.ts +7 -7
  54. package/src/web/kagi.ts +2 -2
  55. package/src/web/parallel.ts +3 -3
  56. package/src/web/search/index.ts +20 -9
  57. package/src/web/search/providers/anthropic.ts +4 -2
  58. package/src/web/search/providers/brave.ts +4 -2
  59. package/src/web/search/providers/codex.ts +4 -1
  60. package/src/web/search/providers/exa.ts +4 -1
  61. package/src/web/search/providers/gemini.ts +4 -1
  62. package/src/web/search/providers/jina.ts +4 -2
  63. package/src/web/search/providers/kagi.ts +5 -1
  64. package/src/web/search/providers/kimi.ts +4 -2
  65. package/src/web/search/providers/parallel.ts +5 -1
  66. package/src/web/search/providers/perplexity.ts +7 -2
  67. package/src/web/search/providers/searxng.ts +4 -1
  68. package/src/web/search/providers/synthetic.ts +4 -2
  69. package/src/web/search/providers/tavily.ts +4 -2
  70. package/src/web/search/providers/utils.ts +63 -1
  71. package/src/web/search/providers/zai.ts +4 -2
@@ -1,6 +1,6 @@
1
1
  import { getAgentDbPath } from "@oh-my-pi/pi-utils";
2
2
  import { AgentStorage } from "../../../session/agent-storage";
3
- import type { SearchSource } from "../../../web/search/types";
3
+ import { SearchProviderError, type SearchProviderId, type SearchSource } from "../../../web/search/types";
4
4
  import { dateToAgeSeconds } from "../utils";
5
5
 
6
6
  /**
@@ -49,6 +49,36 @@ export async function isApiKeyAvailable(findApiKey: () => string | null | Promis
49
49
  }
50
50
  }
51
51
 
52
+ /**
53
+ * Default hard ceiling for a single web-search round-trip. 60s tolerates
54
+ * legitimate slow LLM-mediated responses (anthropic web_search_20250305,
55
+ * perplexity, gemini, codex) while still guaranteeing the session unfreezes
56
+ * within a minute if Bun's `AbortSignal` fails to propagate on Windows.
57
+ *
58
+ * Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
59
+ * settle far faster in practice; reusing the same ceiling keeps the wiring
60
+ * uniform without compromising correctness.
61
+ */
62
+ export const SEARCH_HARD_TIMEOUT_MS = 60_000;
63
+
64
+ /**
65
+ * Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
66
+ * outbound `fetch()` is guaranteed to settle within `ms` even when the
67
+ * runtime fails to propagate cancellation to the underlying transport.
68
+ *
69
+ * Bun's WinHTTP backend on Windows is known to ignore `AbortSignal` once a
70
+ * TCP/TLS connection stalls (oven-sh/bun#15275, oven-sh/bun#18536); without
71
+ * this safety net a stalled web-search request freezes the entire session
72
+ * because the user's Esc is never delivered to the native layer.
73
+ *
74
+ * @param signal - Caller cancellation signal, if any.
75
+ * @param ms - Hard timeout in milliseconds. Defaults to {@link SEARCH_HARD_TIMEOUT_MS}.
76
+ */
77
+ export function withHardTimeout(signal: AbortSignal | undefined, ms: number = SEARCH_HARD_TIMEOUT_MS): AbortSignal {
78
+ const timeout = AbortSignal.timeout(ms);
79
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
80
+ }
81
+
52
82
  /**
53
83
  * Map a provider's raw source list to the unified SearchSource shape,
54
84
  * clamped to the requested result count and annotated with ageSeconds.
@@ -70,3 +100,35 @@ export function toSearchSources(
70
100
  ageSeconds: dateToAgeSeconds(source.publishedDate),
71
101
  }));
72
102
  }
103
+
104
+ /**
105
+ * Quota/auth signals across providers. Telemetry on 15.1.7/15.1.8 showed users
106
+ * hitting credit-exhaustion and 401/402/403 responses that were surfaced as
107
+ * raw HTTP error text. Map those into compact, provider-tagged messages so
108
+ * the orchestrator can chain-advance cleanly and the final summary stays
109
+ * legible when every provider rejects the request.
110
+ *
111
+ * Returns `null` when the response does not match a known quota/auth signal,
112
+ * leaving the caller to throw its provider-specific fallback error.
113
+ */
114
+ const CREDIT_BODY_PATTERN = /credits?\s*(?:exhausted|exceeded)|quota|insufficient/i;
115
+
116
+ export function classifyProviderHttpError(
117
+ provider: SearchProviderId,
118
+ status: number,
119
+ body: string,
120
+ ): SearchProviderError | null {
121
+ if (CREDIT_BODY_PATTERN.test(body)) {
122
+ return new SearchProviderError(provider, `${provider}: credits exhausted`, status);
123
+ }
124
+ if (status === 402) {
125
+ return new SearchProviderError(provider, `${provider}: 402 credits exhausted`, status);
126
+ }
127
+ if (status === 401) {
128
+ return new SearchProviderError(provider, `${provider}: 401 unauthorized`, status);
129
+ }
130
+ if (status === 403) {
131
+ return new SearchProviderError(provider, `${provider}: 403 forbidden`, status);
132
+ }
133
+ return null;
134
+ }
@@ -11,7 +11,7 @@ import { SearchProviderError } from "../../../web/search/types";
11
11
  import { dateToAgeSeconds } from "../utils";
12
12
  import type { SearchParams } from "./base";
13
13
  import { SearchProvider } from "./base";
14
- import { findCredential, isApiKeyAvailable } from "./utils";
14
+ import { classifyProviderHttpError, findCredential, isApiKeyAvailable, withHardTimeout } from "./utils";
15
15
 
16
16
  const ZAI_MCP_URL = "https://api.z.ai/api/mcp/web_search_prime/mcp";
17
17
  const ZAI_TOOL_NAME = "web_search_prime";
@@ -73,11 +73,13 @@ async function callZaiTool(apiKey: string, args: Record<string, unknown>, signal
73
73
  arguments: args,
74
74
  },
75
75
  }),
76
- signal,
76
+ signal: withHardTimeout(signal),
77
77
  });
78
78
 
79
79
  if (!response.ok) {
80
80
  const errorText = await response.text();
81
+ const classified = classifyProviderHttpError("zai", response.status, errorText);
82
+ if (classified) throw classified;
81
83
  throw new SearchProviderError("zai", `Z.AI MCP error (${response.status}): ${errorText}`, response.status);
82
84
  }
83
85