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

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 (100) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/cli.js +3759 -3751
  3. package/dist/types/cli/__tests__/auth-gateway-catalog.test.d.ts +1 -0
  4. package/dist/types/cli/auth-gateway-cli.d.ts +8 -0
  5. package/dist/types/cli/update-cli.d.ts +41 -0
  6. package/dist/types/cli/usage-cli.d.ts +4 -2
  7. package/dist/types/commands/update.d.ts +1 -0
  8. package/dist/types/config/model-registry.d.ts +19 -0
  9. package/dist/types/config/settings-schema.d.ts +30 -7
  10. package/dist/types/cursor.d.ts +47 -1
  11. package/dist/types/eval/js/process-entry.d.ts +2 -1
  12. package/dist/types/eval/js/worker-core.d.ts +4 -1
  13. package/dist/types/extensibility/extensions/runner.d.ts +1 -0
  14. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +4 -0
  15. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +4 -0
  16. package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +15 -7
  17. package/dist/types/extensibility/shared-events.d.ts +2 -0
  18. package/dist/types/modes/components/assistant-message.d.ts +9 -0
  19. package/dist/types/modes/components/custom-editor.d.ts +12 -8
  20. package/dist/types/plan-mode/approved-plan.d.ts +3 -2
  21. package/dist/types/secrets/obfuscator.d.ts +7 -31
  22. package/dist/types/session/agent-session.d.ts +19 -4
  23. package/dist/types/session/prewalk.d.ts +2 -1
  24. package/dist/types/session/session-advisors.d.ts +8 -0
  25. package/dist/types/session/session-metadata.d.ts +29 -0
  26. package/dist/types/session/turn-recovery.d.ts +5 -5
  27. package/dist/types/stt/sherpa-runtime.d.ts +38 -0
  28. package/dist/types/thinking.d.ts +24 -0
  29. package/dist/types/tools/bash.d.ts +5 -4
  30. package/dist/types/tools/computer/exposure.d.ts +8 -0
  31. package/dist/types/tools/computer/supervisor.d.ts +1 -1
  32. package/dist/types/tools/computer/worker-entry.d.ts +1 -1
  33. package/dist/types/tools/computer/worker.d.ts +1 -1
  34. package/dist/types/tools/computer.d.ts +6 -0
  35. package/dist/types/tools/shell-tokenize.d.ts +14 -0
  36. package/dist/types/tools/todo.d.ts +7 -1
  37. package/package.json +12 -12
  38. package/src/advisor/__tests__/advisor.test.ts +80 -10
  39. package/src/advisor/runtime.ts +27 -1
  40. package/src/cli/__tests__/auth-gateway-catalog.test.ts +111 -0
  41. package/src/cli/auth-gateway-cli.ts +63 -16
  42. package/src/cli/config-cli.ts +25 -7
  43. package/src/cli/update-cli.ts +229 -29
  44. package/src/cli/usage-cli.ts +144 -15
  45. package/src/cli.ts +21 -15
  46. package/src/commands/update.ts +6 -0
  47. package/src/config/__tests__/model-registry.test.ts +42 -7
  48. package/src/config/model-registry.ts +67 -4
  49. package/src/config/settings-schema.ts +39 -8
  50. package/src/config/settings.ts +24 -2
  51. package/src/cursor.ts +153 -0
  52. package/src/dap/session.ts +70 -11
  53. package/src/eval/__tests__/js-context-manager.test.ts +9 -1
  54. package/src/eval/__tests__/process-entry-import.test.ts +111 -1
  55. package/src/eval/js/process-entry.ts +9 -5
  56. package/src/eval/js/worker-core.ts +6 -2
  57. package/src/exec/bash-executor.ts +4 -2
  58. package/src/extensibility/extensions/runner.ts +23 -8
  59. package/src/extensibility/legacy-pi-ai-shim.ts +9 -0
  60. package/src/extensibility/legacy-pi-coding-agent-shim.ts +14 -2
  61. package/src/extensibility/legacy-pi-tui-shim.ts +33 -0
  62. package/src/extensibility/shared-events.ts +2 -0
  63. package/src/main.ts +22 -1
  64. package/src/modes/acp/acp-agent.ts +6 -0
  65. package/src/modes/components/assistant-message.ts +88 -11
  66. package/src/modes/components/chat-transcript-builder.ts +2 -0
  67. package/src/modes/components/custom-editor.test.ts +170 -0
  68. package/src/modes/components/custom-editor.ts +79 -29
  69. package/src/modes/components/settings-defs.ts +5 -1
  70. package/src/modes/controllers/event-controller.ts +96 -4
  71. package/src/modes/controllers/selector-controller.ts +14 -0
  72. package/src/modes/interactive-mode.ts +34 -8
  73. package/src/modes/utils/context-usage.ts +19 -2
  74. package/src/modes/utils/interactive-context-helpers.ts +1 -0
  75. package/src/plan-mode/approved-plan.ts +18 -10
  76. package/src/prompts/system/custom-system-prompt.md +1 -1
  77. package/src/prompts/system/system-prompt.md +10 -1
  78. package/src/sdk.ts +4 -0
  79. package/src/secrets/obfuscator.ts +36 -126
  80. package/src/session/agent-session.ts +69 -60
  81. package/src/session/prewalk.ts +8 -3
  82. package/src/session/session-advisors.ts +67 -3
  83. package/src/session/session-metadata.ts +53 -0
  84. package/src/session/session-provider-boundary.ts +0 -1
  85. package/src/session/session-tools.ts +35 -1
  86. package/src/session/turn-recovery.ts +36 -19
  87. package/src/slash-commands/builtin-registry.ts +49 -7
  88. package/src/stt/asr-worker.ts +2 -37
  89. package/src/stt/sherpa-runtime.ts +71 -0
  90. package/src/task/executor.ts +6 -5
  91. package/src/thinking.ts +39 -0
  92. package/src/tools/bash.ts +43 -15
  93. package/src/tools/computer/exposure.ts +38 -0
  94. package/src/tools/computer/supervisor.ts +61 -13
  95. package/src/tools/computer/worker-entry.ts +28 -19
  96. package/src/tools/computer/worker.ts +3 -7
  97. package/src/tools/computer.ts +65 -10
  98. package/src/tools/gh-cache-invalidation.ts +2 -82
  99. package/src/tools/shell-tokenize.ts +83 -0
  100. package/src/tools/todo.ts +44 -17
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Conservative shell command tokenizer shared by the bash approval-pattern
3
+ * matcher and the gh-cache invalidator.
4
+ *
5
+ * Splits a bash command into independent command segments, each a list of word
6
+ * tokens. Handles single/double-quoted strings, backslash escapes, and the
7
+ * standard operators (`;`, `&&`, `||`, `|`, `&`, `(`, `)`, newlines) as segment
8
+ * boundaries so callers treat the pieces as independent command sequences.
9
+ *
10
+ * It is deliberately not a full POSIX parser — heredocs, command substitution,
11
+ * and arithmetic expansion are out of scope; callers fall through when they
12
+ * cannot find the structure they need.
13
+ */
14
+ export function tokenizeShellSegments(command: string): string[][] {
15
+ const segments: string[][] = [];
16
+ let current: string[] = [];
17
+ let buffer = "";
18
+ let inSingle = false;
19
+ let inDouble = false;
20
+ const pushBuffer = () => {
21
+ if (buffer.length > 0) {
22
+ current.push(buffer);
23
+ buffer = "";
24
+ }
25
+ };
26
+ const pushSegment = () => {
27
+ pushBuffer();
28
+ if (current.length > 0) segments.push(current);
29
+ current = [];
30
+ };
31
+ for (let i = 0; i < command.length; i++) {
32
+ const ch = command[i];
33
+ if (inSingle) {
34
+ if (ch === "'") {
35
+ inSingle = false;
36
+ continue;
37
+ }
38
+ buffer += ch;
39
+ continue;
40
+ }
41
+ if (inDouble) {
42
+ if (ch === "\\" && i + 1 < command.length) {
43
+ const next = command[i + 1];
44
+ if (next === '"' || next === "\\" || next === "$" || next === "`") {
45
+ buffer += next;
46
+ i++;
47
+ continue;
48
+ }
49
+ }
50
+ if (ch === '"') {
51
+ inDouble = false;
52
+ continue;
53
+ }
54
+ buffer += ch;
55
+ continue;
56
+ }
57
+ if (ch === "'") {
58
+ inSingle = true;
59
+ continue;
60
+ }
61
+ if (ch === '"') {
62
+ inDouble = true;
63
+ continue;
64
+ }
65
+ if (ch === "\\" && i + 1 < command.length) {
66
+ buffer += command[i + 1];
67
+ i++;
68
+ continue;
69
+ }
70
+ if (ch === " " || ch === "\t") {
71
+ pushBuffer();
72
+ continue;
73
+ }
74
+ if (ch === "\n" || ch === ";" || ch === "&" || ch === "|" || ch === "(" || ch === ")") {
75
+ pushSegment();
76
+ // `&&`, `||` already collapsed by the segment break above.
77
+ continue;
78
+ }
79
+ buffer += ch;
80
+ }
81
+ pushSegment();
82
+ return segments;
83
+ }
package/src/tools/todo.ts CHANGED
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
2
2
  import type { ToolExample } from "@oh-my-pi/pi-ai";
