@oh-my-pi/pi-coding-agent 17.1.1 → 17.1.3

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 (105) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/cli.js +6317 -4085
  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 +4 -0
  6. package/dist/types/launch/broker-list-order.test.d.ts +1 -0
  7. package/dist/types/modes/interactive-mode.d.ts +2 -6
  8. package/dist/types/modes/types.d.ts +8 -5
  9. package/dist/types/modes/utils/ui-helpers.d.ts +1 -6
  10. package/dist/types/task/executor.d.ts +3 -1
  11. package/dist/types/task/structured-subagent.d.ts +3 -0
  12. package/dist/types/task/types.d.ts +11 -11
  13. package/dist/types/thinking.d.ts +13 -0
  14. package/dist/types/tools/auto-generated-guard.d.ts +5 -2
  15. package/dist/types/tools/eval-format/index.d.ts +7 -0
  16. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  17. package/dist/types/tools/eval-format/julia.d.ts +2 -0
  18. package/dist/types/tools/eval-format/python.d.ts +2 -0
  19. package/dist/types/tools/eval-format/ruby.d.ts +2 -0
  20. package/dist/types/tools/memory-render.d.ts +1 -4
  21. package/dist/types/tools/resolve.d.ts +7 -0
  22. package/dist/types/tools/todo.d.ts +4 -1
  23. package/dist/types/web/search/providers/base.d.ts +16 -0
  24. package/dist/types/web/search/providers/brave.d.ts +2 -0
  25. package/dist/types/web/search/providers/firecrawl.d.ts +2 -0
  26. package/dist/types/web/search/providers/gemini.d.ts +3 -0
  27. package/dist/types/web/search/providers/jina.d.ts +2 -0
  28. package/dist/types/web/search/providers/kagi.d.ts +2 -0
  29. package/dist/types/web/search/providers/kimi.d.ts +2 -0
  30. package/dist/types/web/search/providers/parallel.d.ts +2 -0
  31. package/dist/types/web/search/providers/perplexity.d.ts +3 -0
  32. package/dist/types/web/search/providers/searxng.d.ts +10 -0
  33. package/dist/types/web/search/providers/tavily.d.ts +8 -0
  34. package/dist/types/web/search/query.d.ts +190 -0
  35. package/package.json +12 -12
  36. package/src/cli/bench-cli.ts +12 -1
  37. package/src/cli/web-search-cli.ts +7 -0
  38. package/src/config/model-resolver.ts +17 -6
  39. package/src/config/settings-schema.ts +5 -0
  40. package/src/debug/raw-sse-buffer.ts +157 -30
  41. package/src/edit/hashline/filesystem.ts +1 -1
  42. package/src/edit/modes/patch.ts +1 -1
  43. package/src/export/share.ts +4 -3
  44. package/src/launch/broker-list-order.test.ts +89 -0
  45. package/src/launch/broker.ts +49 -8
  46. package/src/modes/controllers/input-controller.ts +8 -8
  47. package/src/modes/interactive-mode.ts +60 -5
  48. package/src/modes/types.ts +9 -4
  49. package/src/modes/utils/ui-helpers.ts +7 -8
  50. package/src/prompts/system/resolve-device-reminder.md +1 -1
  51. package/src/prompts/system/workflow-notice.md +1 -1
  52. package/src/prompts/tools/ast-edit.md +2 -1
  53. package/src/prompts/tools/bash.md +1 -0
  54. package/src/prompts/tools/eval.md +2 -2
  55. package/src/prompts/tools/task.md +5 -2
  56. package/src/prompts/tools/web-search.md +2 -0
  57. package/src/sdk.ts +8 -4
  58. package/src/session/agent-session.ts +14 -0
  59. package/src/session/queued-messages.ts +7 -1
  60. package/src/session/stream-guards.ts +1 -1
  61. package/src/task/executor.ts +17 -9
  62. package/src/task/index.ts +14 -34
  63. package/src/task/structured-subagent.ts +4 -0
  64. package/src/task/types.ts +15 -13
  65. package/src/thinking.ts +27 -0
  66. package/src/tools/ast-edit.ts +4 -1
  67. package/src/tools/auto-generated-guard.ts +18 -5
  68. package/src/tools/bash.ts +13 -0
  69. package/src/tools/browser/render.ts +2 -1
  70. package/src/tools/eval-format/index.ts +24 -0
  71. package/src/tools/eval-format/javascript.ts +952 -0
  72. package/src/tools/eval-format/julia.ts +446 -0
  73. package/src/tools/eval-format/python.ts +544 -0
  74. package/src/tools/eval-format/ruby.ts +380 -0
  75. package/src/tools/eval-render.ts +12 -6
  76. package/src/tools/memory-render.ts +11 -2
  77. package/src/tools/render-utils.ts +8 -3
  78. package/src/tools/resolve.ts +10 -1
  79. package/src/tools/todo.ts +58 -6
  80. package/src/tools/write.ts +1 -1
  81. package/src/web/search/index.ts +28 -5
  82. package/src/web/search/providers/anthropic.ts +62 -4
  83. package/src/web/search/providers/base.ts +16 -0
  84. package/src/web/search/providers/brave.ts +30 -3
  85. package/src/web/search/providers/codex.ts +14 -1
  86. package/src/web/search/providers/duckduckgo.ts +23 -1
  87. package/src/web/search/providers/ecosia.ts +5 -1
  88. package/src/web/search/providers/exa.ts +28 -1
  89. package/src/web/search/providers/firecrawl.ts +37 -3
  90. package/src/web/search/providers/gemini.ts +12 -2
  91. package/src/web/search/providers/google.ts +2 -1
  92. package/src/web/search/providers/jina.ts +31 -8
  93. package/src/web/search/providers/kagi.ts +10 -1
  94. package/src/web/search/providers/kimi.ts +16 -1
  95. package/src/web/search/providers/mojeek.ts +14 -1
  96. package/src/web/search/providers/parallel.ts +48 -2
  97. package/src/web/search/providers/perplexity.ts +94 -6
  98. package/src/web/search/providers/searxng.ts +145 -9
  99. package/src/web/search/providers/startpage.ts +8 -2
  100. package/src/web/search/providers/synthetic.ts +7 -1
  101. package/src/web/search/providers/tavily.ts +52 -3
  102. package/src/web/search/providers/tinyfish.ts +6 -1
  103. package/src/web/search/providers/xai.ts +49 -2
  104. package/src/web/search/providers/zai.ts +19 -1
  105. package/src/web/search/query.ts +850 -0
