@oh-my-pi/pi-coding-agent 15.11.6 → 15.11.8

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 (102) hide show
  1. package/CHANGELOG.md +57 -1
  2. package/dist/cli.js +431 -381
  3. package/dist/types/cli/args.d.ts +2 -0
  4. package/dist/types/cli/bench-cli.d.ts +78 -0
  5. package/dist/types/collab/crypto.d.ts +12 -0
  6. package/dist/types/collab/guest.d.ts +21 -0
  7. package/dist/types/collab/host.d.ts +13 -0
  8. package/dist/types/collab/protocol.d.ts +100 -0
  9. package/dist/types/collab/relay-client.d.ts +22 -0
  10. package/dist/types/commands/bench.d.ts +29 -0
  11. package/dist/types/commands/join.d.ts +12 -0
  12. package/dist/types/config/model-resolver.d.ts +3 -2
  13. package/dist/types/config/settings-schema.d.ts +93 -1
  14. package/dist/types/edit/renderer.d.ts +1 -0
  15. package/dist/types/extensibility/slash-commands.d.ts +1 -11
  16. package/dist/types/modes/components/agent-hub.d.ts +13 -0
  17. package/dist/types/modes/components/collab-prompt-message.d.ts +10 -0
  18. package/dist/types/modes/components/hook-selector.d.ts +4 -6
  19. package/dist/types/modes/components/oauth-selector.d.ts +10 -1
  20. package/dist/types/modes/components/segment-track.d.ts +11 -6
  21. package/dist/types/modes/components/settings-selector.d.ts +8 -1
  22. package/dist/types/modes/components/snapcompact-shape-preview.d.ts +31 -0
  23. package/dist/types/modes/components/status-line/component.d.ts +4 -1
  24. package/dist/types/modes/components/status-line/types.d.ts +9 -0
  25. package/dist/types/modes/components/tool-execution.d.ts +13 -9
  26. package/dist/types/modes/interactive-mode.d.ts +7 -0
  27. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +3 -0
  28. package/dist/types/modes/setup-wizard/scenes/types.d.ts +10 -1
  29. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +3 -0
  30. package/dist/types/modes/types.d.ts +8 -0
  31. package/dist/types/session/agent-session.d.ts +11 -0
  32. package/dist/types/session/session-manager.d.ts +21 -0
  33. package/dist/types/session/snapcompact-inline.d.ts +8 -3
  34. package/dist/types/slash-commands/builtin-registry.d.ts +9 -0
  35. package/dist/types/tools/bash.d.ts +2 -0
  36. package/dist/types/tools/eval-render.d.ts +1 -0
  37. package/dist/types/tools/renderers.d.ts +13 -0
  38. package/dist/types/tools/ssh.d.ts +1 -0
  39. package/package.json +14 -12
  40. package/scripts/bench-guard.ts +71 -0
  41. package/src/cli/args.ts +2 -0
  42. package/src/cli/bench-cli.ts +437 -0
  43. package/src/cli-commands.ts +2 -0
  44. package/src/collab/crypto.ts +57 -0
  45. package/src/collab/guest.ts +421 -0
  46. package/src/collab/host.ts +494 -0
  47. package/src/collab/protocol.ts +191 -0
  48. package/src/collab/relay-client.ts +216 -0
  49. package/src/commands/bench.ts +42 -0
  50. package/src/commands/join.ts +39 -0
  51. package/src/config/model-registry.ts +74 -19
  52. package/src/config/model-resolver.ts +36 -5
  53. package/src/config/settings-schema.ts +119 -1
  54. package/src/edit/renderer.ts +5 -0
  55. package/src/extensibility/slash-commands.ts +1 -97
  56. package/src/hindsight/client.ts +26 -1
  57. package/src/hindsight/state.ts +6 -2
  58. package/src/internal-urls/docs-index.generated.ts +4 -3
  59. package/src/main.ts +11 -2
  60. package/src/mcp/transports/stdio.ts +81 -7
  61. package/src/modes/components/agent-hub.ts +119 -22
  62. package/src/modes/components/assistant-message.ts +126 -6
  63. package/src/modes/components/collab-prompt-message.ts +30 -0
  64. package/src/modes/components/hook-selector.ts +4 -5
  65. package/src/modes/components/oauth-selector.ts +67 -7
  66. package/src/modes/components/segment-track.ts +44 -7
  67. package/src/modes/components/settings-selector.ts +27 -0
  68. package/src/modes/components/snapcompact-shape-preview-doc.md +11 -0
  69. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  70. package/src/modes/components/status-line/component.ts +21 -1
  71. package/src/modes/components/status-line/presets.ts +1 -1
  72. package/src/modes/components/status-line/segments.ts +13 -0
  73. package/src/modes/components/status-line/types.ts +10 -0
  74. package/src/modes/components/tips.txt +2 -1
  75. package/src/modes/components/tool-execution.ts +18 -10
  76. package/src/modes/controllers/input-controller.ts +80 -12
  77. package/src/modes/controllers/selector-controller.ts +6 -2
  78. package/src/modes/controllers/streaming-reveal.ts +7 -0
  79. package/src/modes/interactive-mode.ts +36 -4
  80. package/src/modes/setup-wizard/index.ts +1 -0
  81. package/src/modes/setup-wizard/scenes/glyph.ts +24 -6
  82. package/src/modes/setup-wizard/scenes/providers.ts +36 -2
  83. package/src/modes/setup-wizard/scenes/sign-in.ts +10 -1
  84. package/src/modes/setup-wizard/scenes/theme.ts +28 -1
  85. package/src/modes/setup-wizard/scenes/types.ts +10 -1
  86. package/src/modes/setup-wizard/scenes/web-search.ts +22 -6
  87. package/src/modes/setup-wizard/wizard-overlay.ts +38 -1
  88. package/src/modes/types.ts +8 -0
  89. package/src/modes/utils/context-usage.ts +1 -1
  90. package/src/modes/utils/ui-helpers.ts +7 -0
  91. package/src/prompts/bench.md +7 -0
  92. package/src/sdk.ts +240 -36
  93. package/src/session/agent-session.ts +22 -0
  94. package/src/session/session-manager.ts +44 -0
  95. package/src/session/snapcompact-inline.ts +20 -22
  96. package/src/slash-commands/builtin-registry.ts +210 -0
  97. package/src/tools/bash.ts +3 -0
  98. package/src/tools/eval-render.ts +4 -0
  99. package/src/tools/read.ts +38 -5
  100. package/src/tools/renderers.ts +13 -0
  101. package/src/tools/ssh.ts +3 -0
  102. package/src/tools/write.ts +13 -42
