@oh-my-pi/pi-coding-agent 16.0.10 → 16.0.11

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 (86) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/cli.js +3208 -3199
  3. package/dist/types/advisor/index.d.ts +1 -0
  4. package/dist/types/advisor/transcript-recorder.d.ts +52 -0
  5. package/dist/types/commit/agentic/agent.d.ts +1 -1
  6. package/dist/types/config/settings-schema.d.ts +0 -4
  7. package/dist/types/edit/file-snapshot-store.d.ts +1 -1
  8. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  9. package/dist/types/modes/components/agent-hub.d.ts +6 -1
  10. package/dist/types/modes/components/agent-transcript-viewer.d.ts +39 -0
  11. package/dist/types/modes/components/chat-transcript-builder.d.ts +42 -0
  12. package/dist/types/modes/controllers/command-controller.d.ts +3 -2
  13. package/dist/types/modes/interactive-mode.d.ts +2 -1
  14. package/dist/types/modes/types.d.ts +2 -1
  15. package/dist/types/registry/agent-registry.d.ts +10 -3
  16. package/dist/types/session/compact-modes.d.ts +60 -0
  17. package/dist/types/session/streaming-output.d.ts +0 -2
  18. package/dist/types/tools/__tests__/json-tree.test.d.ts +1 -0
  19. package/package.json +12 -12
  20. package/src/advisor/index.ts +1 -0
  21. package/src/advisor/transcript-recorder.ts +136 -0
  22. package/src/cli/stats-cli.ts +2 -11
  23. package/src/collab/host.ts +25 -13
  24. package/src/commit/agentic/agent.ts +2 -1
  25. package/src/commit/agentic/tools/git-file-diff.ts +2 -2
  26. package/src/commit/changelog/index.ts +1 -1
  27. package/src/commit/map-reduce/map-phase.ts +1 -1
  28. package/src/commit/map-reduce/utils.ts +1 -1
  29. package/src/config/settings-schema.ts +0 -5
  30. package/src/config/settings.ts +0 -6
  31. package/src/edit/file-snapshot-store.ts +1 -1
  32. package/src/edit/renderer.ts +7 -7
  33. package/src/eval/js/tool-bridge.ts +3 -2
  34. package/src/eval/py/prelude.py +3 -2
  35. package/src/export/html/tool-views.generated.js +28 -28
  36. package/src/extensibility/extensions/types.ts +7 -0
  37. package/src/hindsight/mental-models.ts +1 -1
  38. package/src/internal-urls/docs-index.generated.txt +1 -1
  39. package/src/internal-urls/history-protocol.ts +8 -3
  40. package/src/irc/bus.ts +8 -0
  41. package/src/lsp/index.ts +2 -2
  42. package/src/main.ts +4 -1
  43. package/src/modes/acp/acp-agent.ts +63 -0
  44. package/src/modes/components/agent-hub.ts +97 -920
  45. package/src/modes/components/agent-transcript-viewer.ts +461 -0
  46. package/src/modes/components/chat-transcript-builder.ts +462 -0
  47. package/src/modes/components/diff.ts +12 -35
  48. package/src/modes/controllers/command-controller.ts +12 -2
  49. package/src/modes/controllers/event-controller.ts +1 -1
  50. package/src/modes/controllers/input-controller.ts +8 -1
  51. package/src/modes/controllers/selector-controller.ts +4 -1
  52. package/src/modes/interactive-mode.ts +4 -2
  53. package/src/modes/types.ts +2 -1
  54. package/src/prompts/tools/read.md +1 -1
  55. package/src/registry/agent-registry.ts +13 -4
  56. package/src/sdk.ts +1 -1
  57. package/src/session/agent-session.ts +92 -3
  58. package/src/session/compact-modes.ts +105 -0
  59. package/src/session/session-dump-format.ts +1 -1
  60. package/src/session/session-history-format.ts +1 -1
  61. package/src/session/streaming-output.ts +5 -5
  62. package/src/slash-commands/builtin-registry.ts +16 -4
  63. package/src/task/executor.ts +1 -1
  64. package/src/task/output-manager.ts +5 -0
  65. package/src/tools/__tests__/json-tree.test.ts +35 -0
  66. package/src/tools/approval.ts +1 -1
  67. package/src/tools/bash.ts +0 -1
  68. package/src/tools/browser.ts +0 -1
  69. package/src/tools/eval.ts +1 -1
  70. package/src/tools/gh.ts +1 -1
  71. package/src/tools/irc.ts +1 -1
  72. package/src/tools/json-tree.ts +22 -5
  73. package/src/tools/read.ts +5 -6
  74. package/src/web/scrapers/firefox-addons.ts +1 -1
  75. package/src/web/scrapers/github.ts +1 -1
  76. package/src/web/scrapers/go-pkg.ts +2 -2
  77. package/src/web/scrapers/metacpan.ts +2 -2
  78. package/src/web/scrapers/nvd.ts +2 -2
  79. package/src/web/scrapers/ollama.ts +1 -1
  80. package/src/web/scrapers/opencorporates.ts +1 -1
  81. package/src/web/scrapers/pub-dev.ts +1 -1
  82. package/src/web/scrapers/repology.ts +1 -1
  83. package/src/web/scrapers/sourcegraph.ts +1 -1
  84. package/src/web/scrapers/terraform.ts +6 -6
  85. package/src/web/scrapers/wikidata.ts +2 -2
  86. package/src/workspace-tree.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JSON tree rendering utilities shared across tool renderers.
