@oh-my-pi/pi-coding-agent 17.1.0 → 17.1.2

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 (196) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/cli.js +6325 -4420
  3. package/dist/types/cli/bench-cli.d.ts +1 -0
  4. package/dist/types/config/model-resolver.d.ts +6 -3
  5. package/dist/types/config/settings-schema.d.ts +62 -0
  6. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
  7. package/dist/types/launch/broker-list-order.test.d.ts +1 -0
  8. package/dist/types/live/attestation.d.ts +2 -0
  9. package/dist/types/live/controller.d.ts +10 -2
  10. package/dist/types/live/protocol.d.ts +1 -1
  11. package/dist/types/live/transport.d.ts +6 -19
  12. package/dist/types/live/visualizer.d.ts +8 -11
  13. package/dist/types/modes/components/assistant-message.d.ts +1 -0
  14. package/dist/types/modes/components/custom-message.d.ts +1 -1
  15. package/dist/types/modes/components/message-frame.d.ts +8 -4
  16. package/dist/types/modes/components/session-account-selector.d.ts +11 -0
  17. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  18. package/dist/types/modes/interactive-mode.d.ts +3 -6
  19. package/dist/types/modes/types.d.ts +9 -5
  20. package/dist/types/modes/utils/ui-helpers.d.ts +1 -6
  21. package/dist/types/session/agent-session-types.d.ts +8 -1
  22. package/dist/types/session/agent-session.d.ts +20 -1
  23. package/dist/types/session/auth-storage.d.ts +1 -1
  24. package/dist/types/session/eval-runner.d.ts +2 -0
  25. package/dist/types/session/messages.d.ts +2 -0
  26. package/dist/types/session/session-tools.d.ts +15 -0
  27. package/dist/types/session/streaming-output.d.ts +8 -0
  28. package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
  29. package/dist/types/stt/index.d.ts +0 -2
  30. package/dist/types/stt/stt-controller.d.ts +7 -0
  31. package/dist/types/task/executor.d.ts +3 -1
  32. package/dist/types/task/structured-subagent.d.ts +3 -0
  33. package/dist/types/task/types.d.ts +11 -11
  34. package/dist/types/thinking.d.ts +13 -0
  35. package/dist/types/tiny/title-client.d.ts +10 -0
  36. package/dist/types/tools/builtin-names.d.ts +1 -1
  37. package/dist/types/tools/computer/protocol.d.ts +43 -0
  38. package/dist/types/tools/computer/supervisor.d.ts +32 -0
  39. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  40. package/dist/types/tools/computer/worker.d.ts +15 -0
  41. package/dist/types/tools/computer-renderer.d.ts +22 -0
  42. package/dist/types/tools/computer.d.ts +71 -0
  43. package/dist/types/tools/context.d.ts +2 -0
  44. package/dist/types/tools/default-renderer.d.ts +21 -0
  45. package/dist/types/tools/eval-format/index.d.ts +7 -0
  46. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  47. package/dist/types/tools/eval-format/julia.d.ts +2 -0
  48. package/dist/types/tools/eval-format/python.d.ts +2 -0
  49. package/dist/types/tools/eval-format/ruby.d.ts +2 -0
  50. package/dist/types/tools/index.d.ts +2 -0
  51. package/dist/types/tools/resolve.d.ts +7 -0
  52. package/dist/types/tools/todo.d.ts +4 -1
  53. package/dist/types/tts/streaming-player.d.ts +10 -43
  54. package/dist/types/utils/tools-manager.d.ts +1 -2
  55. package/dist/types/web/search/providers/base.d.ts +16 -0
  56. package/dist/types/web/search/providers/brave.d.ts +2 -0
  57. package/dist/types/web/search/providers/firecrawl.d.ts +2 -0
  58. package/dist/types/web/search/providers/gemini.d.ts +3 -0
  59. package/dist/types/web/search/providers/jina.d.ts +2 -0
  60. package/dist/types/web/search/providers/kagi.d.ts +2 -0
  61. package/dist/types/web/search/providers/kimi.d.ts +2 -0
  62. package/dist/types/web/search/providers/parallel.d.ts +2 -0
  63. package/dist/types/web/search/providers/perplexity.d.ts +3 -0
  64. package/dist/types/web/search/providers/searxng.d.ts +10 -0
  65. package/dist/types/web/search/providers/tavily.d.ts +8 -0
  66. package/dist/types/web/search/query.d.ts +190 -0
  67. package/package.json +12 -12
  68. package/src/cli/args.ts +1 -0
  69. package/src/cli/bench-cli.ts +12 -1
  70. package/src/cli/setup-cli.ts +2 -14
  71. package/src/cli/web-search-cli.ts +7 -0
  72. package/src/cli.ts +8 -0
  73. package/src/config/model-registry.ts +6 -0
  74. package/src/config/model-resolver.ts +17 -6
  75. package/src/config/settings-schema.ts +66 -0
  76. package/src/debug/raw-sse-buffer.ts +157 -30
  77. package/src/eval/executor-base.ts +1 -0
  78. package/src/eval/js/executor.ts +2 -0
  79. package/src/exec/bash-executor.ts +1 -0
  80. package/src/export/share.ts +4 -3
  81. package/src/extensibility/extensions/wrapper.ts +68 -12
  82. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
  83. package/src/launch/broker-list-order.test.ts +89 -0
  84. package/src/launch/broker.ts +49 -8
  85. package/src/live/attestation.ts +91 -0
  86. package/src/live/controller.ts +76 -23
  87. package/src/live/protocol.test.ts +3 -3
  88. package/src/live/protocol.ts +1 -1
  89. package/src/live/transport.ts +72 -140
  90. package/src/live/visualizer.ts +114 -134
  91. package/src/modes/components/assistant-message.ts +7 -2
  92. package/src/modes/components/custom-message.ts +4 -1
  93. package/src/modes/components/message-frame.ts +14 -8
  94. package/src/modes/components/session-account-selector.ts +62 -0
  95. package/src/modes/components/tool-execution.ts +17 -110
  96. package/src/modes/controllers/input-controller.ts +55 -47
  97. package/src/modes/controllers/live-command-controller.ts +82 -5
  98. package/src/modes/controllers/selector-controller.ts +62 -0
  99. package/src/modes/interactive-mode.ts +79 -5
  100. package/src/modes/types.ts +10 -4
  101. package/src/modes/utils/ui-helpers.ts +7 -8
  102. package/src/prompts/system/computer-safety.md +14 -0
  103. package/src/prompts/system/resolve-device-reminder.md +1 -1
  104. package/src/prompts/system/workflow-notice.md +1 -1
  105. package/src/prompts/tools/ast-edit.md +1 -0
  106. package/src/prompts/tools/bash.md +1 -0
  107. package/src/prompts/tools/computer.md +26 -0
  108. package/src/prompts/tools/eval.md +2 -2
  109. package/src/prompts/tools/task.md +5 -2
  110. package/src/prompts/tools/web-search.md +2 -0
  111. package/src/sdk.ts +13 -4
  112. package/src/session/agent-session-types.ts +9 -0
  113. package/src/session/agent-session.ts +70 -0
  114. package/src/session/auth-storage.ts +1 -0
  115. package/src/session/eval-runner.ts +5 -0
  116. package/src/session/messages.ts +3 -0
  117. package/src/session/queued-messages.ts +7 -1
  118. package/src/session/session-tools.ts +37 -0
  119. package/src/session/streaming-output.ts +52 -5
  120. package/src/slash-commands/builtin-registry.ts +165 -9
  121. package/src/slash-commands/helpers/session-pin.ts +44 -0
  122. package/src/stt/downloader.ts +0 -2
  123. package/src/stt/index.ts +0 -2
  124. package/src/stt/stt-controller.ts +57 -146
  125. package/src/system-prompt.ts +4 -0
  126. package/src/task/executor.ts +17 -9
  127. package/src/task/index.ts +14 -34
  128. package/src/task/structured-subagent.ts +4 -0
  129. package/src/task/types.ts +15 -13
  130. package/src/thinking.ts +27 -0
  131. package/src/tiny/title-client.ts +22 -0
  132. package/src/tools/ast-edit.ts +4 -1
  133. package/src/tools/bash-interactive.ts +90 -86
  134. package/src/tools/bash.ts +13 -0
  135. package/src/tools/browser/render.ts +2 -1
  136. package/src/tools/builtin-names.ts +1 -0
  137. package/src/tools/computer/protocol.ts +28 -0
  138. package/src/tools/computer/supervisor.ts +258 -0
  139. package/src/tools/computer/worker-entry.ts +25 -0
  140. package/src/tools/computer/worker.ts +135 -0
  141. package/src/tools/computer-renderer.ts +108 -0
  142. package/src/tools/computer.ts +433 -0
  143. package/src/tools/context.ts +2 -0
  144. package/src/tools/default-renderer.ts +139 -0
  145. package/src/tools/essential-tools.ts +1 -0
  146. package/src/tools/eval-format/index.ts +24 -0
  147. package/src/tools/eval-format/javascript.ts +952 -0
  148. package/src/tools/eval-format/julia.ts +446 -0
  149. package/src/tools/eval-format/python.ts +544 -0
  150. package/src/tools/eval-format/ruby.ts +380 -0
  151. package/src/tools/eval-render.ts +12 -6
  152. package/src/tools/index.ts +5 -0
  153. package/src/tools/renderers.ts +2 -0
  154. package/src/tools/resolve.ts +10 -1
  155. package/src/tools/todo.ts +58 -6
  156. package/src/tools/xdev.ts +54 -26
  157. package/src/tts/streaming-player.ts +81 -340
  158. package/src/utils/clipboard.ts +1 -30
  159. package/src/utils/mac-file-urls.applescript +37 -0
  160. package/src/utils/tool-choice.ts +14 -0
  161. package/src/utils/tools-manager.ts +1 -19
  162. package/src/web/search/index.ts +28 -5
  163. package/src/web/search/providers/anthropic.ts +62 -4
  164. package/src/web/search/providers/base.ts +16 -0
  165. package/src/web/search/providers/brave.ts +30 -3
  166. package/src/web/search/providers/codex.ts +14 -1
  167. package/src/web/search/providers/duckduckgo.ts +23 -1
  168. package/src/web/search/providers/ecosia.ts +5 -1
  169. package/src/web/search/providers/exa.ts +28 -1
  170. package/src/web/search/providers/firecrawl.ts +37 -3
  171. package/src/web/search/providers/gemini.ts +12 -2
  172. package/src/web/search/providers/google.ts +2 -1
  173. package/src/web/search/providers/jina.ts +31 -8
  174. package/src/web/search/providers/kagi.ts +10 -1
  175. package/src/web/search/providers/kimi.ts +16 -1
  176. package/src/web/search/providers/mojeek.ts +14 -1
  177. package/src/web/search/providers/parallel.ts +48 -2
  178. package/src/web/search/providers/perplexity.ts +94 -6
  179. package/src/web/search/providers/searxng.ts +145 -9
  180. package/src/web/search/providers/startpage.ts +8 -2
  181. package/src/web/search/providers/synthetic.ts +7 -1
  182. package/src/web/search/providers/tavily.ts +52 -3
  183. package/src/web/search/providers/tinyfish.ts +6 -1
  184. package/src/web/search/providers/xai.ts +49 -2
  185. package/src/web/search/providers/zai.ts +19 -1
  186. package/src/web/search/query.ts +850 -0
  187. package/dist/types/stt/recorder.d.ts +0 -30
  188. package/dist/types/stt/transcriber.d.ts +0 -14
  189. package/dist/types/stt/wav.d.ts +0 -29
  190. package/dist/types/tts/player.d.ts +0 -32
  191. package/src/live/audio-worklet.txt +0 -59
  192. package/src/live/browser-runtime.txt +0 -221
  193. package/src/stt/recorder.ts +0 -551
  194. package/src/stt/transcriber.ts +0 -60
  195. package/src/stt/wav.ts +0 -173
  196. package/src/tts/player.ts +0 -137
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { type AuthStorage, type FetchImpl } from "@oh-my-pi/pi-ai";
11
11
  import type { SearchResponse } from "../../../web/search/types.js";