3
3
  import type { Component } from "@oh-my-pi/pi-tui";
4
4
  import { Text } from "@oh-my-pi/pi-tui";
5
- import { isRecord, prompt } from "@oh-my-pi/pi-utils";
5
+ import { isRecord, prompt, sanitizeText } from "@oh-my-pi/pi-utils";
6
6
  import { type } from "arktype";
7
7
  import chalk from "chalk";
8
8
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
@@ -12,7 +12,7 @@ import type { ToolSession } from "../sdk";
12
12
  import type { SessionEntry } from "../session/session-entries";
13
13
  import { framedBlock, renderStatusLine, renderTreeList } from "../tui";
14
14
  import { normalizePathLikeInput, resolveToCwd } from "./path-utils";
15
- import { formatErrorDetail, formatMoreItems, PREVIEW_LIMITS, pluralize } from "./render-utils";
15
+ import { formatErrorDetail, formatMoreItems, PREVIEW_LIMITS, pluralize, replaceTabs } from "./render-utils";
16
16
 
17
17
  // =============================================================================
18
18
  // Types
@@ -929,9 +929,27 @@ export function phaseRomanNumeral(oneBasedIndex: number): string {
929
929
  return out;
930
930
  }
931
931
 
932
- /** Display-only phase header: `I. Foundation`. State and prompts never see this. */
932
+ /**
933
+ * Every render boundary in this file funnels display text through here.
934
+ *
935
+ * `sanitizeText` strips ANSI/C0 sequences but deliberately preserves tabs, and
936
+ * a raw tab punches holes in bordered TUI output, so both are needed. The raw
937
+ * value stays untouched everywhere else: task content and phase names are the
938
+ * identity keys the local list is looked up by, and what gets persisted.
939
+ */
940
+ function forDisplay(text: string): string {
941
+ return replaceTabs(sanitizeText(text));
942
+ }
943
+
944
+ /**
945
+ * Display-only phase header: `I. Foundation`. State and prompts never see this.
946
+ *
947
+ * Sanitized for the same reason task labels are: this is a render boundary and
948
+ * the name may carry provider or session text holding control sequences. The
949
+ * raw `phase.name` stays the lookup key everywhere else.
950
+ */
933
951
  export function formatPhaseDisplayName(name: string, oneBasedIndex: number): string {
934
- return `${phaseRomanNumeral(oneBasedIndex)}. ${name}`;
952
+ return `${phaseRomanNumeral(oneBasedIndex)}. ${forDisplay(name)}`;
935
953
  }
