@howaboua/pi-codex-conversion 1.0.31-dev.4.26e07fb → 1.0.31-dev.5.36974a1

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.
package/README.md CHANGED
@@ -1,20 +1,31 @@
1
1
  # pi-codex-conversion
2
2
 
3
- Codex-oriented adapter for [Pi](https://github.com/badlogic/pi-mono).
3
+ Codex-style tools for [Pi](https://github.com/badlogic/pi-mono).
4
4
 
5
- This package replaces Pi's default Codex/GPT experience with a narrower Codex-like surface while staying close to Pi's own runtime and prompt construction:
5
+ > [!NOTE]
6
+ > Use the npm package for normal installs. Avoid `pi install git:...` unless you know you want the development checkout; see [Development checkout](#development-checkout).
6
7
 
7
- - swaps active tools to `exec_command`, `write_stdin`, `apply_patch`, `view_image`, plus native OpenAI Codex Responses `web_search` and `image_generation` on `openai-codex`
8
- - saves native Codex image-generation outputs into `.pi/openai-codex-images/` at the workspace/repo root and mirrors the newest image to `.pi/openai-codex-images/latest.png`
9
- - preserves Pi's composed system prompt and applies a narrow Codex-oriented delta on top
10
- - renders exec activity with Codex-style command and background-terminal labels
11
- - renders `apply_patch` calls with Codex-style `Added` / `Edited` / `Deleted` diff blocks and Pi-style colored diff lines
12
- - targets modern Pi tool/rendering APIs and is aligned with Pi `0.74.x` / the `@earendil-works/*` package scope
8
+ GPT/Codex models are strongest when the tool surface looks like the Codex CLI they were trained around: shell commands, resumable terminal sessions, and patch-based edits. This extension brings that workflow to Pi while keeping Pi's runtime, sessions, project context, skills, and UI.
13
9
 
14
- ![Available tools](./available-tools.png)
10
+ The point is to give the model tools it already knows how to use well: shell-first inspection, resumable command sessions, and large one-shot patch edits instead of piecemeal read/edit/write steps.
15
11
 
16
- > [!NOTE]
17
- > Native OpenAI Codex Responses web search activity is surfaced as merged foldable status messages. Pi still does not expose native web-search usage as true tool-call rows.
12
+ ## Install
13
+
14
+ ```bash
15
+ pi install npm:@howaboua/pi-codex-conversion
16
+ ```
17
+
18
+ ## Development checkout
19
+
20
+ The Git checkout is mostly for development and mirrors the maintainer workflow. If you run it directly, you may need to build the bundled `apply_patch` binary for your platform.
21
+
22
+ Run the current checkout without installing globally:
23
+
24
+ ```bash
25
+ pi --no-extensions --no-skills -e /path/to/pi-codex-conversion
26
+ ```
27
+
28
+ ![Available tools](./available-tools.png)
18
29
 
19
30
  ## Active tools in adapter mode
20
31
 
@@ -34,123 +45,32 @@ Notably:
34
45
  - file creation and edits should default to `apply_patch`
35
46
  - Pi may still expose additional runtime tools such as `parallel`; the prompt is written to tolerate that instead of assuming a fixed four-tool universe
36
47
 
37
- ## Layout
48
+ ## What changes in Pi
38
49
 
39
- - `src/index.ts` extension entrypoint, model gating, tool-set swapping, prompt transformation
40
- - `src/adapter/` model detection and active-tool constants
41
- - `src/tools/` Pi tool wrappers, exec session management, and execution rendering
42
- - `src/shell/` shell tokenization, parsing, and exploration summaries
43
- - `src/patch/` patch parsing, path policy, and execution
44
- - `src/prompt/` Codex delta transformer over Pi's composed prompt
45
- - `tests/` — deterministic unit tests
50
+ - Adapter mode activates automatically for OpenAI `gpt*` and `codex*` models, then restores the previous tool set when you switch away.
51
+ - Pi's composed prompt is preserved; the extension only adds a small Codex-style tool-use nudge.
52
+ - Shell activity is rendered with Codex-like labels such as `Ran`, `Explored`, `Read`, and background-terminal status.
53
+ - `apply_patch` renders as Codex-style `Added` / `Edited` / `Deleted` blocks, including inline partial-failure state.
54
+ - Native web search appears as a compact expandable summary after a turn, with queries and sources in the expanded view.
55
+ - Generated images are saved under `.pi/openai-codex-images/` at the workspace/repo root, with the latest image mirrored to `latest.png`.
46
56
 
47
- ## Checks
48
-
49
- ```bash
50
- npm run typecheck
51
- npm test
52
- npm run check
53
- ```
54
-
55
- ## Examples
57
+ ## Command rendering examples
56
58
 
57
59
  - `rg -n foo src` -> `Explored / Search foo in src`
58
60
  - `rg --files src | head -n 50` -> `Explored / List src`
59
61
  - `cat README.md` -> `Explored / Read README.md`
60
- - `exec_command({ cmd: "npm test", yield_time_ms: 1000 })` may return `session_id`, then continue with `write_stdin`
61
- - for short or non-interactive commands, omitting `yield_time_ms` is preferred; tiny non-interactive waits are clamped upward to avoid unnecessary follow-up calls
62
- - `write_stdin({ session_id, chars: "" })` renders like `Waited for background terminal` and is meant for occasional polling, not tight repoll loops
63
- - `write_stdin({ session_id, chars: "y\\n" })` renders like `Interacted with background terminal`
64
- - `view_image({ path: "/absolute/path/to/screenshot.png" })` is available on image-capable models
65
- - `web_search` is surfaced only on `openai-codex`, and the adapter rewrites it into the native OpenAI Responses `type: "web_search"` payload instead of executing a local function tool
66
- - `image_generation` is surfaced only on image-capable `openai-codex` models, and the adapter rewrites it into the native OpenAI Responses `type: "image_generation", output_format: "png"` payload instead of executing a local function tool
67
- - native `image_generation` outputs are saved under `.pi/openai-codex-images/` at the workspace/repo root, with the newest image mirrored to `.pi/openai-codex-images/latest.png`
68
- - when native web search is available, the adapter shows a one-time session notice and merged foldable search-activity messages instead of native tool-call rows
69
- - `apply_patch` partial failures stay inline in the patch row so successful and failed file entries can be seen together
62
+ - `npm test` -> `Ran npm test`
63
+ - `write_stdin({ session_id, chars: "" })` -> `Waited for background terminal`
64
+ - `write_stdin({ session_id, chars: "y\n" })` -> `Interacted with background terminal`
70
65
 
71
66
  Raw command output is still available by expanding the tool result.
72
67
 
73
- ## Install
74
-
75
- ```bash
76
- pi install npm:@howaboua/pi-codex-conversion
77
- ```
78
-
79
- Local development:
80
-
81
- ```bash
82
- pi install ./pi-codex-conversion
83
- ```
84
-
85
- Alternative Git install:
86
-
87
- ```bash
88
- pi install git:github.com/IgorWarzocha/pi-codex-conversion
89
- ```
90
-
91
- ## Publishing
92
-
93
- This package is already configured for public npm publishes via:
94
-
95
- - `publishConfig.access = "public"`
96
- - `prepublishOnly` / `prepack` checks
97
-
98
- Useful commands:
99
-
100
- ```bash
101
- npm run publish:dry-run
102
- npm run publish:dev
103
- npm run release:dev
104
- ```
105
-
106
- What they do:
107
-
108
- - `npm run publish:dry-run` — inspect what would be published
109
- - `npm run publish:dev` — publish the current version under the `dev` dist-tag
110
- - `npm run release:dev` — bump the package to the next `-dev.N` prerelease and publish it under the `dev` dist-tag
111
-
112
- Typical flow:
113
-
114
- ```bash
115
- npm login
116
- npm run publish:dry-run
117
- npm run release:dev
118
- ```
119
-
120
- For modern npm auth, just run `npm login` and complete the browser flow when prompted.
121
-
122
- After publishing, install the dev build with:
123
-
124
- ```bash
125
- pi install npm:@howaboua/pi-codex-conversion@dev
126
- ```
127
-
128
- ## Prompt behavior
129
-
130
- The adapter does not build a standalone replacement prompt anymore. Instead it:
131
-
132
- - keeps Pi's tool descriptions, Pi docs section, AGENTS/project context, skills inventory, and date/cwd when Pi already surfaced them
133
- - adds the current shell to the transformed prompt so quoting and escaping can match the runtime environment
134
- - rewrites the top-level role framing to Codex-style wording
135
- - adds a small Codex delta to the existing `Guidelines` section
136
-
137
- That keeps the prompt much closer to `pi-mono` while still steering the model toward Codex-style tool use.
138
-
139
- ## Notes
68
+ ## Details worth knowing
140
69
 
141
- - Adapter mode activates automatically for OpenAI `gpt*` and `codex*` models.
142
- - When you switch away from those models, Pi restores the previous active tool set.
143
- - `view_image` resolves paths against the active session cwd and only exposes `detail: "original"` for Codex-family image-capable models.
144
- - `web_search` is exposed only for the `openai-codex` provider and is forwarded as the native OpenAI Codex Responses web search tool.
145
- - `image_generation` is exposed only for image-capable `openai-codex` models and is forwarded as the native OpenAI Codex Responses image-generation tool.
146
- - generated images are written under `.pi/openai-codex-images/` at the workspace/repo root, and the latest image is mirrored to `.pi/openai-codex-images/latest.png`.
147
- - `apply_patch` paths stay restricted to the current working directory.
148
- - partial `apply_patch` failures stay in the original patch block and highlight the failed entry instead of adding a second warning row.
149
- - `exec_command` / `write_stdin` use a custom PTY-backed session manager via `node-pty` for interactive sessions.
150
- - tiny `exec_command` waits are clamped for non-interactive commands so short runs do not burn an avoidable follow-up tool call.
151
- - empty `write_stdin` polls are clamped to a meaningful minimum wait so long-running processes are not repolled too aggressively.
152
- - PTY output handling applies basic terminal rewrite semantics (`\r`, `\b`, erase-in-line, and common escape cleanup) so interactive redraws replay sensibly.
153
- - Skills inventory is reintroduced in a Codex-style section when Pi's composed prompt already exposed the underlying Pi skills inventory.
70
+ - `exec_command` and `write_stdin` use a PTY-backed session manager for interactive commands and long-running processes.
71
+ - `apply_patch` accepts absolute paths as-is and resolves relative paths against the current working directory.
72
+ - Shell `apply_patch` is also available inside `exec_command`, but the dedicated `apply_patch` tool is preferred unless you are chaining edits with other shell steps.
73
+ - Native `web_search` and `image_generation` are forwarded to OpenAI Codex Responses tools rather than executed as local function tools.
154
74
 
155
75
  ## License
156
76
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/pi-codex-conversion",
3
- "version": "1.0.31-dev.4.26e07fb",
3
+ "version": "1.0.31-dev.5.36974a1",
4
4
  "description": "Codex-oriented tool and prompt adapter for pi coding agent",
5
5
  "type": "module",
6
6
  "repository": {
@@ -44,7 +44,7 @@
44
44
  "publish:dev": "npm publish --tag dev",
45
45
  "release:dev": "npm version prerelease --preid dev && npm publish --tag dev",
46
46
  "prepack": "npm run check",
47
- "prepublishOnly": "npm run verify:apply-patch-binaries && npm run check",
47
+ "prepublishOnly": "npm run verify:apply-patch-binaries",
48
48
  "build:apply-patch": "node scripts/build-apply-patch-binary.mjs",
49
49
  "sync:apply-patch-source": "node scripts/sync-apply-patch-source.mjs",
50
50
  "verify:apply-patch-binaries": "node scripts/verify-apply-patch-binaries.mjs"
package/src/index.ts CHANGED
@@ -165,8 +165,9 @@ function enableAdapter(pi: ExtensionAPI, ctx: ExtensionContext, state: AdapterSt
165
165
  const toolNames = mergeAdapterTools(pi.getActiveTools(), getAdapterToolNames(ctx));
166
166
  if (!state.enabled) {
167
167
  // Preserve the previous active set once so switching away from Codex-like
168
- // models restores the user's existing Pi tool configuration.
169
- state.previousToolNames = pi.getActiveTools();
168
+ // models restores the user's existing Pi tool configuration. Strip adapter
169
+ // tools in case a fresh session starts from persisted/mixed active tools.
170
+ state.previousToolNames = stripAdapterTools(pi.getActiveTools());
170
171
  state.enabled = true;
171
172
  }
172
173
  pi.setActiveTools(toolNames);
@@ -210,7 +211,7 @@ export function mergeAdapterTools(activeTools: string[], adapterTools: string[])
210
211
  }
211
212
 
212
213
  export function restoreTools(previousTools: string[], activeTools: string[]): string[] {
213
- const restored = [...previousTools];
214
+ const restored = stripAdapterTools(previousTools);
214
215
  for (const toolName of activeTools) {
215
216
  if (!ADAPTER_TOOL_NAMES.includes(toolName) && !restored.includes(toolName)) {
216
217
  restored.push(toolName);
@@ -219,6 +220,10 @@ export function restoreTools(previousTools: string[], activeTools: string[]): st
219
220
  return restored;
220
221
  }
221
222
 
223
+ export function stripAdapterTools(toolNames: string[]): string[] {
224
+ return toolNames.filter((toolName) => !ADAPTER_TOOL_NAMES.includes(toolName));
225
+ }
226
+
222
227
  function hasAdapterTools(activeTools: string[]): boolean {
223
228
  return activeTools.some((toolName) => ADAPTER_TOOL_NAMES.includes(toolName));
224
229
  }
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
- import { dirname, isAbsolute, relative, resolve } from "node:path";
2
+ import { dirname, isAbsolute, resolve } from "node:path";
3
3
  import { DiffError } from "./types.ts";
4
4
 
5
5
  export function normalizePatchPath({ path }: { path: string }): string {
@@ -8,20 +8,15 @@ export function normalizePatchPath({ path }: { path: string }): string {
8
8
  return withoutAt.replace(/^['"]|['"]$/g, "");
9
9
  }
10
10
 
11
- // Relative patch paths stay anchored to ctx.cwd. Absolute patch paths are
12
- // accepted as-is so the adapter can match Codex-style path usage.
11
+ // Match Codex apply_patch path handling: absolute patch paths are accepted
12
+ // as-is, while relative paths are resolved against ctx.cwd.
13
13
  export function resolvePatchPath({ cwd, patchPath }: { cwd: string; patchPath: string }): string {
14
14
  const normalized = normalizePatchPath({ path: patchPath });
15
15
  if (!normalized) {
16
16
  throw new DiffError("Patch path cannot be empty");
17
17
  }
18
18
 
19
- const absolutePath = isAbsolute(normalized) ? normalized : resolve(cwd, normalized);
20
- const rel = relative(cwd, absolutePath);
21
- if (!isAbsolute(normalized) && (rel.startsWith("..") || isAbsolute(rel))) {
22
- throw new DiffError(`Path escapes working directory: ${normalized}`);
23
- }
24
- return absolutePath;
19
+ return isAbsolute(normalized) ? normalized : resolve(cwd, normalized);
25
20
  }
26
21
 
27
22
  export function openFileAtPath({ cwd, path }: { cwd: string; path: string }): string {
@@ -5,15 +5,11 @@ export interface PromptSkill {
5
5
  }
6
6
 
7
7
  const CODEX_GUIDELINES = [
8
- "Prefer a single `apply_patch` call that updates all related files together when one coherent patch will do.",
9
- "When making coordinated edits across multiple files, include them in one `apply_patch` call instead of splitting them into separate patches.",
10
- "When multiple tool calls are independent, emit them together so they can execute in parallel instead of serializing them.",
11
- "Use `parallel` only when tool calls are independent and can safely run at the same time.",
12
- "Use `write_stdin` when an exec session returns `session_id`, and continue until `exit_code` is present.",
13
- "For short or non-interactive commands, prefer the default `exec_command` wait instead of a tiny `yield_time_ms` that forces an extra follow-up call.",
14
- "When polling a running exec session with empty `chars`, wait meaningfully between polls and do not repeatedly poll by reflex.",
15
- "Do not request `tty` unless interactive terminal behavior is required.",
16
- "Native `image_generation` outputs are saved under `.pi/openai-codex-images/` and mirrored to `.pi/openai-codex-images/latest.png`. Use `view_image` only when pixel-level inspection is necessary.",
8
+ "Use `exec_command` for shell commands, file inspection, builds, and tests; prefer `rg` / `rg --files` for discovery and focused commands over truncation.",
9
+ "Use `apply_patch` for text-file changes, including creates/deletes/moves; group related multi-file edits into one patch.",
10
+ "Prefer the `apply_patch` tool; use shell `apply_patch` only when chaining edits with other shell steps.",
11
+ "Use `write_stdin` only for running `exec_command` sessions; poll sparingly.",
12
+ "Run independent tool calls in parallel when practical.",
17
13
  ];
18
14
 
19
15
  function insertBeforeTrailingContext(prompt: string, section: string): string {
@@ -88,9 +84,9 @@ function injectSkills(prompt: string, skills: PromptSkill[]): string {
88
84
  }
89
85
 
90
86
  function injectGuidelines(prompt: string): string {
91
- const match = prompt.match(/(^Guidelines:\n)([\s\S]*?)(\n\n(?:Pi documentation:|# Project Context|# Skills|Current date:))/m);
87
+ const match = prompt.match(/(^Guidelines:\n)([\s\S]*?)(\n\n(?=Pi documentation\b|# Project Context|# Skills|Current date:))/m);
92
88
  if (!match || match.index === undefined) {
93
- const fallbackSection = `Codex mode guidelines:\n${CODEX_GUIDELINES.map((line) => `- ${line}`).join("\n")}`;
89
+ const fallbackSection = `Guidelines:\n${CODEX_GUIDELINES.map((line) => `- ${line}`).join("\n")}`;
94
90
  return insertBeforeTrailingContext(prompt, fallbackSection);
95
91
  }
96
92
 
@@ -280,12 +280,8 @@ export function registerApplyPatchTool(pi: ExtensionAPI): void {
280
280
  pi.registerTool({
281
281
  name: "apply_patch",
282
282
  label: "apply_patch",
283
- description: "Use `apply_patch` to edit files. Send the full patch in `input`.",
283
+ description: "Apply a patch to create, edit, delete, or move files.",
284
284
  promptSnippet: "Edit files with a patch.",
285
- promptGuidelines: [
286
- "Prefer apply_patch for focused textual edits instead of rewriting whole files.",
287
- "When one task needs coordinated edits across multiple files, send them in a single apply_patch call when one coherent patch will do.",
288
- ],
289
285
  parameters: APPLY_PATCH_PARAMETERS,
290
286
  prepareArguments: prepareApplyPatchArguments,
291
287
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
@@ -8,16 +8,16 @@ import { formatUnifiedExecResult } from "./unified-exec-format.ts";
8
8
 
9
9
  const EXEC_COMMAND_PARAMETERS = Type.Object({
10
10
  cmd: Type.String({ description: "Shell command to execute." }),
11
- workdir: Type.Optional(Type.String({ description: "Optional working directory; defaults to the current turn cwd." })),
12
- shell: Type.Optional(Type.String({ description: "Optional shell binary; defaults to the user's shell." })),
11
+ workdir: Type.Optional(Type.String({ description: "Defaults to current cwd." })),
12
+ shell: Type.Optional(Type.String({ description: "Defaults to the user's shell." })),
13
13
  tty: Type.Optional(
14
14
  Type.Boolean({
15
- description: "Whether to allocate a TTY for the command. Defaults to false (plain pipes); set to true to open a PTY and access TTY process.",
15
+ description: "Allocate a TTY. Defaults to false.",
16
16
  }),
17
17
  ),
18
- yield_time_ms: Type.Optional(Type.Number({ description: "How long to wait in milliseconds for output before yielding." })),
19
- max_output_tokens: Type.Optional(Type.Number({ description: "Maximum number of tokens to return. Excess output will be truncated." })),
20
- login: Type.Optional(Type.Boolean({ description: "Whether to run the shell with -l/-i semantics. Defaults to true." })),
18
+ yield_time_ms: Type.Optional(Type.Number({ description: "Wait for output before yielding." })),
19
+ max_output_tokens: Type.Optional(Type.Number({ description: "Excess output will be truncated." })),
20
+ login: Type.Optional(Type.Boolean({ description: "Whether to run through a login-style shell so user PATH/toolchain setup is loaded. Defaults to true." })),
21
21
  });
22
22
 
23
23
  interface ExecCommandParams {
@@ -136,14 +136,8 @@ export function registerExecCommandTool(pi: ExtensionAPI, tracker: ExecCommandTr
136
136
  pi.registerTool({
137
137
  name: "exec_command",
138
138
  label: "exec_command",
139
- description: "Runs a command in a PTY, returning output or a session ID for ongoing interaction.",
139
+ description: "Runs a shell command, returning output or a session ID for ongoing interaction.",
140
140
  promptSnippet: "Run a command.",
141
- promptGuidelines: [
142
- "Use exec_command for search, listing files, and local text-file reads.",
143
- "Prefer rg or rg --files when possible.",
144
- "For short or non-interactive commands, omit `yield_time_ms` so the default wait can avoid unnecessary follow-up calls.",
145
- "Keep tty disabled unless the command truly needs interactive terminal behavior.",
146
- ],
147
141
  parameters: EXEC_COMMAND_PARAMETERS,
148
142
  prepareArguments: prepareExecCommandArguments,
149
143
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
@@ -79,7 +79,7 @@ export function rewriteNativeImageGenerationTool(payload: unknown, model: Extens
79
79
 
80
80
  export function createImageGenerationTool(): ToolDefinition<typeof IMAGE_GENERATION_PARAMETERS> {
81
81
  const description =
82
- "Generate an image. Native openai-codex image_generation outputs are saved under `.pi/openai-codex-images/` and mirrored to `.pi/openai-codex-images/latest.png`. Use `view_image` only when pixel-level inspection is necessary.";
82
+ "Generate an image. Outputs are saved under `.pi/openai-codex-images/` and mirrored to `.pi/openai-codex-images/latest.png`.";
83
83
  return {
84
84
  name: "image_generation",
85
85
  label: "image_generation",
@@ -12,7 +12,7 @@ import { Text } from "@earendil-works/pi-tui";
12
12
 
13
13
  const VIEW_IMAGE_UNSUPPORTED_MESSAGE = "view_image is not allowed because you do not support image inputs";
14
14
  const DETAIL_DESCRIPTION =
15
- "Optional detail override. The only supported value is `original`; omit this field for default resized behavior. Use `original` to preserve the file's original resolution instead of resizing to fit. This is important when high-fidelity image perception or precise localization is needed, especially for CUA agents.";
15
+ "Use `original` to preserve the file's original resolution; omit for default resized behavior.";
16
16
 
17
17
  interface ViewImageParams {
18
18
  path: string;
@@ -37,7 +37,7 @@ type ViewImageParameters = ReturnType<typeof createViewImageParameters>;
37
37
 
38
38
  function createViewImageParameters(allowOriginalDetail: boolean) {
39
39
  const properties: Record<string, TSchema> = {
40
- path: Type.String({ description: "Local filesystem path to an image file" }),
40
+ path: Type.String({ description: "Local image file path." }),
41
41
  };
42
42
  if (allowOriginalDetail) {
43
43
  properties.detail = Type.Optional(Type.String({ description: DETAIL_DESCRIPTION }));
@@ -143,10 +143,8 @@ export function createViewImageTool(options: CreateViewImageToolOptions = {}): T
143
143
  return {
144
144
  name: "view_image",
145
145
  label: "view_image",
146
- description:
147
- "View a local image from the filesystem (only use if given a full filepath by the user, and the image isn't already attached to the thread context within <image ...> tags).",
146
+ description: "View a local image file.",
148
147
  promptSnippet: "View a local image from the filesystem.",
149
- promptGuidelines: ["Use view_image only for image files. Use exec_command for text-file inspection."],
150
148
  parameters,
151
149
  prepareArguments: prepareViewImageArguments,
152
150
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
@@ -6,10 +6,10 @@ import type { ExecSessionManager, UnifiedExecResult } from "./exec-session-manag
6
6
  import { formatUnifiedExecResult } from "./unified-exec-format.ts";
7
7
 
8
8
  const WRITE_STDIN_PARAMETERS = Type.Object({
9
- session_id: Type.Number({ description: "Identifier of the running unified exec session." }),
10
- chars: Type.Optional(Type.String({ description: "Bytes to write to stdin. May be empty to poll." })),
11
- yield_time_ms: Type.Optional(Type.Number({ description: "How long to wait (in milliseconds) for output before yielding." })),
12
- max_output_tokens: Type.Optional(Type.Number({ description: "Maximum number of tokens to return. Excess output will be truncated." })),
9
+ session_id: Type.Number({ description: "Running exec session ID." }),
10
+ chars: Type.Optional(Type.String({ description: "Bytes to write. Empty polls." })),
11
+ yield_time_ms: Type.Optional(Type.Number({ description: "Wait for output before yielding." })),
12
+ max_output_tokens: Type.Optional(Type.Number({ description: "Excess output will be truncated." })),
13
13
  });
14
14
 
15
15
  interface WriteStdinParams {
@@ -108,12 +108,8 @@ export function registerWriteStdinTool(pi: ExtensionAPI, sessions: ExecSessionMa
108
108
  pi.registerTool({
109
109
  name: "write_stdin",
110
110
  label: "write_stdin",
111
- description: "Writes characters to an existing unified exec session and returns recent output.",
111
+ description: "Writes to or polls a running exec session.",
112
112
  promptSnippet: "Write to an exec session.",
113
- promptGuidelines: [
114
- "Use empty `chars` only to poll a running exec session.",
115
- "When polling with empty `chars`, wait meaningfully between polls and do not repeatedly poll by reflex.",
116
- ],
117
113
  parameters: WRITE_STDIN_PARAMETERS,
118
114
  async execute(_toolCallId, params) {
119
115
  const typed = parseWriteStdinParams(params);