@oh-my-pi/pi-coding-agent 16.0.3 → 16.0.5

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 (75) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/cli.js +697 -337
  3. package/dist/types/advisor/advise-tool.d.ts +9 -0
  4. package/dist/types/cli/args.d.ts +2 -0
  5. package/dist/types/cli/bench-cli.d.ts +6 -0
  6. package/dist/types/commands/launch.d.ts +6 -0
  7. package/dist/types/config/settings-schema.d.ts +92 -3
  8. package/dist/types/edit/file-snapshot-store.d.ts +2 -0
  9. package/dist/types/extensibility/extensions/runner.d.ts +5 -2
  10. package/dist/types/extensibility/extensions/types.d.ts +8 -7
  11. package/dist/types/extensibility/shared-events.d.ts +22 -1
  12. package/dist/types/main.d.ts +1 -0
  13. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  14. package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
  15. package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
  16. package/dist/types/modes/utils/context-usage.d.ts +12 -0
  17. package/dist/types/sdk.d.ts +3 -1
  18. package/dist/types/session/agent-session.d.ts +20 -0
  19. package/dist/types/session/session-persistence.d.ts +4 -0
  20. package/dist/types/tools/read.d.ts +1 -0
  21. package/dist/types/tui/code-cell.d.ts +2 -0
  22. package/dist/types/utils/image-vision-fallback.d.ts +28 -0
  23. package/dist/types/web/search/providers/base.d.ts +1 -0
  24. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  25. package/package.json +12 -12
  26. package/src/advisor/__tests__/advisor.test.ts +59 -0
  27. package/src/advisor/advise-tool.ts +13 -0
  28. package/src/cli/args.ts +4 -0
  29. package/src/cli/bench-cli.ts +30 -7
  30. package/src/cli/flag-tables.ts +9 -0
  31. package/src/collab/host.ts +2 -2
  32. package/src/commands/launch.ts +6 -0
  33. package/src/config/settings-schema.ts +85 -3
  34. package/src/edit/file-snapshot-store.ts +12 -3
  35. package/src/eval/py/runner.py +44 -0
  36. package/src/extensibility/extensions/runner.ts +20 -2
  37. package/src/extensibility/extensions/types.ts +16 -5
  38. package/src/extensibility/shared-events.ts +24 -0
  39. package/src/internal-urls/docs-index.generated.ts +81 -81
  40. package/src/main.ts +18 -9
  41. package/src/modes/components/branch-summary-message.ts +1 -0
  42. package/src/modes/components/collab-prompt-message.ts +9 -7
  43. package/src/modes/components/compaction-summary-message.ts +1 -0
  44. package/src/modes/components/custom-message.ts +1 -0
  45. package/src/modes/components/footer.ts +6 -5
  46. package/src/modes/components/hook-message.ts +1 -0
  47. package/src/modes/components/read-tool-group.ts +9 -3
  48. package/src/modes/components/skill-message.ts +1 -0
  49. package/src/modes/components/status-line/component.ts +131 -14
  50. package/src/modes/components/status-line/context-thresholds.ts +0 -1
  51. package/src/modes/components/tips.txt +2 -1
  52. package/src/modes/components/todo-reminder.ts +1 -0
  53. package/src/modes/components/ttsr-notification.ts +1 -0
  54. package/src/modes/components/user-message.ts +6 -6
  55. package/src/modes/controllers/event-controller.ts +2 -7
  56. package/src/modes/controllers/selector-controller.ts +10 -3
  57. package/src/modes/interactive-mode.ts +4 -2
  58. package/src/modes/rpc/rpc-types.ts +1 -1
  59. package/src/modes/utils/context-usage.ts +28 -15
  60. package/src/prompts/system/system-prompt.md +2 -0
  61. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  62. package/src/prompts/tools/image-attachment-describe.md +10 -0
  63. package/src/sdk.ts +14 -18
  64. package/src/session/agent-session.ts +571 -235
  65. package/src/session/session-loader.ts +19 -32
  66. package/src/session/session-persistence.ts +27 -11
  67. package/src/ssh/connection-manager.ts +3 -2
  68. package/src/task/executor.ts +1 -1
  69. package/src/tools/image-gen.ts +67 -25
  70. package/src/tools/read.ts +54 -6
  71. package/src/tui/code-cell.ts +44 -3
  72. package/src/utils/image-vision-fallback.ts +197 -0
  73. package/src/web/search/index.ts +12 -0
  74. package/src/web/search/providers/base.ts +1 -0
  75. package/src/web/search/providers/gemini.ts +56 -18