@@ -1,4 +1,6 @@
1
1
  import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
2
+ import { SHAPE_VARIANT_NAMES } from "@oh-my-pi/snapcompact";
3
+ import { DEFAULT_RELAY_URL } from "../collab/protocol";
2
4
  import { AUTO_THINKING, getConfiguredThinkingLevelMetadata, getThinkingLevelMetadata } from "../thinking";
3
5
  import {
4
6
  TINY_MODEL_DEVICE_DEFAULT,
@@ -100,6 +102,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
100
102
  "Approvals",
101
103
  "Notifications",
102
104
  "Speech",
105
+ "Collab",
103
106
  "Magic Keywords",
104
107
  "Startup & Updates",
105
108
  "Power (macOS)",
@@ -146,7 +149,8 @@ export type StatusLineSegmentId =
146
149
  | "cache_write"
147
150
  | "cache_hit"
148
151
  | "session_name"
149
- | "usage";
152
+ | "usage"
153
+ | "collab";
150
154
 
151
155
  /** Submenu choice metadata. */
152
156
  export type SubmenuOption<V extends string = string> = {
@@ -1326,6 +1330,29 @@ export const SETTINGS_SCHEMA = {
1326
1330
  },
1327
1331
  },
1328
1332
 
1333
+ // Collab
1334
+ "collab.relayUrl": {
1335
+ type: "string",
1336
+ default: DEFAULT_RELAY_URL,
1337
+ ui: {
1338
+ tab: "interaction",
1339
+ group: "Collab",
1340
+ label: "Relay URL",
1341
+ description: "Relay used by /collab (wss://host[:port]; self-host with the omp-collab-relay service)",
1342
+ },
1343
+ },
1344
+
1345
+ "collab.displayName": {
1346
+ type: "string",
1347
+ default: "",
1348
+ ui: {
1349
+ tab: "interaction",
1350
+ group: "Collab",
1351
+ label: "Display Name",
1352
+ description: "Name shown to other collab participants (default: OS username)",
1353
+ },
1354
+ },
1355
+
1329
1356
  // Speech-to-text
1330
1357
  "stt.enabled": {
1331
1358
  type: "boolean",
@@ -1609,6 +1636,97 @@ export const SETTINGS_SCHEMA = {
1609
1636
  },
1610
1637
  },
1611
1638
 
1639
+ "snapcompact.shape": {
1640
+ type: "enum",
1641
+ values: ["auto", ...SHAPE_VARIANT_NAMES] as const,
1642
+ default: "auto",
1643
+ ui: {
1644
+ tab: "context",
1645
+ group: "Experimental",
1646
+ label: "Snapcompact Shape",
1647
+ description:
1648
+ "Frame shape snapcompact prints text with (compaction archive and inline imaging). Auto picks a shape tuned for the current model.",
1649
+ options: [
1650
+ {
1651
+ value: "auto",
1652
+ label: "Auto",
1653
+ description: "Picks a shape tuned for the current model, falling back to its provider family.",
1654
+ },
1655
+ {
1656
+ value: "8x8r-bw",
1657
+ label: "8x8 repeated, black",
1658
+ description:
1659
+ "unscii square cell, black ink, every line printed twice with the copy on a pale highlight band.",
1660
+ },
1661
+ {
1662
+ value: "8x8r-sent",
1663
+ label: "8x8 repeated, sentence hues",
1664
+ description: "Repeated grid with ink cycling six hues at sentence boundaries.",
1665
+ },
1666
+ {
1667
+ value: "8x8u-bw",
1668
+ label: "8x8, black",
1669
+ description: "Plain unscii square cell, single-printed lines, black ink.",
1670
+ },
1671
+ {
1672
+ value: "8x8u-sent",
1673
+ label: "8x8, sentence hues",
1674
+ description: "Plain unscii square cell with sentence-hue ink.",
1675
+ },
1676
+ {
1677
+ value: "6x6u-bw",
1678
+ label: "6x6 dense, black",
1679
+ description: "unscii squeezed to 6x6 — densest readable cell, fewest frames — in black ink.",
1680
+ },
1681
+ {
1682
+ value: "6x6u-sent",
1683
+ label: "6x6 dense, sentence hues",
1684
+ description: "Densest cell with sentence-hue ink.",
1685
+ },
1686
+ {
1687
+ value: "5x8-bw",
1688
+ label: "5x8 legacy, black",
1689
+ description: "Original X.org 5x8 glyphs on the 2576px frame, black ink.",
1690
+ },
1691
+ {
1692
+ value: "5x8-sent",
1693
+ label: "5x8 legacy, sentence hues",
1694
+ description: "The original snapcompact shape (pre-shape-table sessions rendered this).",
1695
+ },
1696
+ {
1697
+ value: "6x12-dim",
1698
+ label: "6x12, dimmed stopwords",
1699
+ description: "X.org 6x12 glyphs, black ink, function words dimmed gray.",
1700
+ },
1701
+ {
1702
+ value: "8x13-bw",
1703
+ label: "8x13, black",
1704
+ description: "X.org 8x13 glyphs, black ink.",
1705
+ },
1706
+ {
1707
+ value: "8on16-bw",
1708
+ label: "8x13 on 16px pitch, black",
1709
+ description: "8x13 glyphs on an 8x16 cell (extra leading), black ink.",
1710
+ },
1711
+ {
1712
+ value: "doc-8on16-bw",
1713
+ label: "Doc 8on16, black",
1714
+ description: "Two word-wrapped newspaper columns of 8x13 glyphs on a 16px pitch, black ink.",
1715
+ },
1716
+ {
1717
+ value: "doc-8on16-sent",
1718
+ label: "Doc 8on16, sentence hues",
1719
+ description: "Two-column doc layout with sentence-hue ink.",
1720
+ },
1721
+ {
1722
+ value: "doc-8on16-sent-dim",
1723
+ label: "Doc 8on16, sentence hues + dimmed stopwords",
1724
+ description: "Two-column doc layout, sentence-hue ink, function words dimmed gray.",
1725
+ },
1726
+ ],
1727
+ },
1728
+ },
1729
+
1612
1730
  // Branch summaries
1613
1731
  "branchSummary.enabled": {
1614
1732
  type: "boolean",
@@ -579,6 +579,11 @@ function wrapEditRendererLine(line: string, width: number): string[] {
579
579
 
580
580
  export const editToolRenderer = {
581
581
  mergeCallAndResult: true,
582
+ // Pending preview is a TAIL window of the streamed diff ("… N more lines
583
+ // above" + last rows); the result render re-anchors the block top-first, so
584
+ // committing the preview's settled head would strand a stale call-box
585
+ // fragment in native scrollback.
586
+ provisionalPendingPreview: true,
582
587
 
583
588
  renderCall(
584
589
  args: EditRenderArgs,
@@ -1,14 +1,8 @@
1
- import type { AutocompleteItem } from "@oh-my-pi/pi-tui";
2
1
  import { parseFrontmatter, prompt } from "@oh-my-pi/pi-utils";
3
2
  import { slashCommandCapability } from "../capability/slash-command";
4
3
  import { appendInlineArgsFallback, templateUsesInlineArgPlaceholders } from "../config/prompt-templates";
5
4
  import type { SlashCommand } from "../discovery";
6
5
  import { loadCapability } from "../discovery";
7
- import {
8
- BUILTIN_SLASH_COMMAND_DEFS,
9
- type BuiltinSlashCommand,
10
- type SubcommandDef,
11
- } from "../slash-commands/builtin-registry";
12
6
  import { EMBEDDED_COMMAND_TEMPLATES } from "../task/commands";
13
7
  import { parseCommandArgs, substituteArgs } from "../utils/command-args";
14
8
 
@@ -24,97 +18,7 @@ export interface SlashCommandInfo {
24
18
  path?: string;
25
19
  }
26
20
 
27
- export type { BuiltinSlashCommand, SubcommandDef } from "../slash-commands/builtin-registry";
28
-
29
- /**
30
- * Build getArgumentCompletions from declarative subcommand definitions.
31
- * Returns subcommand names filtered by prefix in the dropdown.
32
- */
33
- function buildArgumentCompletions(subcommands: SubcommandDef[]): (prefix: string) => AutocompleteItem[] | null {
34
- return (argumentPrefix: string) => {
35
- if (argumentPrefix.includes(" ")) return null; // past the subcommand
36
- const lower = argumentPrefix.toLowerCase();
37
- const matches = subcommands
38
- .filter(s => s.name.startsWith(lower))
39
- .map(s => ({
40
- value: `${s.name} `,
41
- label: s.name,
42
- description: s.description,
43
- hint: s.usage,
44
- }));
45
- return matches.length > 0 ? matches : null;
46
- };
47
- }
48
-
49
- /**
50
- * Build getInlineHint from declarative subcommand definitions.
51
- * Shows remaining completion + usage as dim ghost text after cursor.
52
- */
53
- function buildSubcommandInlineHint(subcommands: SubcommandDef[]): (argumentText: string) => string | null {
54
- return (argumentText: string) => {
55
- const trimmed = argumentText.trimStart();
56
- const spaceIndex = trimmed.indexOf(" ");
57
-
58
- if (spaceIndex === -1) {
59
- // Still typing subcommand name — show remaining chars + usage
60
- const prefix = trimmed.toLowerCase();
61
- if (prefix.length === 0) return null;
62
- const match = subcommands.find(s => s.name.startsWith(prefix));
63
- if (!match) return null;
64
- const remaining = match.name.slice(prefix.length);
65
- return remaining + (match.usage ? ` ${match.usage}` : "");
66
- }
67
-
68
- // Subcommand typed — show remaining usage params
69
- const subName = trimmed.slice(0, spaceIndex).toLowerCase();
70
- const afterSub = trimmed.slice(spaceIndex + 1);
71
- const sub = subcommands.find(s => s.name === subName);
72
- if (!sub?.usage) return null;
73
-
74
- if (afterSub.length > 0) {
75
- const usageParts = sub.usage.split(" ");
76
- const inputParts = afterSub.trim().split(/\s+/);
77
- const remaining = usageParts.slice(inputParts.length);
78
- return remaining.length > 0 ? remaining.join(" ") : null;
79
- }
80
-
81
- return sub.usage;
82
- };
83
- }
84
-
85
- /**
86
- * Build getInlineHint for commands with a simple static hint string.
87
- * Shows the hint only when no arguments have been typed yet.
88
- */
89
- function buildStaticInlineHint(hint: string): (argumentText: string) => string | null {
90
- return (argumentText: string) => (argumentText.trim().length === 0 ? hint : null);
91
- }
92
-
93
- /**
94
- * Materialized builtin slash commands with completion functions derived from
95
- * declarative subcommand/hint definitions.
96
- */
97
- export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<
98
- BuiltinSlashCommand & {
99
- getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null;
100
- getInlineHint?: (argumentText: string) => string | null;
101
- }
102
- > = BUILTIN_SLASH_COMMAND_DEFS.map(cmd => {
103
- if (cmd.subcommands) {
104
- return {
105
- ...cmd,
106
- getArgumentCompletions: buildArgumentCompletions(cmd.subcommands),
107
- getInlineHint: buildSubcommandInlineHint(cmd.subcommands),
108
- };
109
- }
110
- if (cmd.inlineHint) {
111
- return {
112
- ...cmd,
113
- getInlineHint: buildStaticInlineHint(cmd.inlineHint),
114
- };
115
- }
116
- return cmd;
117
- });
21
+ export type { BuiltinSlashCommand, SubcommandDef } from "../slash-commands/types";
118
22
 
119
23
  /**
120
24
  * Represents a custom slash command loaded from a file
@@ -546,7 +546,7 @@ interface BuiltMemoryItem {
546
546
  function buildMemoryItem(item: MemoryItemInput): BuiltMemoryItem {
547
547
  const out: BuiltMemoryItem = { content: item.content };
548
548
  if (item.timestamp !== undefined) {
549
- out.timestamp = item.timestamp instanceof Date ? item.timestamp.toISOString() : item.timestamp;
549
+ out.timestamp = item.timestamp instanceof Date ? formatDateWithLocalOffset(item.timestamp) : item.timestamp;
550
550
  }
551
551
  if (item.context !== undefined) out.context = item.context;
552
552
  if (item.metadata !== undefined) out.metadata = item.metadata;
@@ -558,6 +558,31 @@ function buildMemoryItem(item: MemoryItemInput): BuiltMemoryItem {
558
558
  return out;
559
559
  }
560
560
 
561
+ function formatDateWithLocalOffset(date: Date): string {
562
+ const offsetMinutes = date.getTimezoneOffset();
563
+ const offsetSign = offsetMinutes <= 0 ? "+" : "-";
564
+ const absoluteOffset = Math.abs(offsetMinutes);
565
+ const offsetHours = Math.floor(absoluteOffset / 60);
566
+ const offsetRemainderMinutes = absoluteOffset % 60;
567
+ const milliseconds = date.getMilliseconds();
568
+ const millisecondsPart = milliseconds === 0 ? "" : `.${pad3(milliseconds)}`;
569
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}T${pad2(
570
+ date.getHours(),
571
+ )}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())}${millisecondsPart}${offsetSign}${pad2(
572
+ offsetHours,
573
+ )}:${pad2(offsetRemainderMinutes)}`;
574
+ }
575
+
576
+ function pad2(value: number): string {
577
+ return value < 10 ? `0${value}` : String(value);
578
+ }
579
+
580
+ function pad3(value: number): string {
581
+ if (value < 10) return `00${value}`;
582
+ if (value < 100) return `0${value}`;
583
+ return String(value);
584
+ }
585
+
561
586
  function buildQueryString(query: Record<string, unknown>): string {
562
587
  const params = new URLSearchParams();
563
588
  for (const [key, value] of Object.entries(query)) {
@@ -26,6 +26,7 @@ const RETAIN_FLUSH_INTERVAL_MS = 5_000;
26
26
  interface PendingRetainItem {
27
27
  content: string;
28
28
  context?: string;
29
+ timestamp: Date;
29
30
  }
30
31
 
31
32
  interface RecallOutcome {
@@ -84,7 +85,7 @@ export class HindsightRetainQueue {
84
85
  if (this.#closed) {
85
86
  throw new Error("Hindsight retain queue is closed.");
86
87
  }
87
- this.#items.push({ content, context });
88
+ this.#items.push({ content, context, timestamp: new Date() });
88
89
 
89
90
  if (this.#items.length >= RETAIN_FLUSH_BATCH_SIZE) {
90
91
  void this.flush();
@@ -154,6 +155,7 @@ export class HindsightRetainQueue {
154
155
  context: item.context ?? state.config.retainContext,
155
156
  metadata: { session_id: sessionId },
156
157
  tags: state.retainTags,
158
+ timestamp: item.timestamp,
157
159
  }));
158
160
  await state.client.retainBatch(state.bankId, batch, { async: true });
159
161
  if (state.config.debug) {
@@ -281,6 +283,7 @@ export class HindsightSessionState {
281
283
  }
282
284
 
283
285
  async retainSession(messages: HindsightMessage[]): Promise<void> {
286
+ const retainedAt = new Date();
284
287
  const retainFullWindow = this.config.retainMode === "full-session";
285
288
  let target: HindsightMessage[];
286
289
  let documentId: string;
@@ -291,7 +294,7 @@ export class HindsightSessionState {
291
294
  } else {
292
295
  const windowTurns = this.config.retainEveryNTurns + this.config.retainOverlapTurns;
293
296
  target = sliceLastTurnsByUserBoundary(messages, windowTurns);
294
- documentId = `${this.sessionId}-${Date.now()}`;
297
+ documentId = `${this.sessionId}-${retainedAt.getTime()}`;
295
298
  }
296
299
 
297
300
  const { transcript } = prepareRetentionTranscript(target, true);
@@ -303,6 +306,7 @@ export class HindsightSessionState {
303
306
  context: this.config.retainContext,
304
307
  metadata: { session_id: this.sessionId },
305
308
  tags: this.retainTags,
309
+ timestamp: retainedAt,
306
310
  async: true,
307
311
  });
308
312
  }