12
+ import { type StructuredQuery } from "../query.js";
12
13
  import type { SearchParams } from "./base.js";
13
14
  import { SearchProvider } from "./base.js";
14
15
  type SearchParamsWithFetch = SearchParams & {
@@ -16,6 +17,7 @@ type SearchParamsWithFetch = SearchParams & {
16
17
  };
17
18
  export interface KimiSearchParams {
18
19
  query: string;
20
+ parsedQuery?: StructuredQuery;
19
21
  num_results?: number;
20
22
  include_content?: boolean;
21
23
  signal?: AbortSignal;
@@ -1,5 +1,6 @@
1
1
  import { type AuthStorage, type FetchImpl } from "@oh-my-pi/pi-ai";
2
2
  import type { SearchResponse } from "../../../web/search/types.js";
3
+ import { type StructuredQuery } from "../query.js";
3
4
  import type { SearchParams } from "./base.js";
4
5
  import { SearchProvider } from "./base.js";
5
6
  export declare function searchParallel(params: {
@@ -7,6 +8,7 @@ export declare function searchParallel(params: {
7
8
  num_results?: number;
8
9
  signal?: AbortSignal;
9
10
  fetch?: FetchImpl;
11
+ parsedQuery?: StructuredQuery;
10
12
  }, authStorage: AuthStorage, sessionId?: string): Promise<SearchResponse>;
11
13
  export declare class ParallelProvider extends SearchProvider {
12
14
  readonly id = "parallel";
@@ -9,12 +9,15 @@
9
9
  */
10
10
  import { type AuthStorage, type FetchImpl } from "@oh-my-pi/pi-ai";
11
11
  import type { SearchResponse } from "../../../web/search/types.js";
12
+ import { type StructuredQuery } from "../query.js";
12
13
  import type { SearchParams } from "./base.js";
13
14
  import { SearchProvider } from "./base.js";
14
15
  export interface PerplexitySearchParams {
15
16
  signal?: AbortSignal;
16
17
  query: string;
17
18
  system_prompt?: string;
19
+ /** Pre-parsed view of `query` from the search pipeline; parsed locally when absent. */
20
+ parsedQuery?: StructuredQuery;
18
21
  search_recency_filter?: "hour" | "day" | "week" | "month" | "year";
19
22
  num_results?: number;
20
23
  /** Maximum output tokens. Defaults to 8192. */
@@ -14,6 +14,9 @@
14
14
  * searxng.basicUsername - Optional RFC 7617 Basic auth username
15
15
  * searxng.basicPassword - Optional RFC 7617 Basic auth password
16
16
  * searxng.categories - Optional comma-separated categories filter
17
+ * searxng.engines - Optional comma-separated engine names or shortcuts
18
+ * (e.g. "duckduckgo, br, sp"); shortcuts resolve via
19
+ * the instance's /config endpoint
17
20
  * searxng.language - Optional language code (e.g. en, zh-CN)
18
21
  *
19
22
  * Environment variable fallbacks:
@@ -22,15 +25,22 @@
22
25
  * SEARXNG_BASIC_USERNAME - Optional RFC 7617 Basic auth username
23
26
  * SEARXNG_BASIC_PASSWORD - Optional RFC 7617 Basic auth password
24
27
  *
28
+ * Bang syntax in queries is passed through: `!ddg foo` selects an engine or
29
+ * category server-side and the bang token is stripped from the upstream query.
30
+ * External bangs (`!!g`) are removed client-side because SearXNG answers them
31
+ * with an HTTP redirect even for JSON requests.
32
+ *
25
33
  * Reference: https://docs.searxng.org/dev/search_api.html
26
34
  */
27
35
  import type { AuthStorage, FetchImpl } from "@oh-my-pi/pi-ai";
28
36
  import type { SearchResponse } from "../../../web/search/types.js";
37
+ import type { StructuredQuery } from "../query.js";
29
38
  import type { SearchParams } from "./base.js";
30
39
  import { SearchProvider } from "./base.js";
31
40
  /** Execute SearXNG web search. */
32
41
  export declare function searchSearXNG(params: {
33
42
  query: string;
43
+ parsedQuery?: StructuredQuery;
34
44
  num_results?: number;
35
45
  recency?: "day" | "week" | "month" | "year";
36
46
  signal?: AbortSignal;
@@ -12,6 +12,14 @@ export interface TavilySearchParams {
12
12
  query: string;
13
13
  num_results?: number;
14
14
  recency?: "day" | "week" | "month" | "year";
15
+ /** `site:` hosts mapped to Tavily's `include_domains`. */
16
+ include_domains?: string[];
17
+ /** `-site:` hosts mapped to Tavily's `exclude_domains`. */
18
+ exclude_domains?: string[];
19
+ /** `after:` inclusive lower bound, ISO `YYYY-MM-DD`, mapped to `start_date`. */
20
+ start_date?: string;
21
+ /** `before:` upper bound, ISO `YYYY-MM-DD`, mapped to `end_date`. */
22
+ end_date?: string;
15
23
  signal?: AbortSignal;
16
24
  fetch?: FetchImpl;
17
25
  }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Structured web-search query parsing.
3
+ *
4
+ * Agents habitually embed Google-style directives in search queries —
5
+ * `site:`, `before:`/`after:`, `inurl:`, `filetype:`, quoted phrases, `OR`
6
+ * groups, `-exclusions` — regardless of whether the backing engine parses
7
+ * them. This module turns a raw query into a {@link StructuredQuery} so each
8
+ * provider can:
9
+ *
10
+ * 1. map constraints onto native API parameters where they exist (Perplexity
11
+ * `search_domain_filter`, Tavily `include_domains`, Exa date bounds, …),
12
+ * 2. rebuild a query string containing only the syntax the target engine
13
+ * understands ({@link formatQuery}), and
14
+ * 3. post-filter returned sources leniently ({@link applyQueryConstraints}):
15
+ * a constraint dimension that would eliminate every result is dropped and
16
+ * reported rather than returning nothing.
17
+ */
18
+ import type { SearchSource } from "./types.js";
19
+ /** One free-text token of the query (everything that is not a recognized directive). */
20
+ export interface QueryTerm {
21
+ /** Term text without quotes or operator prefixes. */
22
+ text: string;
23
+ /** Quoted exact phrase (`"like this"`) or verbatim-required (`+term`). */
24
+ phrase?: boolean;
25
+ /** Excluded via `-term` or `NOT term`. */
26
+ negated?: boolean;
27
+ /**
28
+ * OR-group id. Terms sharing an id are alternatives (`a OR b`); terms
29
+ * without a group are implicitly AND-ed. Groups are always contiguous
30
+ * runs in {@link StructuredQuery.terms}.
31
+ */
32
+ group?: number;
33
+ }
34
+ /**
35
+ * A raw query decomposed into free text plus every recognized constraint.
36
+ *
37
+ * All list fields are always present (possibly empty) so consumers can map
38
+ * over them without null checks. Values are stored as typed by the user
39
+ * except for normalization noted per field.
40
+ */
41
+ export interface StructuredQuery {
42
+ /** Original query string, verbatim. */
43
+ raw: string;
44
+ /**
45
+ * Free-text remainder with all recognized directives removed; phrases
46
+ * stay quoted, exclusions keep `-`, OR groups keep `OR`. Empty when the
47
+ * query was directives only — use {@link formatQuery} for a never-empty
48
+ * engine query.
49
+ */
50
+ text: string;
51
+ /** Ordered free-text terms (phrases, exclusions, OR groups). */
52
+ terms: QueryTerm[];
53
+ /** `site:`/`domain:`/`host:` includes — any-of. Lowercased, scheme stripped, may carry a path (`github.com/anthropics`). */
54
+ sites: string[];
55
+ /** `-site:` exclusions, same normalization as {@link sites}. */
56
+ excludedSites: string[];
57
+ /** `inurl:`/`url:`/`allinurl:` substrings — all must appear in the URL. */
58
+ inUrl: string[];
59
+ /** `-inurl:` substrings — none may appear in the URL. */
60
+ excludedInUrl: string[];
61
+ /** `intitle:`/`title:`/`allintitle:` substrings — all must appear in the title. */
62
+ inTitle: string[];
63
+ /** `-intitle:` substrings — none may appear in the title. */
64
+ excludedInTitle: string[];
65
+ /** `intext:`/`inbody:`/`inanchor:`/`allintext:` body substrings. Not post-filterable (snippets are partial); query-building only. */
66
+ inText: string[];
67
+ /** `-intext:` body exclusions. Query-building only. */
68
+ excludedInText: string[];
69
+ /** `filetype:`/`ext:` extensions — any-of. Lowercased, no leading dot. */
70
+ filetypes: string[];
71
+ /** `-filetype:`/`-ext:` extensions — none may match. */
72
+ excludedFiletypes: string[];
73
+ /** Inclusive lower publish-date bound from `after:`/`since:`, ISO `YYYY-MM-DD`. */
74
+ after?: string;
75
+ /** Exclusive upper publish-date bound from `before:`/`until:`, ISO `YYYY-MM-DD`. */
76
+ before?: string;
77
+ /** Language code from `lang:`/`language:`, lowercased (e.g. `en`, `en-us`). */
78
+ lang?: string;
79
+ /** True when any directive or boolean operator was recognized. */
80
+ hasDirectives: boolean;
81
+ /** True when any post-filterable constraint is set (sites, url/title terms, filetypes, date bounds). */
82
+ hasConstraints: boolean;
83
+ }
84
+ /**
85
+ * Query-syntax capabilities of a target engine, used by {@link formatQuery}
86
+ * to decide which parsed features are re-emitted as query text. Everything
87
+ * defaults to `false`: the zero-value produces plain keywords suitable for
88
+ * natural-language APIs.
89
+ */
90
+ export interface QuerySyntax {
91
+ /** Emit `"quoted phrases"`. */
92
+ phrases?: boolean;
93
+ /** Emit `-term` exclusions (negated terms are dropped otherwise). */
94
+ negation?: boolean;
95
+ /** Emit `OR` between alternatives (groups are flattened to keywords otherwise). */
96
+ or?: boolean;
97
+ /** Emit `site:`/`-site:`. */
98
+ site?: boolean;
99
+ /** Emit `inurl:`/`-inurl:`. */
100
+ inUrl?: boolean;
101
+ /** Emit `intitle:`/`-intitle:`. */
102
+ inTitle?: boolean;
103
+ /** Emit `intext:`/`-intext:`. */
104
+ inText?: boolean;
105
+ /** Emit `filetype:`/`-filetype:`. */
106
+ filetype?: boolean;
107
+ /** Emit `before:`/`after:` ISO date bounds. */
108
+ dateRange?: boolean;
109
+ }
110
+ /** Full Google-style syntax: engines that parse the classic operator set (Google, Startpage, Ecosia, Brave, Kagi, Mojeek, SearXNG…). */
111
+ export declare const GOOGLE_QUERY_SYNTAX: QuerySyntax;
112
+ /** Result of {@link applyQueryConstraints}. */
113
+ export interface ConstraintFilterResult {
114
+ /** Sources surviving the lenient filter — never empty when the input was non-empty. */
115
+ sources: SearchSource[];
116
+ /**
117
+ * Directive renderings (`site:arxiv.org`, `before:2024-01-01`, …) of the
118
+ * constraint dimensions that matched zero sources and were therefore
119
+ * relaxed instead of enforced.
120
+ */
121
+ dropped: string[];
122
+ }
123
+ /**
124
+ * Parse a `before:`/`after:` value into ISO `YYYY-MM-DD`.
125
+ * Accepts `YYYY`, `YYYY-MM`, `YYYY-MM-DD` (also `/` and `.` separators) and
126
+ * `MM/DD/YYYY` (day-first assumed when the first field exceeds 12).
127
+ * Bare years/months resolve to the first day of the period, matching
128
+ * Google's `after:2024` ≙ `after:2024-01-01` semantics.
129
+ */
130
+ export declare function parseDateValue(value: string): string | undefined;
131
+ /**
132
+ * Parse a raw query into a {@link StructuredQuery}.
133
+ *
134
+ * Lenient by construction: unknown `name:value` tokens (URLs, `C:\paths`,
135
+ * `TS2345:`, jargon) stay in the free text verbatim, and a directive with an
136
+ * unparseable value (`before:someday`) degrades to a plain term instead of
137
+ * being dropped.
138
+ */
139
+ export declare function parseSearchQuery(raw: string): StructuredQuery;
140
+ /**
141
+ * Rebuild a query string for an engine with the given {@link QuerySyntax}.
142
+ *
143
+ * Constraints whose syntax the engine lacks are omitted (the caller maps
144
+ * them onto API parameters or relies on {@link applyQueryConstraints}).
145
+ * Never returns an empty string for a non-empty input: a directives-only
146
+ * query falls back to the constraint values as keywords, then to `raw` — an
147
+ * engine searching *something* beats an empty-query error.
148
+ */
149
+ export declare function formatQuery(q: StructuredQuery, syntax?: QuerySyntax): string;
150
+ /**
151
+ * Build the engine query for a credential-free HTML engine (Google,
152
+ * Startpage, DuckDuckGo, Ecosia, Mojeek, SearXNG, and the Public Web
153
+ * fan-out over them).
154
+ *
155
+ * Canonicalizes directives via {@link formatQuery} with the engine's
156
+ * {@link QuerySyntax} (default: full Google syntax), after demoting the
157
+ * operators that zero-match across the scraper set: engines only match
158
+ * `site:` against a bare domain (a path yields zero results everywhere),
159
+ * and DuckDuckGo ignores `inurl:` entirely — so either operator silently
160
+ * empties the result set. The raw URL as a plain term matches fine, so
161
+ * bare-domain `site:` filters are kept while path-carrying `site:` and all
162
+ * `inurl:` values become plain keywords; the demotion is structural (before
163
+ * formatting), so OR-grouped and quoted directives are covered. Negated
164
+ * forms (`-site:`, `-inurl:`) pass through untouched — demoting them would
165
+ * invert an exclusion into a search term; the pipeline post-filter
166
+ * ({@link applyQueryConstraints}) enforces every demoted or unsupported
167
+ * constraint on the returned sources. Directive-free queries pass through
168
+ * byte-identical.
169
+ */
170
+ export declare function formatScraperQuery(query: string, parsedQuery?: StructuredQuery, syntax?: QuerySyntax): string;
171
+ /**
172
+ * `site:` matcher: exact host or subdomain of `site`; when `site` carries a
173
+ * path (`github.com/anthropics`), the URL path must start with it.
174
+ */
175
+ export declare function matchesSite(url: string, site: string): boolean;
176
+ /**
177
+ * Strict per-source constraint check: every filterable dimension of `q` must
178
+ * pass. Sources without a resolvable date pass date bounds (a missing date
179
+ * is not proof of violation). For custom provider flows; the standard path
180
+ * is {@link applyQueryConstraints}.
181
+ */
182
+ export declare function matchesQueryConstraints(source: SearchSource, q: StructuredQuery): boolean;
183
+ /**
184
+ * Lenient post-filter: applies each constraint dimension of `q` in turn,
185
+ * skipping (and reporting) any dimension that would eliminate every
186
+ * remaining source. Guarantees a non-empty result for a non-empty input, so
187
+ * a mis-scoped directive degrades to unfiltered results plus a note instead
188
+ * of a dead search.
189
+ */
190
+ export declare function applyQueryConstraints(sources: readonly SearchSource[], q: StructuredQuery): ConstraintFilterResult;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "17.1.0",
4
+ "version": "17.1.2",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -53,17 +53,17 @@
53
53
  "@babel/parser": "^7.29.7",
54
54
  "@babel/traverse": "^7.29.7",
55
55
  "@mozilla/readability": "^0.6.0",
56
- "@oh-my-pi/hashline": "17.1.0",
57
- "@oh-my-pi/omp-stats": "17.1.0",
58
- "@oh-my-pi/pi-agent-core": "17.1.0",
59
- "@oh-my-pi/pi-ai": "17.1.0",
60
- "@oh-my-pi/pi-catalog": "17.1.0",
61
- "@oh-my-pi/pi-mnemopi": "17.1.0",
62
- "@oh-my-pi/pi-natives": "17.1.0",
63
- "@oh-my-pi/pi-tui": "17.1.0",
64
- "@oh-my-pi/pi-utils": "17.1.0",
65
- "@oh-my-pi/pi-wire": "17.1.0",
66
- "@oh-my-pi/snapcompact": "17.1.0",
56
+ "@oh-my-pi/hashline": "17.1.2",
57
+ "@oh-my-pi/omp-stats": "17.1.2",
58
+ "@oh-my-pi/pi-agent-core": "17.1.2",
59
+ "@oh-my-pi/pi-ai": "17.1.2",
60
+ "@oh-my-pi/pi-catalog": "17.1.2",
61
+ "@oh-my-pi/pi-mnemopi": "17.1.2",
62
+ "@oh-my-pi/pi-natives": "17.1.2",
63
+ "@oh-my-pi/pi-tui": "17.1.2",
64
+ "@oh-my-pi/pi-utils": "17.1.2",
65
+ "@oh-my-pi/pi-wire": "17.1.2",
66
+ "@oh-my-pi/snapcompact": "17.1.2",
67
67
  "@opentelemetry/api": "^1.9.1",
68
68
  "@opentelemetry/api-logs": "^0.220.0",
69
69
  "@opentelemetry/context-async-hooks": "^2.9.0",
package/src/cli/args.ts CHANGED
@@ -396,6 +396,7 @@ ${chalk.bold("Available Tools (default-enabled unless noted):")}
396
396
  notebook - Edit Jupyter notebooks
397
397
  inspect_image - Analyze images with a vision model
398
398
  browser - Browser automation (Puppeteer)
399
+ computer - Native host desktop capture and input (disabled by default)
399
400
  task - Launch sub-agents for parallel tasks
400
401
  todo - Manage todo/task lists
401
402
  web_search - Search the web
@@ -76,6 +76,7 @@ export interface BenchCommandArgs {
76
76
 
77
77
  export interface BenchModelRegistry {
78
78
  getAll(): Model<Api>[];
79
+ getAvailable(): Model<Api>[];
79
80
  getApiKey(model: Model<Api>, sessionId?: string): Promise<string | undefined>;
80
81
  resolver(model: ApiKeyResolverModel, sessionId?: string): ApiKeyResolver;
81
82
  hasConfiguredAuth?(model: Model<Api>): boolean;
@@ -693,7 +694,17 @@ function resolveBenchModels(
693
694
  const resolved: BenchTarget[] = [];
694
695
  const errors: string[] = [];
695
696
  for (const selector of selectors) {
696
- const result = resolveCliModel({ cliModel: selector, modelRegistry, settings, preferences });
697
+ // Bench intentionally resolves against the full catalog first, then applies
698
+ // its own exact-id credential fallback below. Using the CLI resolver's
699
+ // authenticated default here would silently redirect non-equivalent bare
700
+ // ids and suppress the warning for equivalent cross-provider models.
701
+ const result = resolveCliModel({
702
+ cliModel: selector,
703
+ modelRegistry,
704
+ availableModels: modelRegistry.getAll(),
705
+ settings,
706
+ preferences,
707
+ });
697
708
  if (result.error) {
698
709
  errors.push(`${selector}: ${result.error}`);
699
710
  continue;
@@ -11,7 +11,6 @@ import { Settings, settings } from "../config/settings";
11
11
  import { theme } from "../modes/theme/theme";
12
12
  import { downloadSttModel, isSttModelCached } from "../stt/downloader";
13
13
  import { isSttModelKey, STT_MODEL_OPTIONS } from "../stt/models";
14
- import { detectRecorder, ensureRecorder } from "../stt/recorder";
15
14
  import { downloadTtsModel, isTtsLocalModelKey, isTtsModelCached, TTS_LOCAL_MODEL_OPTIONS } from "../tts";
16
15
  import { selectSetupModel } from "./setup-model-picker";
17
16
 
@@ -170,17 +169,6 @@ interface SpeechComponent {
170
169
 
171
170
  function buildSpeechComponents(): SpeechComponent[] {
172
171
  return [
173
- {
174
- name: "Recorder",
175
- isReady: async () => detectRecorder() !== null,
176
- status: async () => {
177
- const recorder = detectRecorder();
178
- return recorder ? `${recorder.tool} (${recorder.bin})` : "none — ffmpeg will be downloaded";
179
- },
180
- ensure: async onProgress => {
181
- await ensureRecorder(onProgress);
182
- },
183
- },
184
172
  {
185
173
  name: "Speech-to-Text model",
186
174
  isReady: () => isSttModelCached(settings.get("stt.modelName")),
@@ -316,7 +304,7 @@ ${chalk.bold("Usage:")}
316
304
 
317
305
  ${chalk.bold("Components:")}
318
306
  python Verify a Python 3 interpreter is reachable for code execution
319
- speech Pick + download the speech-to-text and text-to-speech models and an audio recorder
307
+ speech Pick and download speech-to-text and text-to-speech models
320
308
 
321
309
  ${chalk.bold("Options:")}
322
310
  -c, --check Check if dependencies are installed without installing
@@ -325,7 +313,7 @@ ${chalk.bold("Options:")}
325
313
  ${chalk.bold("Examples:")}
326
314
  ${APP_NAME} setup Run the onboarding wizard
327
315
  ${APP_NAME} setup python Check Python execution dependencies
328
- ${APP_NAME} setup speech Set up speech (pick STT + TTS models, install a recorder)
316
+ ${APP_NAME} setup speech Pick and download the STT and TTS models
329
317
  ${APP_NAME} setup speech --check Check if speech dependencies are available
330
318
  ${APP_NAME} setup python --check Check if Python execution is available
331
319
  `);
@@ -130,8 +130,15 @@ ${chalk.bold("Options:")}
130
130
  --compact Render condensed output
131
131
  -h, --help Show this help
132
132
 
133
+ ${chalk.bold("Query directives:")}
134
+ site:/-site: after:/before: (YYYY-MM-DD) inurl: intitle: filetype:
135
+ "exact phrase" -term OR
136
+ Mapped to native provider filters where available, otherwise applied as a
137
+ lenient post-filter (a constraint matching nothing is relaxed, not fatal).
138
+
133
139
  ${chalk.bold("Examples:")}
134
140
  ${APP_NAME} q --provider=exa "what's the color of the sky"
135
141
  ${APP_NAME} q --provider=brave --recency=week "latest TypeScript 5.7 changes"
142
+ ${APP_NAME} q 'transformer scaling site:arxiv.org after:2024 -site:reddit.com'
136
143
  `);
137
144
  }
package/src/cli.ts CHANGED
@@ -79,6 +79,7 @@ async function runSmokeTest(): Promise<void> {
79
79
  const { smokeTestTtsWorker } = await import("./tts/tts-client");
80
80
  const { smokeTestMnemopiEmbedWorker } = await import("./mnemopi/embed-client");
81
81
  const { smokeTestJsEvalWorker } = await import("./eval/js/context-manager");
82
+ const { smokeTestComputerWorker } = await import("./tools/computer/supervisor");
82
83
  // Smoke dependencies stay lazy so normal CLI startup does not load worker clients.
83
84
  const { smokeTestDaemonBroker } = await import("./launch/client");
84
85
  await smokeTestSyncWorker();
@@ -98,6 +99,7 @@ async function runSmokeTest(): Promise<void> {
98
99
  await smokeTestTinyTitleWorker();
99
100
  await smokeTestSttWorker();
100
101
  await smokeTestJsEvalWorker();
102
+ await smokeTestComputerWorker();
101
103
  await smokeTestTtsWorker();
102
104
  await smokeTestMnemopiEmbedWorker();
103
105
  await smokeTestDaemonBroker();
@@ -107,6 +109,7 @@ async function runSmokeTest(): Promise<void> {
107
109
  const TINY_WORKER_ARG = "__omp_worker_tiny_inference";
108
110
  const STATS_SYNC_WORKER_ARG = "__omp_worker_stats_sync";
109
111
  const TAB_WORKER_ARG = "__omp_worker_tab";
112
+ const COMPUTER_WORKER_ARG = "__omp_worker_computer";
110
113
  const JS_EVAL_WORKER_ARG = "__omp_worker_js_eval";
111
114
  const JS_EVAL_PROCESS_ARG = "__omp_worker_js_eval_process";
112
115
  const STT_WORKER_ARG = "__omp_worker_stt";
@@ -152,6 +155,11 @@ async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
152
155
  await import("./tools/browser/tab-worker-entry");
153
156
  return true;
154
157
  }
158
+ if (arg === COMPUTER_WORKER_ARG) {
159
+ if (parentPort) installWorkerInbox(parentPort);
160
+ await import("./tools/computer/worker-entry");
161
+ return true;
162
+ }
155
163
  if (arg === JS_EVAL_WORKER_ARG) {
156
164
  if (parentPort) installWorkerInbox(parentPort);
157
165
  await import("./eval/js/worker-entry");
@@ -65,6 +65,7 @@ const BUILT_IN_DISCOVERY_NON_AUTHORITATIVE_RETRY_MS = 5 * 60 * 1000;
65
65
  import type { ApiKeyResolver, FetchImpl } from "@oh-my-pi/pi-ai";
66
66
  import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
67
67
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
68
+ import { setCodexAttestationProvider } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
68
69
  import {
69
70
  getBundledModelReferenceIndex,
70
71
  inheritReferenceThinking,
@@ -72,6 +73,7 @@ import {
72
73
  } from "@oh-my-pi/pi-catalog/identity";
73
74
  import { isBunTestRuntime, isRecord, logger, wrapFetchForExtraCa } from "@oh-my-pi/pi-utils";
74
75
  import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
76
+ import { generateCodexAttestation } from "../live/attestation";
75
77
  import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
76
78
  import { type ApiKeyResolverModel, type ApiKeyResolverOptions, createApiKeyResolver } from "./api-key-resolver";
77
79
  import type { ConfigError, ConfigFile } from "./config-file";
@@ -90,6 +92,10 @@ import { ModelsConfigFile, type ProviderValidationModel, validateProviderConfigu
90
92
  import type { ModelOverride, ModelsConfig, ProviderAuthMode } from "./models-config-schema";
91
93
  import { settings } from "./settings";
92
94
 
95
+ // DeviceCheck attestation (`x-oai-attestation`) for ChatGPT-OAuth Codex
96
+ // requests; the pi-ai provider resolves it just-in-time per request.
97
+ setCodexAttestationProvider(generateCodexAttestation);
98
+
93
99
  export const kNoAuth = "N/A";
94
100
 
95
101
  export function isAuthenticated(apiKey: string | undefined | null): apiKey is string {
@@ -466,7 +466,7 @@ export interface ModelMatchPreferences {
466
466
  }
467
467
 
468
468
  export type ModelLookupRegistry = Pick<ModelRegistry, "getAvailable">;
469
- type CliModelRegistry = Pick<ModelRegistry, "getAll">;
469
+ type CliModelRegistry = Pick<ModelRegistry, "getAll" | "getAvailable">;
470
470
  type InitialModelRegistry = Pick<ModelRegistry, "getAvailable" | "find">;
471
471
  type RestorableModelRegistry = Pick<ModelRegistry, "getAvailable" | "find" | "getApiKey">;
472
472
 
@@ -1601,6 +1601,7 @@ function findExactCliModel(
1601
1601
  selector: string,
1602
1602
  allModels: Model<Api>[],
1603
1603
  availableModels: Model<Api>[],
1604
+ options?: { catalogFallback?: boolean },
1604
1605
  ): Model<Api> | undefined {
1605
1606
  // Explicit provider/id references stay authoritative against the full catalog.
1606
1607
  const referenced = findExactModelReferenceMatch(selector, allModels);
@@ -1615,6 +1616,13 @@ function findExactCliModel(
1615
1616
  model.id.toLowerCase() === lower || formatModelString(model).toLowerCase() === lower;
1616
1617
  const preferred = availableModels.find(isFlatMatch);
1617
1618
  if (preferred) return preferred;
1619
+ // The unauthenticated catalog fallback is a weak match: a bare id like
1620
+ // `default` collides with the bundled `cursor/default` model, which must not
1621
+ // shadow a configured `modelRoles.default` role the user can actually run.
1622
+ // Callers resolving a possible role name pass `catalogFallback: false` so the
1623
+ // role gets a chance first; the deferred fuzzy fallback below still recovers
1624
+ // the catalog id when no role matches.
1625
+ if (options?.catalogFallback === false) return undefined;
1618
1626
  return availableModels === allModels ? undefined : allModels.find(isFlatMatch);
1619
1627
  }
1620
1628
 
@@ -1635,13 +1643,16 @@ export interface ResolveCliModelResult {
1635
1643
  /**
1636
1644
  * Resolve a single model from CLI flags.
1637
1645
  *
1638
- * Exact model names take precedence over configured role names.
1646
+ * Explicit `provider/id` references and authenticated bare ids take precedence
1647
+ * over configured role names, which in turn take precedence over an
1648
+ * unauthenticated catalog-only id (so a bundled `cursor/default` never shadows a
1649
+ * configured `modelRoles.default`).
1639
1650
  */
1640
1651
  export function resolveCliModel(options: {
1641
1652
  cliProvider?: string;
1642
1653
  cliModel?: string;
1643
1654
  modelRegistry: CliModelRegistry;
1644
- /** Authenticated models to prefer for unqualified selectors; omit to preserve catalog-order behavior. */
1655
+ /** Authenticated models to prefer for unqualified selectors; defaults to the registry's authenticated set. */
1645
1656
  availableModels?: Model<Api>[];
1646
1657
  settings?: Settings;
1647
1658
  preferences?: ModelMatchPreferences;
@@ -1662,7 +1673,7 @@ export function resolveCliModel(options: {
1662
1673
  };
1663
1674
  }
1664
1675
 
1665
- const availableModels = preferredModels ?? allModels;
1676
+ const availableModels = preferredModels ?? modelRegistry.getAvailable();
1666
1677
  const providerMap = new Map<string, string>();
1667
1678
  for (const model of allModels) {
1668
1679
  providerMap.set(model.provider.toLowerCase(), model.provider);
@@ -1680,7 +1691,7 @@ export function resolveCliModel(options: {
1680
1691
 
1681
1692
  const trimmedModel = cliModel.trim();
1682
1693
  if (!provider) {
1683
- const exact = findExactCliModel(trimmedModel, allModels, availableModels);
1694
+ const exact = findExactCliModel(trimmedModel, allModels, availableModels, { catalogFallback: false });
1684
1695
  if (exact) {
1685
1696
  return {
1686
1697
  model: exact,
@@ -1696,7 +1707,7 @@ export function resolveCliModel(options: {
1696
1707
  MAX_THINKING_SUFFIX_OPTIONS,
1697
1708
  );
1698
1709
  if (exactThinkingLevel) {
1699
- const exactSuffixed = findExactCliModel(exactBase, allModels, availableModels);
1710
+ const exactSuffixed = findExactCliModel(exactBase, allModels, availableModels, { catalogFallback: false });
1700
1711
  if (exactSuffixed) {
1701
1712
  return {
1702
1713
  model: exactSuffixed,
@@ -141,6 +141,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
141
141
  "Available Tools",
142
142
  "Todos",
143
143
  "Grep & Browser",
144
+ "Computer",
144
145
  "GitHub",
145
146
  "Output Limits",
146
147
  "Execution",
@@ -3830,6 +3831,66 @@ export const SETTINGS_SCHEMA = {
3830
3831
  },
3831
3832
  },
3832
3833
 
3834
+ "computer.enabled": {
3835
+ type: "boolean",
3836
+ default: false,
3837
+ ui: {
3838
+ tab: "tools",
3839
+ group: "Available Tools",
3840
+ label: "Computer",
3841
+ description: "Enable native host-desktop screenshots and input for OpenAI computer use",
3842
+ },
3843
+ },
3844
+
3845
+ "computer.backend": {
3846
+ type: "enum",
3847
+ values: ["auto", "native"] as const,
3848
+ default: "auto",
3849
+ ui: {
3850
+ tab: "tools",
3851
+ group: "Computer",
3852
+ label: "Computer Backend",
3853
+ description: "Select automatic or explicit platform-native desktop capture and input",
3854
+ options: [
3855
+ { value: "auto", label: "Auto" },
3856
+ { value: "native", label: "Native" },
3857
+ ],
3858
+ },
3859
+ },
3860
+
3861
+ "computer.display": {
3862
+ type: "string",
3863
+ default: "all",
3864
+ ui: {
3865
+ tab: "tools",
3866
+ group: "Computer",
3867
+ label: "Computer Display",
3868
+ description: "Composite all displays or select a native display id",
3869
+ },
3870
+ },
3871
+
3872
+ "computer.maxWidth": {
3873
+ type: "number",
3874
+ default: 1920,
3875
+ ui: {
3876
+ tab: "tools",
3877
+ group: "Computer",
3878
+ label: "Computer Screenshot Width",
3879
+ description: "Maximum composite screenshot width in pixels",
3880
+ },
3881
+ },
3882
+
3883
+ "computer.maxHeight": {
3884
+ type: "number",
3885
+ default: 1200,
3886
+ ui: {
3887
+ tab: "tools",
3888
+ group: "Computer",
3889
+ label: "Computer Screenshot Height",
3890
+ description: "Maximum composite screenshot height in pixels",
3891
+ },
3892
+ },
3893
+
3833
3894
  "checkpoint.enabled": {
3834
3895
  type: "boolean",
3835
3896
  default: false,
@@ -5205,6 +5266,11 @@ export const SETTINGS_SCHEMA = {
5205
5266
  default: undefined,
5206
5267
  },
5207
5268
 
5269
+ "searxng.engines": {
5270
+ type: "string",
5271
+ default: undefined,
5272
+ },
5273
+
5208
5274
  "searxng.language": {
5209
5275
  type: "string",
5210
5276
  default: undefined,