@@ -30,6 +30,7 @@ import {
30
30
  ProcessTerminal,
31
31
  Spacer,
32
32
  setTerminalTextSizing,
33
+ setTuiTight,
33
34
  TERMINAL,
34
35
  Text,
35
36
  TUI,
@@ -566,6 +567,7 @@ export class InteractiveMode implements InteractiveModeContext {
566
567
  );
567
568
  }
568
569
 
570
+ setTuiTight(settings.get("tui.tight"));
569
571
  this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"));
570
572
  this.ui.setMaxInlineImages(settings.get("tui.maxInlineImages"));
571
573
  // OSC 66 text-sizing is Kitty-only; resolve the setting against the terminal's
@@ -2179,7 +2181,7 @@ export class InteractiveMode implements InteractiveModeContext {
2179
2181
  }
2180
2182
 
2181
2183
  #formatKeepContextLabel(contextUsage: ContextUsage | undefined): string {
2182
- if (contextUsage?.tokens == null) {
2184
+ if (!contextUsage) {
2183
2185
  return "Approve and keep context";
2184
2186
  }
2185
2187
  const tokens = formatContextTokenCount(contextUsage.tokens);
@@ -2188,7 +2190,7 @@ export class InteractiveMode implements InteractiveModeContext {
2188
2190
  }
2189
2191
 
2190
2192
  #isKeepContextDisabled(contextUsage: ContextUsage | undefined): boolean {
2191
- return contextUsage?.percent != null && contextUsage.percent > PLAN_KEEP_CONTEXT_DISABLE_THRESHOLD_PERCENT;
2193
+ return contextUsage !== undefined && contextUsage.percent > PLAN_KEEP_CONTEXT_DISABLE_THRESHOLD_PERCENT;
2192
2194
  }
2193
2195
 
2194
2196
  async #openPlanInExternalEditor(planFilePath: string): Promise<void> {
@@ -108,7 +108,7 @@ export interface RpcSessionState {
108
108
  /** For session dump / export (plain-text parity with /dump). */
109
109
  systemPrompt?: string[];
110
110
  dumpTools?: Array<{ name: string; description: string; parameters: unknown; examples?: readonly ToolExample[] }>;
111
- /** Current context window usage. Null tokens/percent when unknown (e.g. right after compaction). */
111
+ /** Current context window usage. */
112
112
  contextUsage?: ContextUsage;
113
113
  }
114
114
 