936
954
 
937
955
  export const TODO_STRIKE_HOLD_FRAMES = 2;
@@ -970,27 +988,31 @@ function formatTodoLine(
970
988
  matched = false,
971
989
  ): string {
972
990
  const checkbox = uiTheme.checkbox;
991
+ // Sanitize only for display. A mirrored Cursor snapshot carries provider text
992
+ // verbatim, and a label holding ANSI/C0 sequences would otherwise rewrite the
993
+ // terminal every time the list renders or replays. `item.content` stays raw
994
+ // everywhere else: it is the identity key the local list is looked up by
995
+ // (`findTaskByContent`) and what gets persisted.
996
+ const label = forDisplay(item.content);
973
997
  switch (item.status) {
974
998
  case "completed": {
975
- const revealCount = completionKeys.has(item.content) ? strikeRevealCount(item.content, frame) : undefined;
999
+ const revealCount = completionKeys.has(item.content) ? strikeRevealCount(label, frame) : undefined;
976
1000
  const content =
977
- revealCount === undefined
978
- ? strikethroughText(item.content)
979
- : partialStrikethrough(item.content, revealCount);
1001
+ revealCount === undefined ? strikethroughText(label) : partialStrikethrough(label, revealCount);
980
1002
  return uiTheme.fg("success", `${prefix}${checkbox.checked} ${content}`);
981
1003
  }
982
1004
  case "in_progress":
983
- return uiTheme.fg("accent", `${prefix}${checkbox.unchecked} ${item.content}`);
1005
+ return uiTheme.fg("accent", `${prefix}${checkbox.unchecked} ${label}`);
984
1006
  case "abandoned":
985
- return uiTheme.fg("error", `${prefix}${checkbox.unchecked} ${strikethroughText(item.content)}`);
1007
+ return uiTheme.fg("error", `${prefix}${checkbox.unchecked} ${strikethroughText(label)}`);
986
1008
  case "blocked": {
987
- const note = item.blocker ? `blocked: ${item.blocker}` : "blocked";
988
- return uiTheme.fg("warning", `${prefix}${checkbox.unchecked} ${item.content} (${note})`);
1009
+ const note = item.blocker ? `blocked: ${forDisplay(item.blocker)}` : "blocked";
1010
+ return uiTheme.fg("warning", `${prefix}${checkbox.unchecked} ${label} (${note})`);
989
1011
  }
990
1012
  default:
991
1013
  // A pending todo lit by a live subagent match renders accent, matching
992
1014
  // the sticky HUD's convention (#5873).
993
- return uiTheme.fg(matched ? "accent" : "dim", `${prefix}${checkbox.unchecked} ${item.content}`);
1015
+ return uiTheme.fg(matched ? "accent" : "dim", `${prefix}${checkbox.unchecked} ${label}`);
994
1016
  }
995
1017
  }