@@ -0,0 +1,446 @@
1
+ const INDENT = " ";
2
+
3
+ const BLOCK_OPENERS: Record<string, true> = {
4
+ function: true,
5
+ macro: true,
6
+ struct: true,
7
+ if: true,
8
+ for: true,
9
+ while: true,
10
+ let: true,
11
+ begin: true,
12
+ quote: true,
13
+ try: true,
14
+ module: true,
15
+ baremodule: true,
16
+ do: true,
17
+ };
18
+
19
+ const BRANCH_CLAUSES: Record<string, true> = {
20
+ else: true,
21
+ elseif: true,
22
+ catch: true,
23
+ finally: true,
24
+ };
25
+
26
+ const EXPRESSION_PREFIX_WORDS: Record<string, true> = {
27
+ baremodule: true,
28
+ begin: true,
29
+ catch: true,
30
+ const: true,
31
+ do: true,
32
+ else: true,
33
+ elseif: true,
34
+ finally: true,
35
+ for: true,
36
+ function: true,
37
+ global: true,
38
+ if: true,
39
+ in: true,
40
+ isa: true,
41
+ let: true,
42
+ local: true,
43
+ macro: true,
44
+ module: true,
45
+ mutable: true,
46
+ quote: true,
47
+ return: true,
48
+ struct: true,
49
+ throw: true,
50
+ try: true,
51
+ where: true,
52
+ while: true,
53
+ };
54
+
55
+ type QuoteKind = "double" | "triple" | "command" | "char";
56
+
57
+ interface QuoteFrame {
58
+ type: "quote";
59
+ kind: QuoteKind;
60
+ escaped: boolean;
61
+ }
62
+
63
+ interface InterpolationFrame {
64
+ type: "interpolation";
65
+ closers: string[];
66
+ canEndExpression: boolean;
67
+ }
68
+
69
+ type LiteralFrame = QuoteFrame | InterpolationFrame;
70
+
71
+ type PrefixToken = "none" | "dot" | "colon" | "at" | "other";
72
+
73
+ function isIdentifierStart(character: string): boolean {
74
+ const code = character.charCodeAt(0);
75
+ return character === "_" || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code >= 0x80;
76
+ }
77
+
78
+ function isIdentifierContinue(character: string): boolean {
79
+ const code = character.charCodeAt(0);
80
+ return isIdentifierStart(character) || (code >= 48 && code <= 57) || character === "!" || character === "?";
81
+ }
82
+
83
+ function isHorizontalWhitespace(character: string): boolean {
84
+ return character === " " || character === "\t" || character === "\v" || character === "\f";
85
+ }
86
+
87
+ function isExpressionSeparator(character: string): boolean {
88
+ return (
89
+ character === "=" ||
90
+ character === "," ||
91
+ character === ";" ||
92
+ character === ":" ||
93
+ character === "." ||
94
+ character === "@" ||
95
+ character === "+" ||
96
+ character === "-" ||
97
+ character === "*" ||
98
+ character === "/" ||
99
+ character === "\\" ||
100
+ character === "%" ||
101
+ character === "^" ||
102
+ character === "&" ||
103
+ character === "|" ||
104
+ character === "<" ||
105
+ character === ">" ||
106
+ character === "~"
107
+ );
108
+ }
109
+
110
+ function consumeLineComment(source: string, start: number): number {
111
+ let index = start + 1;
112
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") index++;
113
+ return index;
114
+ }
115
+
116
+ function consumeBlockComment(source: string, start: number): number {
117
+ let depth = 1;
118
+ let index = start + 2;
119
+
120
+ while (index < source.length) {
121
+ if (source[index] === "#" && source[index + 1] === "=") {
122
+ depth++;
123
+ index += 2;
124
+ continue;
125
+ }
126
+ if (source[index] === "=" && source[index + 1] === "#") {
127
+ depth--;
128
+ index += 2;
129
+ if (depth === 0) return index;
130
+ continue;
131
+ }
132
+ index++;
133
+ }
134
+
135
+ return source.length;
136
+ }
137
+
138
+ function quoteWidth(kind: QuoteKind): number {
139
+ return kind === "triple" ? 3 : 1;
140
+ }
141
+
142
+ function quoteCloses(source: string, index: number, kind: QuoteKind): boolean {
143
+ if (kind === "triple") {
144
+ return source[index] === '"' && source[index + 1] === '"' && source[index + 2] === '"';
145
+ }
146
+ if (kind === "double") return source[index] === '"';
147
+ if (kind === "command") return source[index] === "`";
148
+ return source[index] === "'";
149
+ }
150
+
151
+ function pushQuote(frames: LiteralFrame[], kind: QuoteKind): void {
152
+ frames.push({ type: "quote", kind, escaped: false });
153
+ }
154
+
155
+ /**
156
+ * Finds the end of a quoted literal while treating interpolation as opaque code.
157
+ * The small lexer is deliberately independent from display layout: its only job
158
+ * is to keep separators and block words inside a literal out of the formatter.
159
+ */
160
+ function consumeQuotedLiteral(source: string, start: number, kind: QuoteKind): number {
161
+ const frames: LiteralFrame[] = [];
162
+ pushQuote(frames, kind);
163
+ let index = start + quoteWidth(kind);
164
+
165
+ while (index < source.length) {
166
+ const frame = frames.at(-1);
167
+ if (!frame) return index;
168
+
169
+ if (frame.type === "quote") {
170
+ const character = source[index];
171
+ if (frame.escaped) {
172
+ frame.escaped = false;
173
+ index++;
174
+ continue;
175
+ }
176
+ if (character === "\\") {
177
+ frame.escaped = true;
178
+ index++;
179
+ continue;
180
+ }
181
+ if (frame.kind !== "char" && character === "$" && source[index + 1] === "(") {
182
+ frames.push({ type: "interpolation", closers: [")"], canEndExpression: false });
183
+ index += 2;
184
+ continue;
185
+ }
186
+ if (quoteCloses(source, index, frame.kind)) {
187
+ index += quoteWidth(frame.kind);
188
+ frames.pop();
189
+ const parent = frames.at(-1);
190
+ if (!parent) return index;
191
+ if (parent.type === "interpolation") parent.canEndExpression = true;
192
+ continue;
193
+ }
194
+ index++;
195
+ continue;
196
+ }
197
+
198
+ const character = source[index];
199
+ if (character === "#") {
200
+ index = source[index + 1] === "=" ? consumeBlockComment(source, index) : consumeLineComment(source, index);
201
+ continue;
202
+ }
203
+ if (character === '"') {
204
+ const nestedKind = source.startsWith('"""', index) ? "triple" : "double";
205
+ pushQuote(frames, nestedKind);
206
+ index += quoteWidth(nestedKind);
207
+ continue;
208
+ }
209
+ if (character === "`") {
210
+ pushQuote(frames, "command");
211
+ index++;
212
+ continue;
213
+ }
214
+ if (character === "'") {
215
+ if (frame.canEndExpression) {
216
+ index++;
217
+ continue;
218
+ }
219
+ pushQuote(frames, "char");
220
+ index++;
221
+ continue;
222
+ }
223
+ if (isIdentifierStart(character)) {
224
+ const wordStart = index;
225
+ index++;
226
+ while (index < source.length && isIdentifierContinue(source[index])) index++;
227
+ frame.canEndExpression = EXPRESSION_PREFIX_WORDS[source.slice(wordStart, index)] !== true;
228
+ continue;
229
+ }
230
+ if (character === "(" || character === "[" || character === "{") {
231
+ frame.closers.push(character === "(" ? ")" : character === "[" ? "]" : "}");
232
+ frame.canEndExpression = false;
233
+ index++;
234
+ continue;
235
+ }
236
+ if (character === ")" || character === "]" || character === "}") {
237
+ if (frame.closers.at(-1) === character) {
238
+ frame.closers.pop();
239
+ index++;
240
+ if (frame.closers.length === 0) frames.pop();
241
+ else frame.canEndExpression = true;
242
+ continue;
243
+ }
244
+ frame.canEndExpression = true;
245
+ index++;
246
+ continue;
247
+ }
248
+ if (character === "\n" || character === "\r" || isExpressionSeparator(character)) {
249
+ frame.canEndExpression = false;
250
+ index++;
251
+ continue;
252
+ }
253
+ if (!isHorizontalWhitespace(character)) frame.canEndExpression = true;
254
+ index++;
255
+ }
256
+
257
+ return source.length;
258
+ }
259
+
260
+ /** Formats an arbitrary Julia source prefix for stable, readable display. */
261
+ export function formatJuliaForDisplay(source: string): string {
262
+ const output: string[] = [];
263
+ const delimiterClosers: string[] = [];
264
+ let index = 0;
265
+ let blockDepth = 0;
266
+ let atLineStart = true;
267
+ let pendingWhitespace = "";
268
+ let suppressSourceNewline = false;
269
+ let canEndExpression = false;
270
+ let previousToken: PrefixToken = "none";
271
+
272
+ function beginContent(dedentBlock: boolean, dedentDelimiter: boolean): void {
273
+ if (atLineStart) {
274
+ pendingWhitespace = "";
275
+ const indentation = Math.max(
276
+ 0,
277
+ blockDepth - (dedentBlock ? 1 : 0) + delimiterClosers.length - (dedentDelimiter ? 1 : 0),
278
+ );
279
+ if (indentation > 0) output.push(INDENT.repeat(indentation));
280
+ atLineStart = false;
281
+ } else if (pendingWhitespace.length > 0) {
282
+ output.push(pendingWhitespace);
283
+ pendingWhitespace = "";
284
+ }
285
+ suppressSourceNewline = false;
286
+ }
287
+
288
+ function appendOpaque(start: number, end: number): boolean {
289
+ output.push(source.slice(start, end));
290
+ let containsNewline = false;
291
+ for (let cursor = start; cursor < end; cursor++) {
292
+ if (source[cursor] === "\n" || source[cursor] === "\r") {
293
+ containsNewline = true;
294
+ atLineStart = true;
295
+ } else {
296
+ atLineStart = false;
297
+ }
298
+ }
299
+ return containsNewline;
300
+ }
301
+
302
+ while (index < source.length) {
303
+ const character = source[index];
304
+
305
+ if (isHorizontalWhitespace(character)) {
306
+ const whitespaceStart = index;
307
+ index++;
308
+ while (index < source.length && isHorizontalWhitespace(source[index])) index++;
309
+ if (!atLineStart) pendingWhitespace = source.slice(whitespaceStart, index);
310
+ continue;
311
+ }
312
+
313
+ if (character === "\n" || character === "\r") {
314
+ pendingWhitespace = "";
315
+ const newlineWidth = character === "\r" && source[index + 1] === "\n" ? 2 : 1;
316
+ index += newlineWidth;
317
+ if (suppressSourceNewline && atLineStart) {
318
+ suppressSourceNewline = false;
319
+ continue;
320
+ }
321
+ output.push("\n");
322
+ atLineStart = true;
323
+ suppressSourceNewline = false;
324
+ canEndExpression = false;
325
+ previousToken = "none";
326
+ continue;
327
+ }
328
+
329
+ if (character === "#") {
330
+ beginContent(false, false);
331
+ const commentEnd =
332
+ source[index + 1] === "=" ? consumeBlockComment(source, index) : consumeLineComment(source, index);
333
+ const containsNewline = appendOpaque(index, commentEnd);
334
+ if (containsNewline) {
335
+ canEndExpression = false;
336
+ previousToken = "none";
337
+ }
338
+ index = commentEnd;
339
+ continue;
340
+ }
341
+
342
+ if (character === '"') {
343
+ beginContent(false, false);
344
+ const literalKind = source.startsWith('"""', index) ? "triple" : "double";
345
+ const literalEnd = consumeQuotedLiteral(source, index, literalKind);
346
+ appendOpaque(index, literalEnd);
347
+ index = literalEnd;
348
+ canEndExpression = true;
349
+ previousToken = "other";
350
+ continue;
351
+ }
352
+
353
+ if (character === "`") {
354
+ beginContent(false, false);
355
+ const literalEnd = consumeQuotedLiteral(source, index, "command");
356
+ appendOpaque(index, literalEnd);
357
+ index = literalEnd;
358
+ canEndExpression = true;
359
+ previousToken = "other";
360
+ continue;
361
+ }
362
+
363
+ if (character === "'") {
364
+ beginContent(false, false);
365
+ if (canEndExpression) {
366
+ output.push(character);
367
+ index++;
368
+ previousToken = "other";
369
+ continue;
370
+ }
371
+ const literalEnd = consumeQuotedLiteral(source, index, "char");
372
+ appendOpaque(index, literalEnd);
373
+ index = literalEnd;
374
+ canEndExpression = true;
375
+ previousToken = "other";
376
+ continue;
377
+ }
378
+
379
+ if (isIdentifierStart(character)) {
380
+ const wordStart = index;
381
+ index++;
382
+ while (index < source.length && isIdentifierContinue(source[index])) index++;
383
+ const word = source.slice(wordStart, index);
384
+ const isStructural =
385
+ delimiterClosers.length === 0 &&
386
+ previousToken !== "dot" &&
387
+ previousToken !== "colon" &&
388
+ previousToken !== "at";
389
+ const isEnd = isStructural && word === "end";
390
+ const isBranch = isStructural && BRANCH_CLAUSES[word] === true;
391
+ beginContent(isEnd || isBranch, false);
392
+ output.push(word);
393
+
394
+ if (isEnd) blockDepth = Math.max(0, blockDepth - 1);
395
+ else if (isStructural && BLOCK_OPENERS[word] === true) blockDepth++;
396
+
397
+ canEndExpression = EXPRESSION_PREFIX_WORDS[word] !== true;
398
+ previousToken = "other";
399
+ continue;
400
+ }
401
+
402
+ if (character === "(" || character === "[" || character === "{") {
403
+ beginContent(false, false);
404
+ output.push(character);
405
+ delimiterClosers.push(character === "(" ? ")" : character === "[" ? "]" : "}");
406
+ canEndExpression = false;
407
+ previousToken = "other";
408
+ index++;
409
+ continue;
410
+ }
411
+
412
+ if (character === ")" || character === "]" || character === "}") {
413
+ const matchesDelimiter = delimiterClosers.at(-1) === character;
414
+ beginContent(false, matchesDelimiter);
415
+ output.push(character);
416
+ if (matchesDelimiter) delimiterClosers.pop();
417
+ canEndExpression = true;
418
+ previousToken = "other";
419
+ index++;
420
+ continue;
421
+ }
422
+
423
+ if (character === ";" && delimiterClosers.length === 0) {
424
+ beginContent(false, false);
425
+ output.push(";\n");
426
+ atLineStart = true;
427
+ pendingWhitespace = "";
428
+ suppressSourceNewline = true;
429
+ canEndExpression = false;
430
+ previousToken = "none";
431
+ index++;
432
+ continue;
433
+ }
434
+
435
+ beginContent(false, false);
436
+ output.push(character);
437
+ if (character === ".") previousToken = "dot";
438
+ else if (character === ":") previousToken = "colon";
439
+ else if (character === "@") previousToken = "at";
440
+ else previousToken = "other";
441
+ canEndExpression = !isExpressionSeparator(character);
442
+ index++;
443
+ }
444
+
445
+ return output.join("");
446
+ }