@oh-my-pi/pi-coding-agent 15.10.8 → 15.10.10

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 (52) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/dist/types/config/model-registry.d.ts +13 -0
  3. package/dist/types/config/settings-schema.d.ts +0 -9
  4. package/dist/types/debug/terminal-info.d.ts +0 -1
  5. package/dist/types/extensibility/custom-tools/loader.d.ts +22 -3
  6. package/dist/types/extensibility/extensions/index.d.ts +1 -1
  7. package/dist/types/extensibility/extensions/loader.d.ts +17 -1
  8. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +8 -0
  9. package/dist/types/mcp/transports/stdio.d.ts +12 -0
  10. package/dist/types/modes/components/custom-editor.d.ts +3 -2
  11. package/dist/types/modes/components/transcript-container.d.ts +12 -26
  12. package/dist/types/sdk.d.ts +42 -2
  13. package/dist/types/task/discovery.d.ts +1 -2
  14. package/dist/types/task/executor.d.ts +16 -0
  15. package/dist/types/tiny/title-client.d.ts +1 -1
  16. package/dist/types/tools/index.d.ts +17 -0
  17. package/dist/types/tools/todo.d.ts +2 -0
  18. package/dist/types/tui/hyperlink.d.ts +8 -0
  19. package/package.json +9 -9
  20. package/src/cli/list-models.ts +5 -11
  21. package/src/config/model-registry.ts +91 -20
  22. package/src/config/settings-schema.ts +0 -10
  23. package/src/debug/terminal-info.ts +0 -3
  24. package/src/edit/diff.ts +48 -15
  25. package/src/eval/js/shared/rewrite-imports.ts +9 -1
  26. package/src/extensibility/custom-tools/loader.ts +43 -19
  27. package/src/extensibility/extensions/index.ts +1 -0
  28. package/src/extensibility/extensions/loader.ts +29 -6
  29. package/src/extensibility/plugins/legacy-pi-compat.ts +30 -6
  30. package/src/internal-urls/docs-index.generated.ts +4 -4
  31. package/src/mcp/transports/stdio.ts +139 -3
  32. package/src/modes/components/custom-editor.ts +69 -9
  33. package/src/modes/components/model-selector.ts +62 -52
  34. package/src/modes/components/transcript-container.ts +204 -125
  35. package/src/modes/controllers/event-controller.ts +0 -45
  36. package/src/modes/controllers/input-controller.ts +5 -5
  37. package/src/modes/controllers/mcp-command-controller.ts +2 -2
  38. package/src/modes/controllers/selector-controller.ts +0 -4
  39. package/src/modes/interactive-mode.ts +2 -10
  40. package/src/prompts/system/system-prompt.md +3 -3
  41. package/src/prompts/tools/bash.md +3 -3
  42. package/src/prompts/tools/todo.md +5 -1
  43. package/src/sdk.ts +138 -56
  44. package/src/ssh/ssh-executor.ts +60 -4
  45. package/src/task/discovery.ts +17 -24
  46. package/src/task/executor.ts +19 -0
  47. package/src/task/index.ts +4 -0
  48. package/src/tiny/title-client.ts +6 -3
  49. package/src/tools/index.ts +17 -0
  50. package/src/tools/todo.ts +16 -7
  51. package/src/tui/hyperlink.ts +27 -3
  52. package/src/web/search/providers/anthropic.ts +8 -2
@@ -3,10 +3,12 @@ import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
3
3
  import type { FetchImpl, ToolChoice } from "@oh-my-pi/pi-ai";
4
4
  import { logger } from "@oh-my-pi/pi-utils";
5
5
  import type { AsyncJobManager } from "../async/job-manager";
6
+ import type { Rule } from "../capability/rule";
6
7
  import type { PromptTemplate } from "../config/prompt-templates";
7
8
  import type { Settings } from "../config/settings";
8
9
  import { EditTool } from "../edit";
9
10
  import { checkPythonKernelAvailability } from "../eval/py/kernel";
11
+ import type { ToolPathWithSource } from "../extensibility/custom-tools";
10
12
  import type { Skill } from "../extensibility/skills";
11
13
  import type { GoalModeState, GoalRuntime } from "../goals";
12
14
  import { GoalTool } from "../goals/tools/goal-tool";
