@oai404iao/pi-codex-web-search 0.1.1 → 0.2.0

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Alpha bootstrap candidate; initial npm publication is still pending.
4
4
 
5
- Peer floor: Pi 0.85.1; tested against 0.85.1.
5
+ Peer floor: Pi 0.86.1; tested against 0.86.1.
6
6
 
7
7
  Installs `web_search` and search activity rendering. Depends only on
8
8
  `pi-codex-runtime`, not core, imagegen or the old bundle.
@@ -10,6 +10,11 @@ Catalog-supported standalone profiles call `alpha/search` using Pi's model
10
10
  registry authentication. Hosted profiles require core; without it the tool stays
11
11
  inactive and explicit execution reports the missing provider capability.
12
12
 
13
+ Optional Code Mode discovery offers `codex_web__web_search` on standalone
14
+ profiles only, reusing the same authentication/client and capturing turn identity
15
+ and history before auth I/O. It requires an exact Code Mode grant and adds no
16
+ Code Mode dependency. Hosted placeholders remain direct; no direct tool is hidden.
17
+
13
18
  When core is present, the broker contributes response-local search capture and
14
19
  citation signatures without a second provider registration.
15
20
  Uses the existing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oai404iao/pi-codex-web-search",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "Unpublished pi-codex-web-search workspace for the staged Codex split",
6
6
  "type": "module",
@@ -28,9 +28,9 @@
28
28
  ]
29
29
  },
30
30
  "peerDependencies": {
31
- "@earendil-works/pi-ai": ">=0.85.1",
32
- "@earendil-works/pi-coding-agent": ">=0.85.1",
33
- "@earendil-works/pi-tui": ">=0.85.1",
31
+ "@earendil-works/pi-ai": ">=0.86.1",
32
+ "@earendil-works/pi-coding-agent": ">=0.86.1",
33
+ "@earendil-works/pi-tui": ">=0.86.1",
34
34
  "typebox": "*"
35
35
  },
36
36
  "peerDependenciesMeta": {
@@ -45,12 +45,12 @@
45
45
  }
46
46
  },
47
47
  "dependencies": {
48
- "@oai404iao/pi-codex-runtime": "0.2.0"
48
+ "@oai404iao/pi-codex-runtime": "0.3.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@earendil-works/pi-ai": "0.85.1",
52
- "@earendil-works/pi-coding-agent": "0.85.1",
53
- "@earendil-works/pi-tui": "0.85.1",
51
+ "@earendil-works/pi-ai": "0.86.1",
52
+ "@earendil-works/pi-coding-agent": "0.86.1",
53
+ "@earendil-works/pi-tui": "0.86.1",
54
54
  "@types/node": "^26.2.0",
55
55
  "tsx": "^4.20.6",
56
56
  "typebox": "^1.1.24",
@@ -77,5 +77,5 @@
77
77
  "engines": {
78
78
  "node": ">=22.19.0"
79
79
  },
80
- "gitHead": "61f1d82471fff63e3e9d5c0276b5e069c9fc8f1f"
80
+ "gitHead": "b02484cecad9081886797bf9b4438135aaae1e07"
81
81
  }
@@ -0,0 +1,23 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { registerCodeModeContribution, type OwnedCodeModeTool } from "@oai404iao/pi-codex-runtime/internal/code-mode-contributions";
3
+ import { loadModelSettings } from "@oai404iao/pi-codex-runtime/internal/model-catalog/runtime";
4
+ import { standaloneWebSearch, webSearchToolSchema, type WebSearchInput } from "./tools/web-search.js";
5
+
6
+ export function registerSearchContribution(pi: ExtensionAPI): void {
7
+ const enabled = (ctx: ExtensionContext) => {
8
+ const settings = loadModelSettings(ctx.model, ctx.cwd);
9
+ return settings.enabled && settings.webSearchImplementation === "standalone";
10
+ };
11
+ const tool: OwnedCodeModeTool = {
12
+ name: "web_search", effect: "read", parallel: true,
13
+ description: "Run the installed Codex standalone alpha/search client with the current model/profile and Pi credentials. Returns text and structured results to JavaScript. Sends the owner's bounded recent conversation tail. Hosted placeholders are never executed; Pi direct tool hooks are not inherited.",
14
+ parameters: webSearchToolSchema,
15
+ async invoke(input, ctx) {
16
+ ctx.signal.throwIfAborted();
17
+ if (!ctx.pi || !enabled(ctx.pi)) throw new Error("Only an enabled Codex standalone search profile can be nested; hosted search stays direct");
18
+ const result = await standaloneWebSearch(input as WebSearchInput, ctx.pi, ctx.signal);
19
+ return { value: { text: result.content.map((part) => part.text).join("\n"), results: result.details.results } };
20
+ },
21
+ };
22
+ registerCodeModeContribution(pi, "codex_web", (ctx) => enabled(ctx) ? [tool] : []);
23
+ }
package/src/index.ts CHANGED
@@ -1,20 +1,23 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { addPackageTool, ensureCodexServices } from "@oai404iao/pi-codex-runtime";
3
+ import { registerCodeModeOwnedTool } from "@oai404iao/pi-codex-runtime/internal/code-mode-contributions";
3
4
  import { currentCodexTurn, resolveCodexRequestIdentity } from "@oai404iao/pi-codex-runtime/internal/codex-wire-identity";