@@ -94,7 +94,7 @@ export function computeNonMessageTokens(session: AgentSession): number {
94
94
  * the status-line fast path intentionally uses the equivalent collapsed total
95
95
  * in `computeNonMessageTokens`.
96
96
  */
97
- function computeNonMessageBreakdown(session: AgentSession): {
97
+ export function computeNonMessageBreakdown(session: AgentSession): {
98
98
  skillsTokens: number;
99
99
  toolsTokens: number;
100
100
  systemContextTokens: number;
@@ -119,22 +119,37 @@ export function computeContextBreakdown(
119
119
  const model = session.model;
120
120
  const contextWindow = model?.contextWindow ?? 0;
121
121
 
122
+ const breakdown = typeof session.getContextBreakdown === "function" ? session.getContextBreakdown() : undefined;
123
+
122
124
  let messagesTokens = 0;
123
- const convo = session.messages;
124
- if (convo) {
125
- for (const message of convo) {
126
- messagesTokens += estimateTokens(message);
125
+ let skillsTokens = 0;
126
+ let toolsTokens = 0;
127
+ let systemContextTokens = 0;
128
+ let systemPromptTokens = 0;
129
+ let usedTokens = 0;
130
+
131
+ if (breakdown) {
132
+ messagesTokens = breakdown.messagesTokens;
133
+ skillsTokens = breakdown.skillsTokens;
134
+ toolsTokens = breakdown.systemToolsTokens;
135
+ systemContextTokens = breakdown.systemContextTokens;
136
+ systemPromptTokens = breakdown.systemPromptTokens;
137
+ usedTokens = breakdown.usedTokens;
138
+ } else {
139
+ const convo = session.messages;
140
+ if (convo) {
141
+ for (const message of convo) {
142
+ messagesTokens += estimateTokens(message);
143
+ }
127
144
  }
145
+ const nonMessage = computeNonMessageBreakdown(session);
146
+ skillsTokens = nonMessage.skillsTokens;
147
+ toolsTokens = nonMessage.toolsTokens;
148
+ systemContextTokens = nonMessage.systemContextTokens;
149
+ systemPromptTokens = nonMessage.systemPromptTokens;
150
+ usedTokens = skillsTokens + toolsTokens + systemContextTokens + systemPromptTokens + messagesTokens;
128
151
  }
129
152
 
130
- // The rendered system prompt already contains the skill descriptions and the
131
- // markdown tool descriptions. To present a non-overlapping breakdown:
132
- // System prompt = total system prompt text - skills section (tool descriptions stay)
133
- // Tools = JSON tool schema sent separately on the wire
134
- // Skills = the skill list embedded in the system prompt
135
- // Messages = conversation messages
136
- const { skillsTokens, toolsTokens, systemContextTokens, systemPromptTokens } = computeNonMessageBreakdown(session);
137
-
138
153
  const categories: CategoryInfo[] = [
139
154
  { id: "systemPrompt", label: "System prompt", tokens: systemPromptTokens, color: "accent", glyph: CELL_FILLED },
140
155
  { id: "systemTools", label: "System tools", tokens: toolsTokens, color: "warning", glyph: CELL_FILLED },
@@ -155,8 +170,6 @@ export function computeContextBreakdown(
155
170
  },
156
171
  ];
157
172
 
158
- const usedTokens = categories.reduce((sum, c) => sum + c.tokens, 0);
159
-
160
173
  let autoCompactBufferTokens = 0;
161
174
  if (contextWindow > 0) {
162
175
  const compactionSettings = session.settings.getGroup("compaction") as CompactionSettings;
@@ -117,6 +117,8 @@ ENV
117
117
 
118
118
  # Skills & Rules
119
119
  {{#if skills.length}}
120
+ Skills are specialized knowledge. Scan descriptions for your task domain.
121
+ If a skill applies, you MUST read `skill://<name>` before proceeding.
120
122
  <skills>
121
123
  {{#each skills}}
122
124
  - {{name}}: {{description}}
@@ -0,0 +1,8 @@
1
+ You are an image-analysis assistant. The user attached an image to a model that cannot see images, so your description is injected into that model's context in place of the image. The downstream model relies entirely on your text — it never sees the pixels.
2
+
3
+ Core behavior:
4
+ - Be faithful and evidence-first: distinguish direct observations from inferences.
5
+ - Transcribe ALL visible text verbatim, preserving casing, punctuation, and layout order. Mark unreadable segments explicitly rather than guessing.
6
+ - NEVER fabricate occluded, blurry, or uncertain details — say what is uncertain.
7
+ - Be thorough but compact: prefer dense, information-rich prose over filler.
8
+ - Do not add meta commentary, preambles ("This image shows…"), or closing remarks. Output only the description.
@@ -0,0 +1,10 @@
1
+ Describe this image in enough detail that a model which cannot see it can reason about its content.
2
+
3
+ Cover, where present:
4
+ - The overall scene, subject, and what is happening.
5
+ - People, objects, and their relationships, positions, colors, and counts.
6
+ - All visible text, transcribed verbatim (OCR).
7
+ - UI/screenshot elements: labels, buttons, inputs, states, errors, highlighted or disabled controls.
8
+ - Diagrams, charts, tables: structure, axes, series, and the values they encode.
9
+
10
+ Flag anything ambiguous or unreadable. Output the description as plain prose only.
package/src/sdk.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  getOpenAICodexTransportDetails,
22
22
  prewarmOpenAICodexResponses,
23
23
  } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
24
+ import { FALLBACK_DIALECT, preferredDialect } from "@oh-my-pi/pi-catalog/identity";
24
25
  import type { Component } from "@oh-my-pi/pi-tui";
25
26
  import {
26
27
  $env,
@@ -40,7 +41,6 @@ import { AutoLearnController, buildAutoLearnInstructions } from "./autolearn/con
40
41
  import { loadCapability } from "./capability";
41
42
  import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
42
43
  import { bucketRules } from "./capability/rule-buckets";
43
- import { createApiKeyResolver } from "./config/api-key-resolver";
44
44
  import { shouldEnableAppendOnlyContext } from "./config/append-only-context-mode";
45
45
  import { ModelRegistry } from "./config/model-registry";
46
46
  import {
@@ -415,6 +415,8 @@ export interface CreateAgentSessionOptions {
415
415
  providerSessionId?: string;
416
416
  /** Optional provider-facing prompt cache key, distinct from request lineage. */
417
417
  providerPromptCacheKey?: string;
418
+ /** Absolute wall-clock deadline in Unix epoch milliseconds. */
419
+ deadline?: number;
418
420
 
419
421
  /** Custom tools to register (in addition to built-in tools). Accepts both CustomTool and ToolDefinition. */
420
422
  customTools?: (CustomTool | ToolDefinition)[];
@@ -567,10 +569,15 @@ export type DialectFormat = "auto" | "native" | Dialect;
567
569
 
568
570
  export function resolveDialect(
569
571
  format: DialectFormat,
570
- model: Pick<Model, "supportsTools"> | undefined,
572
+ model: (Pick<Model, "supportsTools"> & Partial<Pick<Model, "id">>) | undefined,
571
573
  ): Dialect | undefined {
572
574
  if (format === "native") return undefined;
573
- if (format === "auto") return model?.supportsTools === false ? "glm" : undefined;
575
+ if (format === "auto") {
576
+ if (model?.supportsTools !== false) return undefined;
577
+ if (!model.id) return "glm";
578
+ const preferred = preferredDialect(model.id);
579
+ return preferred === FALLBACK_DIALECT ? "glm" : preferred;
580
+ }
574
581
  return format;
575
582
  }
576
583
 
@@ -2458,6 +2465,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2458
2465
  onResponse,
2459
2466
  sessionId: providerSessionId,
2460
2467
  promptCacheKey: options.providerPromptCacheKey,
2468
+ deadline: options.deadline,
2461
2469
  transformContext,
2462
2470
  transformProviderContext,
2463
2471
  steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
@@ -2475,28 +2483,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2475
2483
  kimiApiFormat: settings.get("providers.kimiApiFormat") ?? "anthropic",
2476
2484
  preferWebsockets: preferOpenAICodexWebsockets,
2477
2485
  getToolContext: tc => toolContextStore.getContext(tc),
2478
- getApiKey: async (provider, ctx) => {
2479
- // Read agent.sessionId at call time so credential selection stays aligned
2480
- // with metadataResolver after /new, fork, resume, or branch switches.
2481
- // Retry steps (ctx carries an auth error) drive the central a/b/c
2482
- // policy — force-refresh the same account, then rotate to a sibling —
2483
- // and may legitimately yield no key when every account is exhausted.
2484
- if (ctx?.error !== undefined) {
2485
- return createApiKeyResolver(modelRegistry, provider, { sessionId: agent.sessionId })(ctx);
2486
- }
2487
- const key = await modelRegistry.getApiKeyForProvider(provider, agent.sessionId);
2488
- if (!key) {
2489
- throw new Error(`No API key found for provider "${provider}"`);
2490
- }
2491
- return key;
2492
- },
2486
+ getApiKey: requestModel => modelRegistry.resolver(requestModel, agent.sessionId),
2493
2487
  streamFn: (streamModel, context, streamOptions) => {
2494
2488
  const openrouterRoutingPreset = settings.get("providers.openrouterVariant");
2495
2489
  const openrouterVariant =
2496
2490
  openrouterRoutingPreset && openrouterRoutingPreset !== "default" ? openrouterRoutingPreset : undefined;
2491
+ const antigravityEndpointMode = settings.get("providers.antigravityEndpoint");
2497
2492
  return streamSimple(streamModel, context, {
2498
2493
  ...streamOptions,
2499
2494
  openrouterVariant: streamOptions?.openrouterVariant ?? openrouterVariant,
2495
+ antigravityEndpointMode: streamOptions?.antigravityEndpointMode ?? antigravityEndpointMode,
2500
2496
  });
2501
2497
  },
2502
2498
  cursorExecHandlers,