@@ -154,6 +156,21 @@ export interface ToolSession {
154
156
  skills?: Skill[];
155
157
  /** Pre-loaded prompt templates */
156
158
  promptTemplates?: PromptTemplate[];
159
+ /** Pre-loaded rules (forwarded to subagents to skip re-discovery). */
160
+ rules?: Rule[];
161
+ /**
162
+ * Pre-discovered extension source paths. Forwarded to subagents so they
163
+ * skip the FS scan but still re-bind extensions to their own session-scoped
164
+ * `ExtensionAPI` (cwd, eventBus, runtime). Inline extension factories
165
+ * (`<inline-N>`) are NOT included — those are session-local.
166
+ */
167
+ extensionPaths?: string[];
168
+ /**
169
+ * Pre-discovered custom-tool source paths from `.omp/tools/`, `.claude/tools/`,
170
+ * plugins, etc. Forwarded to subagents so they skip the FS scan but still
171
+ * re-bind tools to their own session-scoped `CustomToolAPI`.
172
+ */
173
+ customToolPaths?: ToolPathWithSource[];
157
174
  /** Whether LSP integrations are enabled */
158
175
  enableLsp?: boolean;
159
176
  /** Whether an edit-capable tool is available in this session (controls hashline output) */
package/src/tools/todo.ts CHANGED
@@ -51,7 +51,7 @@ export interface TodoToolDetails {
51
51
  // =============================================================================
52
52
 
53
53
  const TodoOp = z
54
- .enum(["init", "start", "done", "rm", "drop", "append", "note"] as const)
54
+ .enum(["init", "start", "done", "rm", "drop", "append", "note", "view"] as const)
55
55
  .describe("operation to apply");
56
56
 
57
57
  const InitListEntry = z.object({
@@ -380,6 +380,8 @@ function applyEntry(phases: TodoPhase[], entry: TodoOpEntryValue, errors: string
380
380
  }
381
381
  case "append":
382
382
  return appendItems(phases, entry, errors);
383
+ case "view":
384
+ return phases;
383
385
  }
384
386
  }
385
387
 
@@ -523,9 +525,12 @@ export function markdownToPhases(md: string): { phases: TodoPhase[]; errors: str
523
525
  return { phases, errors };
524
526
  }
525
527
 
526
- function formatSummary(phases: TodoPhase[], errors: string[]): string {
528
+ function formatSummary(phases: TodoPhase[], errors: string[], readOnly = false): string {
527
529
  const tasks = phases.flatMap(phase => phase.tasks);
528
- if (tasks.length === 0) return errors.length > 0 ? `Errors: ${errors.join("; ")}` : "Todo list cleared.";
530
+ if (tasks.length === 0) {
531
+ if (errors.length > 0) return `Errors: ${errors.join("; ")}`;
532
+ return readOnly ? "Todo list is empty." : "Todo list cleared.";
533
+ }
529
534
 
530
535
  const remainingByPhase = phases
531
536
  .map(phase => ({
@@ -608,15 +613,19 @@ export class TodoTool implements AgentTool<typeof todoSchema, TodoToolDetails> {
608
613
  _context?: AgentToolContext,
609
614
  ): Promise<AgentToolResult<TodoToolDetails>> {
610
615
  const previousPhases = clonePhases(this.session.getTodoPhases?.() ?? []);
611
- const { phases: updated, errors } = applyParams(clonePhases(previousPhases), params);
612
- const completedTasks = getCompletionTransitions(previousPhases, updated);
613
- this.session.setTodoPhases?.(updated);
616
+ // Pure-view calls are reads: no normalization, no state write.
617
+ const readOnly = params.ops.every(entry => entry.op === "view");
618
+ const { phases: updated, errors } = readOnly
619
+ ? { phases: previousPhases, errors: [] as string[] }
620
+ : applyParams(clonePhases(previousPhases), params);
621
+ const completedTasks = readOnly ? [] : getCompletionTransitions(previousPhases, updated);
622
+ if (!readOnly) this.session.setTodoPhases?.(updated);
614
623
  const storage = this.session.getSessionFile() ? "session" : "memory";
615
624
  const details: TodoToolDetails = { phases: updated, storage };
616
625
  if (completedTasks.length > 0) details.completedTasks = completedTasks;
617
626
 
618
627
  return {
619
- content: [{ type: "text", text: formatSummary(updated, errors) }],
628
+ content: [{ type: "text", text: formatSummary(updated, errors, readOnly) }],
620
629
  details,
621
630
  isError: errors.length > 0 ? true : undefined,
622
631
  };
@@ -18,6 +18,7 @@ import {
18
18
 
19
19
  const OSC = "\x1b]";
20
20
  const ST = "\x1b\\";
21
+ const BEL = "\x07";
21
22
 
22
23
  /** Stable 8-char hex ID derived from a URI — hints terminals to coalesce identical adjacent links. */
23
24
  function buildLinkId(uri: string): string {
@@ -60,14 +61,18 @@ function safeHyperlinkUri(uri: string): string | undefined {
60
61
  return uri;
61
62
  }
62
63
 
63
- function wrapHyperlink(uri: string, displayText: string): string {
64
- if (!isHyperlinkEnabled()) return displayText;
64
+ function wrapHyperlinkCore(uri: string, displayText: string, terminator: typeof ST | typeof BEL): string {
65
65
  // Do not double-wrap if the text already embeds an OSC 8 sequence.
66
66
  if (displayText.includes("\x1b]8;")) return displayText;
67
67
  const safeUri = safeHyperlinkUri(uri);
68
68
  if (!safeUri) return displayText;
69
69
  const id = buildLinkId(safeUri);
70
- return `${OSC}8;id=${id};${safeUri}${ST}${displayText}${OSC}8;;${ST}`;
70
+ return `${OSC}8;id=${id};${safeUri}${terminator}${displayText}${OSC}8;;${terminator}`;
71
+ }
72
+
73
+ function wrapHyperlink(uri: string, displayText: string): string {
74
+ if (!isHyperlinkEnabled()) return displayText;
75
+ return wrapHyperlinkCore(uri, displayText, ST);
71
76
  }
72
77
 
73
78
  /**
@@ -95,6 +100,25 @@ export function urlHyperlink(url: string, displayText: string): string {
95
100
  }
96
101
  }
97
102
 
103
+ /**
104
+ * Wrap `displayText` in an OSC 8 hyperlink pointing at an HTTP(S) URL,
105
+ * bypassing terminal capability auto-detection. Used for auth prompts where
106
+ * an inert "click" label blocks login on terminals whose capabilities are
107
+ * not advertised. Still returns plain text when the user has explicitly
108
+ * opted out via `tui.hyperlinks=off`.
109
+ */
110
+ export function urlHyperlinkAlways(url: string, displayText: string): string {
111
+ if (settings.get("tui.hyperlinks") === "off") return displayText;
112
+ const normalized = url.match(/^www\./i) ? `https://${url}` : url;
113
+ try {
114
+ const parsed = new URL(normalized);
115
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return displayText;
116
+ return wrapHyperlinkCore(parsed.href, displayText, BEL);
117
+ } catch {
118
+ return displayText;
119
+ }
120
+ }
121
+
98
122
  /**
99
123
  * Wrap `displayText` in an OSC 8 hyperlink pointing at a filesystem path.
100
124
  *
@@ -16,6 +16,7 @@ import {
16
16
  type FetchImpl,
17
17
  stripClaudeToolPrefix,
18
18
  withAuth,
19
+ wrapFetchForCch,
19
20
  } from "@oh-my-pi/pi-ai";
20
21
  import { $env } from "@oh-my-pi/pi-utils";
21
22
  import type {
@@ -64,7 +65,9 @@ function buildSystemBlocks(
64
65
  model: string,
65
66
  systemPrompt?: string,
66
67
  ): AnthropicSystemBlock[] | undefined {
67
- const includeClaudeCode = !model.startsWith("claude-3-5-haiku");
68
+ // Match the streaming path: the CC billing header + system instruction are
69
+ // an OAuth fingerprint and must not be claimed on API-key requests.
70
+ const includeClaudeCode = auth.isOAuth && !model.startsWith("claude-3-5-haiku");
68
71
  const extraInstructions = auth.isOAuth ? ["You are a helpful AI assistant with web search capabilities."] : [];
69
72
 
70
73
  return buildAnthropicSystemBlocks(systemPrompt ? [systemPrompt] : undefined, {
@@ -118,7 +121,10 @@ async function callSearch(
118
121
  body.system = systemBlocks;
119
122
  }
120
123
 
121
- const response = await fetchImpl(url, {
124
+ // OAuth requests inject the CC billing header (buildSystemBlocks); patch its
125
+ // cch attestation like the streaming path instead of shipping `cch=00000`.
126
+ const doFetch = auth.isOAuth ? wrapFetchForCch(fetchImpl) : fetchImpl;
127
+ const response = await doFetch(url, {
122
128
  method: "POST",
123
129
  headers,
124
130
  body: JSON.stringify(body),