@oh-my-pi/pi-coding-agent 16.1.10 → 16.1.12

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 (63) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/cli.js +2480 -2481
  3. package/dist/types/export/html/index.d.ts +31 -2
  4. package/dist/types/export/html/web-palette.d.ts +117 -0
  5. package/dist/types/export/share.d.ts +10 -5
  6. package/dist/types/hindsight/content.d.ts +7 -0
  7. package/dist/types/hindsight/transcript.d.ts +1 -1
  8. package/dist/types/modes/components/hook-editor.d.ts +2 -1
  9. package/dist/types/modes/interactive-mode.d.ts +5 -0
  10. package/dist/types/modes/types.d.ts +17 -0
  11. package/dist/types/modes/utils/keybinding-matchers.d.ts +13 -0
  12. package/dist/types/secrets/index.d.ts +1 -1
  13. package/dist/types/secrets/obfuscator.d.ts +43 -9
  14. package/dist/types/session/agent-session.d.ts +4 -0
  15. package/dist/types/session/session-context.d.ts +2 -0
  16. package/dist/types/session/session-entries.d.ts +6 -0
  17. package/dist/types/session/session-manager.d.ts +2 -1
  18. package/dist/types/tools/acp-bridge.d.ts +29 -0
  19. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +7 -0
  20. package/dist/types/tools/browser/tab-worker.d.ts +20 -0
  21. package/dist/types/utils/jj.d.ts +25 -0
  22. package/dist/types/utils/title-generator.d.ts +0 -2
  23. package/package.json +12 -12
  24. package/scripts/generate-share-viewer.ts +4 -2
  25. package/src/autoresearch/git.ts +12 -0
  26. package/src/discovery/opencode.ts +47 -4
  27. package/src/edit/hashline/filesystem.ts +8 -0
  28. package/src/edit/modes/patch.ts +18 -2
  29. package/src/edit/modes/replace.ts +13 -10
  30. package/src/export/html/index.ts +50 -8
  31. package/src/export/html/web-palette.ts +142 -0
  32. package/src/export/share.ts +198 -8
  33. package/src/hindsight/backend.ts +4 -4
  34. package/src/hindsight/content.ts +17 -1
  35. package/src/hindsight/transcript.ts +2 -2
  36. package/src/internal-urls/docs-index.generated.txt +1 -1
  37. package/src/main.ts +8 -0
  38. package/src/modes/components/agent-dashboard.ts +8 -8
  39. package/src/modes/components/hook-editor.ts +13 -10
  40. package/src/modes/components/session-selector.ts +3 -0
  41. package/src/modes/controllers/event-controller.ts +9 -5
  42. package/src/modes/controllers/extension-ui-controller.ts +6 -2
  43. package/src/modes/interactive-mode.ts +69 -29
  44. package/src/modes/theme/dark.json +1 -1
  45. package/src/modes/types.ts +18 -0
  46. package/src/modes/utils/keybinding-matchers.ts +36 -1
  47. package/src/modes/utils/ui-helpers.ts +1 -0
  48. package/src/prompts/tools/browser.md +3 -2
  49. package/src/sdk.ts +14 -2
  50. package/src/secrets/index.ts +1 -1
  51. package/src/secrets/obfuscator.ts +220 -71
  52. package/src/session/agent-session.ts +57 -43
  53. package/src/session/session-context.ts +5 -0
  54. package/src/session/session-entries.ts +6 -0
  55. package/src/session/session-manager.ts +3 -1
  56. package/src/task/worktree.ts +12 -4
  57. package/src/thinking.ts +1 -1
  58. package/src/tools/acp-bridge.ts +66 -0
  59. package/src/tools/browser/cmux/cmux-tab.ts +37 -0
  60. package/src/tools/browser/tab-worker.ts +160 -37
  61. package/src/tools/write.ts +2 -25
  62. package/src/utils/jj.ts +47 -0
  63. package/src/utils/title-generator.ts +31 -99
@@ -12,7 +12,7 @@ import type { Settings } from "../config/settings";
12
12
  import titleMarkerInstruction from "../prompts/system/title-marker-instruction.md" with { type: "text" };
13
13
  import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
14
14
  import titleMarkerSystemPrompt from "../prompts/system/title-system-marker.md" with { type: "text" };
15
- import { ONLINE_TINY_TITLE_MODEL_KEY } from "../tiny/models";
15
+ import { isTinyTitleLocalModelKey, ONLINE_TINY_TITLE_MODEL_KEY } from "../tiny/models";
16
16
  import { formatTitleUserMessage, isLowSignalTitleInput, normalizeGeneratedTitle } from "../tiny/text";
17
17
  import { tinyTitleClient } from "../tiny/title-client";
18
18
 
@@ -23,7 +23,6 @@ const TITLE_MARKER_INSTRUCTION = prompt.render(titleMarkerInstruction);
23
23
  const DEFAULT_TERMINAL_TITLE = "π";
24
24
  const TERMINAL_TITLE_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
25
25
 
26
- export const TITLE_LOCAL_FALLBACK_DELAY_MS = 10_000;
27
26
  const TITLE_MAX_TOKENS = 30;
28
27
  const REASONING_SAFE_MAX_TOKENS = 1024;
29
28
  const SET_TITLE_TOOL_NAME = "set_title";