4
5
  import { createWebSearchToolDefinition } from "./tools/web-search.js";
5
6
  import { createWebSearchCapture } from "./tools/web-search/capture.js";
6
7
  import { registerWebSearchActivityRenderer } from "./tools/web-search/render.js";
8
+ import { registerSearchContribution } from "./code-mode-adapter.js";
7
9
 
8
10
  export default function codexWebSearch(pi: ExtensionAPI): void {
9
11
  const broker = ensureCodexServices(pi);
10
12
  if (!broker.claim("package:web-search")) return;
13
+ registerSearchContribution(pi);
11
14
  broker.addPresentation("web_search", {
12
15
  clear() {}, flush() {}, scheduleFlush() {},
13
16
  registerRenderers: () => registerWebSearchActivityRenderer(pi),
14
17
  streamEffects: () => ({ createEventObserver: createWebSearchCapture }),
15
18
  });
16
19
  addPackageTool(broker, "web_search", () => {
17
- pi.registerTool(createWebSearchToolDefinition({
20
+ registerCodeModeOwnedTool(pi, createWebSearchToolDefinition({
18
21
  getCurrentTurnId: sessionId => currentCodexTurn(sessionId)?.turnId,
19
22
  getRequestIdentity: sessionId => resolveCodexRequestIdentity(sessionId, undefined, "turn"),
20
23
  hasProviderRuntime: () => broker.coreEnabled,
@@ -241,6 +241,7 @@ export async function standaloneWebSearch(
241
241
  signal?: AbortSignal,
242
242
  invocation: StandaloneWebSearchInvocation = {},
243
243
  ) {
244
+ signal?.throwIfAborted();
244
245
  const model = ctx.model;
245
246
  if (!model || !ctx.modelRegistry) throw new Error("No active model is available for standalone web search.");
246
247
  const settings = loadModelSettings(model, ctx.cwd);
@@ -257,15 +258,6 @@ export async function standaloneWebSearch(
257
258
  if (input.image_query?.length && !contentTypes.includes("image")) {
258
259
  throw new Error("Image search is disabled by the current model profile.");
259
260
  }
260
- const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
261
- if (!auth.ok) throw new Error(auth.error);
262
- if (!hasCodexRequestAuth({
263
- modelHeaders: model.headers,
264
- auth: { apiKey: auth.apiKey, headers: auth.headers },
265
- })) {
266
- throw new Error(`No request authentication for provider: ${model.provider}`);
267
- }
268
-
269
261
  const url = resolveCodexApiEndpoint(model.baseUrl, settings.apiKeyMode, "alpha/search");
270
262
  const piSessionId = ctx.sessionManager?.getSessionId();
271
263
  const identity = invocation.identity
@@ -293,6 +285,14 @@ export async function standaloneWebSearch(
293
285
  model: model.id,
294
286
  })
295
287
  : undefined;
288
+ // A nested cell may survive a user-turn rollover while auth is pending.
289
+ // Identity and visible history must describe this invocation, not that later turn.
290
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
291
+ signal?.throwIfAborted();
292
+ if (!auth.ok) throw new Error(auth.error);
293
+ if (!hasCodexRequestAuth({ modelHeaders: model.headers, auth: { apiKey: auth.apiKey, headers: auth.headers } })) {
294
+ throw new Error(`No request authentication for provider: ${model.provider}`);
295
+ }
296
296
  const response = await fetch(url, {
297
297
  method: "POST",
298
298
  headers: buildCodexJsonHeaders({
@@ -322,6 +322,7 @@ export async function standaloneWebSearch(
322
322
  throw new Error(`Standalone web search failed: HTTP ${response.status}: ${await response.text()}`);
323
323
  }
324
324
  const result = await response.json() as StandaloneSearchResponse;
325
+ signal?.throwIfAborted();
325
326
  if (typeof result.output !== "string" || !result.output.trim()) {
326
327
  throw new Error("Standalone web search returned no output.");
327
328
  }