996
1018
 
@@ -1065,13 +1087,15 @@ export const todoToolRenderer = {
1065
1087
  // both the new single-op and legacy batch shapes so a malformed delta
1066
1088
  // never breaks the TUI render loop (#2005).
1067
1089
  const opsList = normalizeTodoArg(args);
1090
+ // Model-authored, partially-streamed strings going straight into a header:
1091
+ // `renderStatusLine` only flattens CR/LF and leaves the rest to the caller.
1068
1092
  const ops =
1069
1093
  opsList.length === 0
1070
1094
  ? ["update"]
1071
1095
  : opsList.map(e => {
1072
- const parts = [e.op ?? "update"];
1073
- if (e.task) parts.push(e.task);
1074
- if (e.phase) parts.push(e.phase);
1096
+ const parts = [forDisplay(e.op ?? "update")];
1097
+ if (e.task) parts.push(forDisplay(e.task));
1098
+ if (e.phase) parts.push(forDisplay(e.phase));
1075
1099
  if (Array.isArray(e.items) && e.items.length) {
1076
1100
  parts.push(`${e.items.length} item${e.items.length === 1 ? "" : "s"}`);
1077
1101
  }
@@ -1125,7 +1149,10 @@ export const todoToolRenderer = {
1125
1149
  uiTheme,
1126
1150
  );
1127
1151
  if (allTasks.length === 0) {
1128
- const fallback = result.content?.find(content => content.type === "text")?.text ?? "No todos";
1152
+ // Provider text on the Cursor path (the todo summary or a refusal note),
1153
+ // so sanitize like every other label. The error branch above already
1154
+ // goes through `formatErrorDetail`.
1155
+ const fallback = forDisplay(result.content?.find(content => content.type === "text")?.text ?? "No todos");
1129
1156
  return new Text(`${header}\n ${uiTheme.fg("dim", fallback)}`, 0, 0);
1130
1157
  }
1131
1158