@@ -81,78 +80,6 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
81
80
  return undefined;
82
81
  }
83
82
 
84
- export async function raceFirstNonNull<T>(
85
- primary: Promise<T | null>,
86
- startFallback: () => Promise<T | null>,
87
- delayMs: number = TITLE_LOCAL_FALLBACK_DELAY_MS,
88
- onPrimaryWinAfterFallback?: () => void,
89
- ): Promise<T | null> {
90
- const { promise, resolve } = Promise.withResolvers<T | null>();
91
- let resolved = false;
92
- let primarySettled = false;
93
- let fallbackStarted = false;
94
- let fallbackSettled = false;
95
-
96
- const resolveOnce = (value: T | null): void => {
97
- if (resolved) return;
98
- resolved = true;
99
- resolve(value);
100
- };
101
- const maybeResolveNull = (): void => {
102
- if (primarySettled && fallbackStarted && fallbackSettled) resolveOnce(null);
103
- };
104
- const startFallbackOnce = (): void => {
105
- if (fallbackStarted || resolved) return;
106
- fallbackStarted = true;
107
- let fallback: Promise<T | null>;
108
- try {
109
- fallback = startFallback();
110
- } catch {
111
- fallbackSettled = true;
112
- maybeResolveNull();
113
- return;
114
- }
115
- void fallback.then(
116
- value => {
117
- fallbackSettled = true;
118
- if (value !== null) resolveOnce(value);
119
- else maybeResolveNull();
120
- },
121
- () => {
122
- fallbackSettled = true;
123
- maybeResolveNull();
124
- },
125
- );
126
- };
127
-
128
- const timer = setTimeout(startFallbackOnce, delayMs);
129
- void primary.then(
130
- value => {
131
- primarySettled = true;
132
- clearTimeout(timer);
133
- if (value !== null) {
134
- if (fallbackStarted) onPrimaryWinAfterFallback?.();
135
- resolveOnce(value);
136
- return;
137
- }
138
- startFallbackOnce();
139
- maybeResolveNull();
140
- },
141
- () => {
142
- primarySettled = true;
143
- clearTimeout(timer);
144
- startFallbackOnce();
145
- maybeResolveNull();
146
- },
147
- );
148
-
149
- try {
150
- return await promise;
151
- } finally {
152
- clearTimeout(timer);
153
- }
154
- }
155
-
156
83
  /**
157
84
  * Generate a title for a session based on the first user message.
158
85
  *
@@ -200,36 +127,41 @@ export async function generateSessionTitle(
200
127
  );
201
128
  }
202
129
 
203
- const onlineAbortController = new AbortController();
204
- const localTitlePromise = titleSystemPrompt
205
- ? tinyTitleClient.generate(tinyModel, firstMessage, { systemPrompt: titleSystemPrompt })
206
- : tinyTitleClient.generate(tinyModel, firstMessage);
207
- const localTitle = localTitlePromise.then(
208
- title => title || null,
209
- err => {
210
- logger.warn("title-generator: local model error", {
130
+ // User explicitly picked a local tiny model. NEVER fall back to the online
131
+ // smol path (issue #3187): the smol role resolves through priority.json and
132
+ // silently bills whatever provider holds the resolved API key — OpenRouter
133
+ // in the reporter's case, leaking real credits without consent. If the
134
+ // local worker fails (unknown key, download missing, transformers.js
135
+ // crash, abort), leave the session untitled; the next user turn retries.
136
+ if (!isTinyTitleLocalModelKey(tinyModel)) {
137
+ logger.warn("title-generator: unknown local tiny model; skipping title (will not fall back to online)", {
138
+ sessionId,
139
+ model: tinyModel,
140
+ reason: "unknown-local-model",
141
+ });
142
+ return null;
143
+ }
144
+ try {
145
+ const localTitle = titleSystemPrompt
146
+ ? await tinyTitleClient.generate(tinyModel, firstMessage, { systemPrompt: titleSystemPrompt })
147
+ : await tinyTitleClient.generate(tinyModel, firstMessage);
148
+ if (!localTitle) {
149
+ logger.warn("title-generator: local tiny model produced no title; skipping (no online fallback)", {
211
150
  sessionId,
212
151
  model: tinyModel,
213
- error: err instanceof Error ? err.message : String(err),
152
+ reason: "local-no-output",
214
153
  });
215
154
  return null;
216
- },
217
- );
218
- const startOnline = (): Promise<string | null> =>
219
- generateTitleOnline(
220
- firstMessage,
221
- registry,
222
- settings,
155
+ }
156
+ return localTitle;
157
+ } catch (err) {
158
+ logger.warn("title-generator: local tiny model errored; skipping (no online fallback)", {
223
159
  sessionId,
224
- currentModel,
225
- metadataResolver,
226
- onlineAbortController.signal,
227
- titleSystemPrompt,
228
- );
229
-
230
- return raceFirstNonNull(localTitle, startOnline, TITLE_LOCAL_FALLBACK_DELAY_MS, () => {
231
- onlineAbortController.abort();
232
- });
160
+ model: tinyModel,
161
+ error: err instanceof Error ? err.message : String(err),
162
+ });
163
+ return null;
164
+ }
233
165
  }
234
166
 
235
167
  export async function generateTitleOnline(