3
3
  */
4
- import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
4
+ import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
5
5
  import type { Theme } from "../modes/theme/theme";
6
6
  import { truncateToWidth } from "./render-utils";
7
7
 
@@ -19,6 +19,8 @@ const ARGS_INLINE_PAIR_SEP = ", ";
19
19
  const ARGS_INLINE_PAIR_SEP_WIDTH = Bun.stringWidth(ARGS_INLINE_PAIR_SEP);
20
20
  const ARGS_INLINE_MORE = "…";
21
21
  const ARGS_INLINE_MORE_WIDTH = Bun.stringWidth(ARGS_INLINE_MORE);
22
+ /** Minimal value footprint (quotes + a couple chars) reserved for each not-yet-rendered key. */
23
+ const ARGS_INLINE_TAIL_VALUE_RESERVE = 4;
22
24
 
23
25
  function isRecord(value: unknown): value is Record<string, unknown> {
24
26
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -49,10 +51,15 @@ export function formatScalar(value: unknown, maxLen: number): string {
49
51
  * Format args inline for collapsed view.
50
52
  */
51
53
  export function formatArgsInline(args: Record<string, unknown>, maxWidth: number): string {
52
- let result = "";
53
- let width = 0;
54
+ const keys: string[] = [];
54
55
  for (const key in args) {
55
56
  if (key in HIDDEN_ARG_KEYS) continue;
57
+ keys.push(key);
58
+ }
59
+ let result = "";
60
+ let width = 0;
61
+ for (let i = 0; i < keys.length; i++) {
62
+ const key = keys[i];
56
63
  const value = args[key];
57
64
  const sep = width > 0 ? ARGS_INLINE_PAIR_SEP : "";
58
65
  const sepW = width > 0 ? ARGS_INLINE_PAIR_SEP_WIDTH : 0;
@@ -61,11 +68,21 @@ export function formatArgsInline(args: Record<string, unknown>, maxWidth: number
61
68
  if (cap <= 0) {
62
69
  return `${result}${ARGS_INLINE_MORE}`;
63
70
  }
64
- const valueMaxLen = Math.min(maxWidth - current, 24);
71
+ // Reserve each still-pending key's minimal footprint (sep + name + `=` +
72
+ // a short value) so a long value can't starve the keys that follow it.
73
+ let tailReserve = 0;
74
+ for (let j = i + 1; j < keys.length; j++) {
75
+ tailReserve += ARGS_INLINE_PAIR_SEP_WIDTH + Bun.stringWidth(keys[j]) + 1 + ARGS_INLINE_TAIL_VALUE_RESERVE;
76
+ }
77
+ // Budget the whole `key=value` piece against the width left after the
78
+ // tail reserve, then back out the value's share. The last key reserves
79
+ // nothing and fills the line.
80
+ const pieceBudget = Math.min(cap, maxWidth - current - tailReserve);
81
+ const valueMaxLen = Math.max(1, pieceBudget - Bun.stringWidth(key) - 3);
65
82
  const valueStr = formatScalar(value, valueMaxLen);
66
83
  const piece = `${key}=${valueStr}`;
67
84
  const pieceW = Bun.stringWidth(piece);
68
- if (pieceW > cap) {
85
+ if (pieceW > pieceBudget) {
69
86
  return `${result}${sep}${truncateToWidth(piece, cap)}`;
70
87
  }
71
88
  result += sep + piece;
package/src/tools/read.ts CHANGED
@@ -275,7 +275,7 @@ function formatMergedBraceLine(
275
275
  shouldAddHashLines: boolean,
276
276
  shouldAddLineNumbers: boolean,
277
277
  ): { model: string; display: string } {
278
- const merged = `${headText.trimEnd()} .. ${tailText.trim()}`;
278
+ const merged = `${headText.trimEnd()} ${tailText.trim()}`;
279
279
  if (shouldAddHashLines) {
280
280
  return { model: `${startLine}-${endLine}:${merged}`, display: merged };
281
281
  }
@@ -315,7 +315,7 @@ const FOOTER_RANGE_SAMPLES = 2;
315
315
 
316
316
  /**
317
317
  * Footer appended to summarized reads telling the model how to recover the
318
- * elided body. Without this hint, agents either ignore the `...`/`{ .. }`
318
+ * elided body. Without this hint, agents either ignore the `…`/`{ }`
319
319
  * markers or burn a turn guessing the right selector (see issue #1046). The
320
320
  * footer demonstrates the multi-range selector syntax with concrete sample
321
321
  * ranges drawn from the actual elision so the model re-reads only what it
@@ -327,7 +327,6 @@ function formatSummaryElisionFooter(
327
327
  elidedLines: number,
328
328
  ): string {
329
329
  if (elidedRanges.length === 0) return "";
330
- const lineWord = elidedLines === 1 ? "line" : "lines";
331
330
  const sampleCount = Math.min(elidedRanges.length, FOOTER_RANGE_SAMPLES);
332
331
  const selector = elidedRanges
333
332
  .slice(0, sampleCount)
@@ -335,7 +334,7 @@ function formatSummaryElisionFooter(
335
334
  .join(",");
336
335
  const example = `${readPath}:${selector}`;
337
336
  const tail = elidedRanges.length > sampleCount ? `, e.g. ${example}` : ` with ${example}`;
338
- return `[${elidedLines} ${lineWord} elided; re-read needed ranges${tail}]`;
337
+ return `[…${elidedLines}ln elided; re-read needed ranges${tail}]`;
339
338
  }
340
339
  const READ_CHUNK_SIZE = 8 * 1024;
341
340
 
@@ -1904,8 +1903,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1904
1903
  let elidedLines = 0;
1905
1904
  for (const unit of units) {
1906
1905
  if (unit.kind === "elided") {
1907
- modelParts.push("...");
1908
- displayParts.push("...");
1906
+ modelParts.push("");
1907
+ displayParts.push("");
1909
1908
  elidedRanges.push({ start: unit.startLine, end: unit.endLine });
1910
1909
  elidedLines += unit.endLine - unit.startLine + 1;
1911
1910
  continue;
@@ -173,7 +173,7 @@ export const handleFirefoxAddons: SpecialHandler = async (
173
173
  md += `- ${permission}\n`;
174
174
  }
175
175
  if (permissions.length > preview.length) {
176
- md += `\n*...and ${permissions.length - preview.length} more*\n`;
176
+ md += `\n[…${permissions.length - preview.length} permissions elided…]\n`;
177
177
  }
178
178
  }
179
179
 
@@ -468,7 +468,7 @@ async function renderGitHubRepo(
468
468
  md += `${prefix}${item.path}\n`;
469
469
  }
470
470
  if (tree.length > 100) {
471
- md += `... and ${tree.length - 100} more files\n`;
471
+ md += `[…${tree.length - 100} files elided…]\n`;
472
472
  }
473
473
  md += "```\n\n";
474
474
  }
@@ -211,7 +211,7 @@ export const handleGoPkg: SpecialHandler = async (
211
211
  sections.push(exported.slice(0, 50).join("\n"));
212
212
  if (exported.length > 50) {
213
213
  notes.push(`showing 50 of ${exported.length} exports`);
214
- sections.push(`\n... and ${exported.length - 50} more`);
214
+ sections.push(`\n[…${exported.length - 50} exports elided…]`);
215
215
  }
216
216
  sections.push("");
217
217
  }
@@ -240,7 +240,7 @@ export const handleGoPkg: SpecialHandler = async (
240
240
  sections.push(imports.slice(0, 20).join("\n"));
241
241
  if (imports.length > 20) {
242
242
  notes.push(`showing 20 of ${imports.length} imports`);
243
- sections.push(`\n... and ${imports.length - 20} more`);
243
+ sections.push(`\n[…${imports.length - 20} imports elided…]`);
244
244
  }
245
245
  sections.push("");
246
246
  }
@@ -162,7 +162,7 @@ function formatModuleMarkdown(module: ModuleResponse, release: ReleaseResponse |
162
162
  md += "\n";
163
163
  }
164
164
  if (runtimeDeps.length > 20) {
165
- md += `\n*...and ${runtimeDeps.length - 20} more*\n`;
165
+ md += `\n[…${runtimeDeps.length - 20} dependencies elided…]\n`;
166
166
  }
167
167
  }
168
168
  }
@@ -212,7 +212,7 @@ function formatReleaseMarkdown(release: ReleaseResponse): string {
212
212
  md += "\n";
213
213
  }
214
214
  if (runtimeDeps.length > 20) {
215
- md += `\n*...and ${runtimeDeps.length - 20} more*\n`;
215
+ md += `\n[…${runtimeDeps.length - 20} dependencies elided…]\n`;
216
216
  }
217
217
  }
218
218
 
@@ -182,7 +182,7 @@ export const handleNvd: SpecialHandler = async (
182
182
  md += `- \`${cpe}\`\n`;
183
183
  }
184
184
  if (cpes.length > 20) {
185
- md += `\n*...and ${cpes.length - 20} more*\n`;
185
+ md += `\n[…${cpes.length - 20} CPEs elided…]\n`;
186
186
  }
187
187
  md += "\n";
188
188
  }
@@ -195,7 +195,7 @@ export const handleNvd: SpecialHandler = async (
195
195
  md += `- ${ref.url}${tags}\n`;
196
196
  }
197
197
  if (vuln.references.length > 15) {
198
- md += `\n*...and ${vuln.references.length - 15} more references*\n`;
198
+ md += `\n[…${vuln.references.length - 15} references elided…]\n`;
199
199
  }
200
200
  }
201
201
 
@@ -139,7 +139,7 @@ function formatTagList(tags: string[], maxItems: number): string {
139
139
  const limited = tags.slice(0, maxItems);
140
140
  const formatted = limited.map(tag => `\`${tag}\``).join(", ");
141
141
  if (tags.length > maxItems) {
142
- return `${formatted} (and ${tags.length - maxItems} more)`;
142
+ return `${formatted} […${tags.length - maxItems} tags elided…]`;
143
143
  }
144
144
  return formatted;
145
145
  }
@@ -216,7 +216,7 @@ export const handleOpenCorporates: SpecialHandler = async (
216
216
  md += "\n";
217
217
  }
218
218
  if (inactiveOfficers.length > 10) {
219
- md += `\n*...and ${inactiveOfficers.length - 10} more former officers*\n`;
219
+ md += `\n[…${inactiveOfficers.length - 10} former officers elided…]\n`;
220
220
  }
221
221
  md += "\n";
222
222
  }
@@ -109,7 +109,7 @@ export const handlePubDev: SpecialHandler = async (url: string, timeout: number,
109
109
  md += "\n";
110
110
  }
111
111
  if (deps.length > 20) {
112
- md += `\n*...and ${deps.length - 20} more*\n`;
112
+ md += `\n[…${deps.length - 20} dependencies elided…]\n`;
113
113
  }
114
114
  md += "\n";
115
115
  }
@@ -239,7 +239,7 @@ export const handleRepology: SpecialHandler = async (
239
239
  }
240
240
 
241
241
  if (packages.length > 15) {
242
- md += `\n*...and ${packages.length - 15} more repositories*\n`;
242
+ md += `\n[…${packages.length - 15} repositories elided…]\n`;
243
243
  }
244
244
 
245
245
  md += `\n---\n\n[View on Repology](${url})\n`;
@@ -294,7 +294,7 @@ async function renderSearch(
294
294
  }
295
295
 
296
296
  if (results.length > maxResults) {
297
- md += `... and ${results.length - maxResults} more results\n`;
297
+ md += `[…${results.length - maxResults} results elided…]\n`;
298
298
  }
299
299
 
300
300
  return { content: md, ok: true };
@@ -150,7 +150,7 @@ async function handleModuleUrl(
150
150
  md += `| ${input.name} | \`${type}\` | ${required} | ${desc} |\n`;
151
151
  }
152
152
  if (inputs.length > 30) {
153
- md += `\n*... and ${inputs.length - 30} more inputs*\n`;
153
+ md += `\n[…${inputs.length - 30} inputs elided…]\n`;
154
154
  }
155
155
  md += "\n";
156
156
  }
@@ -165,7 +165,7 @@ async function handleModuleUrl(
165
165
  md += "\n";
166
166
  }
167
167
  if (outputs.length > 20) {
168
- md += `\n*... and ${outputs.length - 20} more outputs*\n`;
168
+ md += `\n[…${outputs.length - 20} outputs elided…]\n`;
169
169
  }
170
170
  md += "\n";
171
171
  }
@@ -180,7 +180,7 @@ async function handleModuleUrl(
180
180
  md += "\n";
181
181
  }
182
182
  if (deps.length > 15) {
183
- md += `\n*... and ${deps.length - 15} more dependencies*\n`;
183
+ md += `\n[…${deps.length - 15} dependencies elided…]\n`;
184
184
  }
185
185
  md += "\n";
186
186
  }
@@ -193,7 +193,7 @@ async function handleModuleUrl(
193
193
  md += `- \`${res.type}\` (${res.name})\n`;
194
194
  }
195
195
  if (resources.length > 20) {
196
- md += `\n*... and ${resources.length - 20} more resources*\n`;
196
+ md += `\n[…${resources.length - 20} resources elided…]\n`;
197
197
  }
198
198
  md += "\n";
199
199
  }
@@ -205,7 +205,7 @@ async function handleModuleUrl(
205
205
  md += `- **${sub.name}**: \`${sub.path}\`\n`;
206
206
  }
207
207
  if (mod.submodules.length > 10) {
208
- md += `\n*... and ${mod.submodules.length - 10} more submodules*\n`;
208
+ md += `\n[…${mod.submodules.length - 10} submodules elided…]\n`;
209
209
  }
210
210
  }
211
211
 
@@ -267,7 +267,7 @@ async function handleProviderUrl(
267
267
  md += `- [${doc.title}](https://registry.terraform.io/providers/${namespace}/${type}/latest/docs/${doc.category}/${doc.slug})\n`;
268
268
  }
269
269
  if (docs.length > 15) {
270
- md += `\n*... and ${docs.length - 15} more*\n`;
270
+ md += `\n[…${docs.length - 15} documents elided…]\n`;
271
271
  }
272
272
  md += "\n";
273
273
  }
@@ -169,7 +169,7 @@ export const handleWikidata: SpecialHandler = async (
169
169
  if (values.length > 0) {
170
170
  // Limit values shown per property
171
171
  const displayValues = values.slice(0, 10);
172
- const overflow = values.length > 10 ? ` (+${values.length - 10} more)` : "";
172
+ const overflow = values.length > 10 ? ` […${values.length - 10} values elided…]` : "";
173
173
  processedProperties.push(`- **${propLabel}:** ${displayValues.join(", ")}${overflow}`);
174
174
  }
175
175
  }
@@ -187,7 +187,7 @@ export const handleWikidata: SpecialHandler = async (
187
187
  const maxProps = 50;
188
188
  md += processedProperties.slice(0, maxProps).join("\n");
189
189
  if (processedProperties.length > maxProps) {
190
- md += `\n\n*...and ${processedProperties.length - maxProps} more properties*`;
190
+ md += `\n\n[…${processedProperties.length - maxProps} properties elided…]`;
191
191
  }
192
192
  md += "\n";
193
193
  }
@@ -298,7 +298,7 @@ function applyLineCap(
298
298
  const removed = new Set(removable.map(item => item.index));
299
299
  const kept = lines.filter((_, index) => !removed.has(index));
300
300
  kept.push({
301
- label: `… (${removable.length} lines elided beyond depth/cap)`,
301
+ label: `[…${removable.length}ln elided…]`,
302
302
  depth: 0,
303
303
  isRoot: false,
304
304
  });