@oh-my-pi/pi-coding-agent 17.1.8 → 17.2.1
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/CHANGELOG.md +110 -0
- package/dist/{CHANGELOG-vt8ene9g.md → CHANGELOG-dj46zzrm.md} +110 -0
- package/dist/cli.js +10009 -9654
- package/dist/{template-dys3vk5b.js → template-8vdv6xb4.js} +1 -1
- package/dist/types/advisor/runtime.d.ts +8 -2
- package/dist/types/advisor/transcript-recorder.d.ts +15 -0
- package/dist/types/auto-thinking/classifier.d.ts +9 -2
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/session-picker.d.ts +14 -7
- package/dist/types/collab/guest.d.ts +21 -2
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/model-discovery.d.ts +12 -0
- package/dist/types/config/settings-schema.d.ts +73 -8
- package/dist/types/cursor-bridge-tools.d.ts +54 -0
- package/dist/types/cursor.d.ts +193 -8
- package/dist/types/edit/edit-clipboard.d.ts +19 -0
- package/dist/types/edit/hashline/diff.d.ts +9 -1
- package/dist/types/edit/index.d.ts +8 -1
- package/dist/types/edit/streaming.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +19 -2
- package/dist/types/extensibility/extensions/types.d.ts +25 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +25 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/local-protocol.d.ts +3 -2
- package/dist/types/internal-urls/security-protocol.d.ts +15 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/launch/terminal-output-worker-client.d.ts +5 -0
- package/dist/types/launch/terminal-output-worker-protocol.d.ts +14 -0
- package/dist/types/launch/terminal-output-worker.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +33 -4
- package/dist/types/modes/components/codex-reset-fireworks.d.ts +51 -0
- package/dist/types/modes/components/read-tool-group.d.ts +12 -0
- package/dist/types/modes/components/session-selector.d.ts +6 -0
- package/dist/types/modes/components/status-line/component.d.ts +23 -0
- package/dist/types/modes/components/tool-execution.d.ts +16 -7
- package/dist/types/modes/components/usage-row.d.ts +2 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +26 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +4 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +16 -0
- package/dist/types/modes/theme/theme.d.ts +12 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/sdk.d.ts +18 -0
- package/dist/types/security/auth.d.ts +30 -0
- package/dist/types/security/cloud.d.ts +79 -0
- package/dist/types/security/comparison.d.ts +49 -0
- package/dist/types/security/contracts/ids.d.ts +15 -0
- package/dist/types/security/contracts/index.d.ts +4 -0
- package/dist/types/security/contracts/schemas.d.ts +660 -0
- package/dist/types/security/contracts/types.d.ts +234 -0
- package/dist/types/security/contracts/validation.d.ts +5 -0
- package/dist/types/security/coordinator.d.ts +100 -0
- package/dist/types/security/importers/codex-security.d.ts +7 -0
- package/dist/types/security/importers/index.d.ts +2 -0
- package/dist/types/security/importers/sarif.d.ts +9 -0
- package/dist/types/security/index.d.ts +13 -0
- package/dist/types/security/preflight.d.ts +61 -0
- package/dist/types/security/provenance.d.ts +24 -0
- package/dist/types/security/publication.d.ts +78 -0
- package/dist/types/security/remediation.d.ts +27 -0
- package/dist/types/security/resource-output.d.ts +8 -0
- package/dist/types/security/sarif.d.ts +2 -0
- package/dist/types/security/store.d.ts +40 -0
- package/dist/types/session/agent-session-events.d.ts +2 -0
- package/dist/types/session/agent-session-types.d.ts +36 -1
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/claude-session-store.d.ts +13 -0
- package/dist/types/session/codex-session-store.d.ts +14 -0
- package/dist/types/session/credential-pin.d.ts +58 -0
- package/dist/types/session/foreign-session-import.d.ts +15 -0
- package/dist/types/session/foreign-session-jsonl.d.ts +9 -0
- package/dist/types/session/foreign-session-store.d.ts +23 -0
- package/dist/types/session/session-advisors.d.ts +47 -2
- package/dist/types/session/session-entries.d.ts +19 -1
- package/dist/types/session/session-handoff.d.ts +4 -1
- package/dist/types/session/session-loader.d.ts +2 -0
- package/dist/types/session/session-maintenance.d.ts +1 -1
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-tools.d.ts +25 -1
- package/dist/types/session/turn-recovery.d.ts +10 -4
- package/dist/types/slash-commands/helpers/security.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/executor.d.ts +14 -2
- package/dist/types/task/render.d.ts +7 -0
- package/dist/types/thinking.d.ts +18 -6
- package/dist/types/tools/acp-bridge.d.ts +28 -5
- package/dist/types/tools/builtin-names.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +17 -1
- package/dist/types/tools/hub/launch.d.ts +5 -1
- package/dist/types/tools/index.d.ts +10 -2
- package/dist/types/tools/path-utils.d.ts +19 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/tools/security-scan.d.ts +96 -0
- package/dist/types/tools/xdev.d.ts +2 -1
- package/dist/types/utils/changelog.d.ts +19 -0
- package/dist/types/utils/git.d.ts +10 -0
- package/dist/types/utils/jj.d.ts +11 -4
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/scripts/security-compare.ts +40 -0
- package/src/advisor/runtime.ts +138 -12
- package/src/advisor/transcript-recorder.ts +56 -0
- package/src/auto-thinking/classifier.ts +48 -10
- package/src/cli/args.ts +6 -0
- package/src/cli/flag-tables.ts +2 -0
- package/src/cli/gallery-fixtures/fs.ts +25 -0
- package/src/cli/session-picker.ts +34 -17
- package/src/cli/ttsr-cli.ts +19 -1
- package/src/cli.ts +9 -0
- package/src/collab/guest.ts +37 -4
- package/src/collab/host.ts +1 -0
- package/src/commands/launch.ts +6 -0
- package/src/config/model-discovery.ts +32 -5
- package/src/config/model-registry.ts +107 -25
- package/src/config/settings-schema.ts +77 -6
- package/src/config/settings.ts +25 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +463 -12
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/hashline/diff.ts +49 -10
- package/src/edit/hashline/execute.ts +38 -6
- package/src/edit/hashline/filesystem.ts +27 -3
- package/src/edit/index.ts +12 -2
- package/src/edit/renderer.ts +4 -4
- package/src/edit/streaming.ts +17 -2
- package/src/eval/py/prelude.py +7 -3
- package/src/export/html/template.js +1 -1
- package/src/extensibility/extensions/runner.ts +70 -0
- package/src/extensibility/extensions/types.ts +30 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +65 -13
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/local-protocol.ts +3 -2
- package/src/internal-urls/mcp-protocol.ts +4 -1
- package/src/internal-urls/router.ts +4 -1
- package/src/internal-urls/security-protocol.ts +261 -0
- package/src/internal-urls/types.ts +1 -1
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/lsp/client.ts +18 -3
- package/src/lsp/index.ts +7 -0
- package/src/lsp/render.ts +1 -1
- package/src/main.ts +127 -56
- package/src/mcp/manager.ts +120 -18
- package/src/modes/acp/acp-agent.ts +15 -9
- package/src/modes/components/chat-transcript-builder.ts +30 -11
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/read-tool-group.ts +159 -17
- package/src/modes/components/session-selector.ts +19 -3
- package/src/modes/components/status-line/component.ts +381 -54
- package/src/modes/components/tool-execution.ts +78 -31
- package/src/modes/components/usage-row.ts +10 -5
- package/src/modes/controllers/event-controller.ts +26 -4
- package/src/modes/controllers/mcp-command-controller.ts +68 -3
- package/src/modes/controllers/selector-controller.ts +94 -36
- package/src/modes/controllers/tan-command-controller.ts +14 -0
- package/src/modes/interactive-mode.ts +31 -17
- package/src/modes/rpc/host-uris.ts +6 -0
- package/src/modes/rpc/rpc-client.ts +18 -0
- package/src/modes/rpc/rpc-mode.ts +16 -1
- package/src/modes/rpc/rpc-types.ts +11 -0
- package/src/modes/theme/theme.ts +151 -6
- package/src/modes/types.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +38 -21
- package/src/prompts/agents/security-reviewer.md +75 -0
- package/src/prompts/security/scan-coordinator.md +7 -0
- package/src/prompts/security/scan-request.md +21 -0
- package/src/prompts/security/validate-request.md +8 -0
- package/src/prompts/system/auto-thinking-difficulty.md +4 -2
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +6 -0
- package/src/prompts/system/xdev-mount-notice.md +1 -1
- package/src/prompts/tools/bash.md +15 -17
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/glob.md +7 -6
- package/src/prompts/tools/grep.md +7 -6
- package/src/prompts/tools/security-publish.md +1 -0
- package/src/prompts/tools/security-scan.md +1 -0
- package/src/sdk.ts +212 -21
- package/src/security/auth.ts +98 -0
- package/src/security/cloud.ts +686 -0
- package/src/security/comparison.ts +255 -0
- package/src/security/contracts/ids.ts +111 -0
- package/src/security/contracts/index.ts +4 -0
- package/src/security/contracts/schemas.ts +201 -0
- package/src/security/contracts/types.ts +254 -0
- package/src/security/contracts/validation.ts +65 -0
- package/src/security/coordinator.ts +708 -0
- package/src/security/importers/codex-security.ts +387 -0
- package/src/security/importers/index.ts +2 -0
- package/src/security/importers/sarif.ts +357 -0
- package/src/security/index.ts +13 -0
- package/src/security/preflight.ts +405 -0
- package/src/security/provenance.ts +106 -0
- package/src/security/publication.ts +326 -0
- package/src/security/remediation.ts +93 -0
- package/src/security/resource-output.ts +50 -0
- package/src/security/sarif.ts +78 -0
- package/src/security/store.ts +430 -0
- package/src/session/agent-session-events.ts +1 -0
- package/src/session/agent-session-types.ts +40 -1
- package/src/session/agent-session.ts +302 -175
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/model-controls.ts +11 -3
- package/src/session/session-advisors.ts +140 -16
- package/src/session/session-entries.ts +21 -1
- package/src/session/session-handoff.ts +20 -3
- package/src/session/session-loader.ts +25 -9
- package/src/session/session-maintenance.ts +59 -11
- package/src/session/session-manager.ts +61 -0
- package/src/session/session-tools.ts +107 -5
- package/src/session/settings-stream-fn.ts +1 -0
- package/src/session/turn-recovery.ts +97 -51
- package/src/slash-commands/builtin-registry.ts +152 -3
- package/src/slash-commands/helpers/security.ts +451 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/agents.ts +2 -0
- package/src/task/executor.ts +20 -4
- package/src/task/render.ts +20 -4
- package/src/task/structured-subagent.ts +6 -4
- package/src/thinking.ts +29 -10
- package/src/tools/acp-bridge.ts +52 -8
- package/src/tools/browser.ts +5 -0
- package/src/tools/builtin-names.ts +1 -0
- package/src/tools/checkpoint.ts +0 -13
- package/src/tools/grep.ts +64 -8
- package/src/tools/hub/launch.ts +10 -15
- package/src/tools/index.ts +37 -5
- package/src/tools/path-utils.ts +91 -0
- package/src/tools/read.ts +3 -0
- package/src/tools/security-scan.ts +287 -0
- package/src/tools/write.ts +9 -3
- package/src/tools/xdev.ts +39 -14
- package/src/utils/changelog.ts +144 -4
- package/src/utils/git.ts +61 -14
- package/src/utils/jj.ts +19 -13
- package/src/web/search/providers/codex.ts +34 -0
- package/src/web/search/types.ts +1 -1
package/src/utils/git.ts
CHANGED
|
@@ -197,12 +197,14 @@ const GIT_NON_INTERACTIVE_ENV = {
|
|
|
197
197
|
GIT_ASKPASS: "true",
|
|
198
198
|
GIT_EDITOR: "true",
|
|
199
199
|
GIT_TERMINAL_PROMPT: "0",
|
|
200
|
+
LC_ALL: undefined,
|
|
201
|
+
LC_MESSAGES: "C",
|
|
200
202
|
SSH_ASKPASS: "/usr/bin/false",
|
|
201
|
-
} satisfies Record<string, string>;
|
|
203
|
+
} satisfies Record<string, string | undefined>;
|
|
202
204
|
const GH_NON_INTERACTIVE_ENV = {
|
|
203
205
|
...GIT_NON_INTERACTIVE_ENV,
|
|
204
206
|
GH_PROMPT_DISABLED: "1",
|
|
205
|
-
} satisfies Record<string, string>;
|
|
207
|
+
} satisfies Record<string, string | undefined>;
|
|
206
208
|
|
|
207
209
|
/** Default deadline for git and gh subprocesses spawned by the coding agent. */
|
|
208
210
|
export const GIT_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -215,6 +217,14 @@ export const GIT_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
|
|
|
215
217
|
export const GIT_NETWORK_TIMEOUT_MS = 30 * 60 * 1000;
|
|
216
218
|
/** Maximum captured stdout or stderr bytes retained from git and gh subprocesses. */
|
|
217
219
|
export const GIT_COMMAND_OUTPUT_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
220
|
+
/**
|
|
221
|
+
* Deadline for synchronous git plumbing commands launched via
|
|
222
|
+
* {@link gitSpawnSyncText}. These run on the render path (e.g. reftable HEAD
|
|
223
|
+
* resolution), so the deadline is short: a command that has not exited by then
|
|
224
|
+
* is killed and reported as {@link GIT_COMMAND_TIMEOUT_EXIT_CODE} so the caller
|
|
225
|
+
* degrades instead of freezing the UI indefinitely.
|
|
226
|
+
*/
|
|
227
|
+
export const GIT_SPAWN_SYNC_TIMEOUT_MS = 5_000;
|
|
218
228
|
|
|
219
229
|
const GIT_COMMAND_TIMEOUT_EXIT_CODE = 124;
|
|
220
230
|
// Exit code returned when the `git` binary cannot be launched at all (spawn
|
|
@@ -378,16 +388,35 @@ function normalizeStdin(input: CommandOptions["stdin"]): "ignore" | Uint8Array {
|
|
|
378
388
|
return new Uint8Array(input);
|
|
379
389
|
}
|
|
380
390
|
|
|
381
|
-
function
|
|
391
|
+
function buildNonInteractiveEnv(
|
|
392
|
+
env: Record<string, string | undefined>,
|
|
393
|
+
pinnedEnv: Record<string, string | undefined>,
|
|
394
|
+
): Record<string, string | undefined> {
|
|
395
|
+
const preservedCharacterLocale =
|
|
396
|
+
env.LC_ALL !== undefined && /(?:^|[._-])utf-?8(?:$|[.@_-])/i.test(env.LC_ALL) ? env.LC_ALL : undefined;
|
|
382
397
|
return {
|
|
383
|
-
...
|
|
384
|
-
|
|
385
|
-
...
|
|
386
|
-
...overrides,
|
|
387
|
-
...GIT_NON_INTERACTIVE_ENV,
|
|
398
|
+
...env,
|
|
399
|
+
...(preservedCharacterLocale === undefined ? {} : { LC_CTYPE: preservedCharacterLocale }),
|
|
400
|
+
...pinnedEnv,
|
|
388
401
|
};
|
|
389
402
|
}
|
|
390
403
|
|
|
404
|
+
function buildGitEnv(overrides?: Record<string, string | undefined>): Record<string, string | undefined> {
|
|
405
|
+
return buildNonInteractiveEnv(
|
|
406
|
+
{
|
|
407
|
+
...process.env,
|
|
408
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
409
|
+
...AMBIENT_GIT_ENV,
|
|
410
|
+
...overrides,
|
|
411
|
+
},
|
|
412
|
+
GIT_NON_INTERACTIVE_ENV,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function buildGhEnv(): Record<string, string | undefined> {
|
|
417
|
+
return buildNonInteractiveEnv({ ...process.env }, GH_NON_INTERACTIVE_ENV);
|
|
418
|
+
}
|
|
419
|
+
|
|
391
420
|
function ensureAvailable(): void {
|
|
392
421
|
if (!$which("git")) {
|
|
393
422
|
throw new Error("git is not installed.");
|
|
@@ -399,8 +428,17 @@ function ensureAvailable(): void {
|
|
|
399
428
|
* exit code plus trimmed stdout; a missing `git` binary (spawn ENOENT) is
|
|
400
429
|
* reported as {@link GIT_SPAWN_ENOENT_EXIT_CODE} so sync read-only callers
|
|
401
430
|
* degrade to `null` instead of throwing an uncaught error during rendering.
|
|
431
|
+
*
|
|
432
|
+
* A deadline ({@link GIT_SPAWN_SYNC_TIMEOUT_MS}) is enforced so a pathological
|
|
433
|
+
* git invocation (lock contention, NFS stall, …) cannot hang the render path
|
|
434
|
+
* indefinitely: a child killed by the deadline is reported as
|
|
435
|
+
* {@link GIT_COMMAND_TIMEOUT_EXIT_CODE} rather than a successful exit.
|
|
402
436
|
*/
|
|
403
|
-
function gitSpawnSyncText(
|
|
437
|
+
function gitSpawnSyncText(
|
|
438
|
+
cwd: string,
|
|
439
|
+
args: readonly string[],
|
|
440
|
+
timeoutMs: number = GIT_SPAWN_SYNC_TIMEOUT_MS,
|
|
441
|
+
): { exitCode: number; stdout: string } {
|
|
404
442
|
const commandArgs = withShortLivedGitConfig(withNoOptionalLocks(args));
|
|
405
443
|
try {
|
|
406
444
|
const result = Bun.spawnSync(["git", ...commandArgs], {
|
|
@@ -409,8 +447,14 @@ function gitSpawnSyncText(cwd: string, args: readonly string[]): { exitCode: num
|
|
|
409
447
|
stdout: "pipe",
|
|
410
448
|
stderr: "pipe",
|
|
411
449
|
windowsHide: true,
|
|
450
|
+
timeout: timeoutMs,
|
|
412
451
|
});
|
|
413
|
-
|
|
452
|
+
// Bun's timeout marker is authoritative even when process cleanup reports
|
|
453
|
+
// exit code zero, so render-path callers never trust partial output.
|
|
454
|
+
const exitCode = result.exitedDueToTimeout
|
|
455
|
+
? GIT_COMMAND_TIMEOUT_EXIT_CODE
|
|
456
|
+
: (result.exitCode ?? GIT_COMMAND_TIMEOUT_EXIT_CODE);
|
|
457
|
+
return { exitCode, stdout: new TextDecoder().decode(result.stdout).trim() };
|
|
414
458
|
} catch (err) {
|
|
415
459
|
if (isEnoent(err)) return { exitCode: GIT_SPAWN_ENOENT_EXIT_CODE, stdout: "" };
|
|
416
460
|
throw err;
|
|
@@ -1683,6 +1727,12 @@ export const revList = {
|
|
|
1683
1727
|
async range(cwd: string, base: string, head: string, signal?: AbortSignal): Promise<string[]> {
|
|
1684
1728
|
return splitLines(await runText(cwd, ["rev-list", "--reverse", `${base}..${head}`], { readOnly: true, signal }));
|
|
1685
1729
|
},
|
|
1730
|
+
/** Commits reachable from `ref` that touched `file`, newest first, capped at `limit`. */
|
|
1731
|
+
async touching(cwd: string, ref: string, file: string, limit: number, signal?: AbortSignal): Promise<string[]> {
|
|
1732
|
+
return splitLines(
|
|
1733
|
+
await runText(cwd, ["rev-list", `--max-count=${limit}`, ref, "--", file], { readOnly: true, signal }),
|
|
1734
|
+
);
|
|
1735
|
+
},
|
|
1686
1736
|
};
|
|
1687
1737
|
|
|
1688
1738
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -2374,10 +2424,7 @@ export const github = {
|
|
|
2374
2424
|
try {
|
|
2375
2425
|
const child = Bun.spawn(["gh", ...args], {
|
|
2376
2426
|
cwd,
|
|
2377
|
-
env:
|
|
2378
|
-
...process.env,
|
|
2379
|
-
...GH_NON_INTERACTIVE_ENV,
|
|
2380
|
-
},
|
|
2427
|
+
env: buildGhEnv(),
|
|
2381
2428
|
stdin: "ignore",
|
|
2382
2429
|
stdout: "pipe",
|
|
2383
2430
|
stderr: "pipe",
|
package/src/utils/jj.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { $which } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { LRUCache } from "lru-cache/raw";
|
|
5
|
+
import { withTimeoutSignal } from "./fetch-timeout";
|
|
5
6
|
import * as git from "./git";
|
|
6
7
|
|
|
7
8
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -27,19 +28,24 @@ export interface JjRepository {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/** Options for `jj diff` invocations. */
|
|
30
|
-
export interface DiffOptions {
|
|
31
|
+
export interface DiffOptions extends JjCommandOptions {
|
|
31
32
|
/** Optional file paths to restrict the diff with `-- <files>`. */
|
|
32
33
|
readonly files?: readonly string[];
|
|
33
34
|
/** Return only changed file names instead of Git-format diff text. */
|
|
34
35
|
readonly nameOnly?: boolean;
|
|
35
|
-
/** Optional abort signal passed to the spawned `jj` process. */
|
|
36
|
-
readonly signal?: AbortSignal;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
/** Options for a bounded `jj` subprocess query. */
|
|
39
|
+
export interface JjCommandOptions {
|
|
40
|
+
/** Optional cancellation signal for the subprocess. */
|
|
40
41
|
readonly signal?: AbortSignal;
|
|
42
|
+
/** Deadline in milliseconds. Defaults to {@link JJ_COMMAND_TIMEOUT_MS}. */
|
|
43
|
+
readonly timeoutMs?: number;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
/** Default finite deadline for local jj subprocesses. */
|
|
47
|
+
export const JJ_COMMAND_TIMEOUT_MS = 5_000;
|
|
48
|
+
|
|
43
49
|
// ════════════════════════════════════════════════════════════════════════════
|
|
44
50
|
// Error
|
|
45
51
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -83,10 +89,10 @@ function formatCommandFailure(
|
|
|
83
89
|
return `jj ${args.join(" ")} failed with exit code ${result.exitCode}`;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
async function jj(cwd: string, args: readonly string[], options:
|
|
92
|
+
async function jj(cwd: string, args: readonly string[], options: JjCommandOptions = {}): Promise<JjCommandResult> {
|
|
87
93
|
const child = Bun.spawn(["jj", "--no-pager", "--color=never", ...args], {
|
|
88
94
|
cwd,
|
|
89
|
-
signal: options.signal,
|
|
95
|
+
signal: withTimeoutSignal(options.timeoutMs ?? JJ_COMMAND_TIMEOUT_MS, options.signal),
|
|
90
96
|
stdin: "ignore",
|
|
91
97
|
stdout: "pipe",
|
|
92
98
|
stderr: "pipe",
|
|
@@ -109,7 +115,7 @@ async function jj(cwd: string, args: readonly string[], options: CommandOptions
|
|
|
109
115
|
async function runChecked(
|
|
110
116
|
cwd: string,
|
|
111
117
|
args: readonly string[],
|
|
112
|
-
options:
|
|
118
|
+
options: JjCommandOptions = {},
|
|
113
119
|
): Promise<JjCommandResult> {
|
|
114
120
|
ensureAvailable();
|
|
115
121
|
const result = await jj(cwd, args, options);
|
|
@@ -119,14 +125,14 @@ async function runChecked(
|
|
|
119
125
|
return result;
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
async function runText(cwd: string, args: readonly string[], options:
|
|
128
|
+
async function runText(cwd: string, args: readonly string[], options: JjCommandOptions = {}): Promise<string> {
|
|
123
129
|
return (await runChecked(cwd, args, options)).stdout;
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
async function runOptionalText(
|
|
127
133
|
cwd: string,
|
|
128
134
|
args: readonly string[],
|
|
129
|
-
options:
|
|
135
|
+
options: JjCommandOptions = {},
|
|
130
136
|
): Promise<string | null> {
|
|
131
137
|
try {
|
|
132
138
|
const result = await jj(cwd, args, options);
|
|
@@ -294,7 +300,7 @@ export const workingCopy = {
|
|
|
294
300
|
* Label `@` with its nearest bookmark, falling back to its short change ID.
|
|
295
301
|
* Returns `null` when `jj` is unavailable or the query fails.
|
|
296
302
|
*/
|
|
297
|
-
async label(cwd: string,
|
|
303
|
+
async label(cwd: string, options?: JjCommandOptions): Promise<string | null> {
|
|
298
304
|
const raw = await runOptionalText(
|
|
299
305
|
cwd,
|
|
300
306
|
[
|
|
@@ -306,7 +312,7 @@ export const workingCopy = {
|
|
|
306
312
|
"-T",
|
|
307
313
|
WORKING_COPY_LABEL_TEMPLATE,
|
|
308
314
|
],
|
|
309
|
-
|
|
315
|
+
options,
|
|
310
316
|
);
|
|
311
317
|
return raw === null ? null : parseWorkingCopyLabel(raw);
|
|
312
318
|
},
|
|
@@ -325,8 +331,8 @@ export const status = {
|
|
|
325
331
|
* Count changes in `@` relative to its parent using the Git status shape.
|
|
326
332
|
* Jujutsu has no index, so `staged` is always zero.
|
|
327
333
|
*/
|
|
328
|
-
async summary(cwd: string,
|
|
329
|
-
const raw = await runOptionalText(cwd, ["diff", "-r", "@", "--summary", "--ignore-working-copy"],
|
|
334
|
+
async summary(cwd: string, options?: JjCommandOptions): Promise<git.GitStatusSummary | null> {
|
|
335
|
+
const raw = await runOptionalText(cwd, ["diff", "-r", "@", "--summary", "--ignore-working-copy"], options);
|
|
330
336
|
return raw === null ? null : parseStatusSummary(raw);
|
|
331
337
|
},
|
|
332
338
|
|
|
@@ -114,7 +114,28 @@ function getDefaultModelCandidates(): CodexModelCandidate[] {
|
|
|
114
114
|
return fallbackModel ? [{ modelId: fallbackModel.id, catalogModel: fallbackModel }] : [{ modelId: FALLBACK_MODEL }];
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Raised when Codex produced an answer without invoking the hosted `web_search`
|
|
119
|
+
* tool. GPT-5.6 Responses-Lite models receive `tool_choice: "auto"` (the forced
|
|
120
|
+
* hosted choice is invalid under the lite shape — see #5771 / #5772), so the
|
|
121
|
+
* model may skip searching and return a plain completion. A search command must
|
|
122
|
+
* not present that as a successful, search-backed result (#6988); this advances
|
|
123
|
+
* the candidate chain to a model that will search, or surfaces a clear failure
|
|
124
|
+
* when the model was explicitly configured.
|
|
125
|
+
*/
|
|
126
|
+
class CodexNoWebSearchError extends SearchProviderError {
|
|
127
|
+
constructor() {
|
|
128
|
+
super(
|
|
129
|
+
"codex",
|
|
130
|
+
"Codex returned a completion without running web search (no web_search_call event); refusing to treat a non-search answer as a search result",
|
|
131
|
+
502,
|
|
132
|
+
);
|
|
133
|
+
this.name = "CodexNoWebSearchError";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
function shouldRetryWithNextDefaultModel(error: unknown): boolean {
|
|
138
|
+
if (error instanceof CodexNoWebSearchError) return true;
|
|
118
139
|
if (!(error instanceof SearchProviderError)) return false;
|
|
119
140
|
if (error.provider !== "codex" || error.status !== 400) return false;
|
|
120
141
|
return /model is not supported|requested model is not supported|not supported when using codex with a chatgpt account/i.test(
|
|
@@ -472,11 +493,19 @@ async function callCodexSearch(
|
|
|
472
493
|
let model = requestedModel;
|
|
473
494
|
let requestId = "";
|
|
474
495
|
let usage: { inputTokens: number; outputTokens: number; totalTokens: number } | undefined;
|
|
496
|
+
// Evidence that the hosted web_search tool actually ran. Lite models get
|
|
497
|
+
// `tool_choice: "auto"` and may answer without searching (#6988); a search
|
|
498
|
+
// command must reject that rather than return a non-search completion.
|
|
499
|
+
let webSearchInvoked = false;
|
|
475
500
|
|
|
476
501
|
for await (const rawEvent of readSseJson<Record<string, unknown>>(response.body, options.signal)) {
|
|
477
502
|
const eventType = typeof rawEvent.type === "string" ? rawEvent.type : "";
|
|
478
503
|
if (!eventType) continue;
|
|
479
504
|
|
|
505
|
+
if (eventType.startsWith("response.web_search_call")) {
|
|
506
|
+
webSearchInvoked = true;
|
|
507
|
+
}
|
|
508
|
+
|
|
480
509
|
if (eventType === "response.output_text.delta") {
|
|
481
510
|
const delta = typeof rawEvent.delta === "string" ? rawEvent.delta : "";
|
|
482
511
|
if (delta) {
|
|
@@ -485,6 +514,7 @@ async function callCodexSearch(
|
|
|
485
514
|
} else if (eventType === "response.output_item.done") {
|
|
486
515
|
const item = rawEvent.item as CodexResponseItem | undefined;
|
|
487
516
|
if (!item) continue;
|
|
517
|
+
if (item.type === "web_search_call") webSearchInvoked = true;
|
|
488
518
|
|
|
489
519
|
// Handle text message content and extract sources from annotations
|
|
490
520
|
if (item.type === "message" && item.content) {
|
|
@@ -538,6 +568,10 @@ async function callCodexSearch(
|
|
|
538
568
|
}
|
|
539
569
|
}
|
|
540
570
|
|
|
571
|
+
if (!webSearchInvoked) {
|
|
572
|
+
throw new CodexNoWebSearchError();
|
|
573
|
+
}
|
|
574
|
+
|
|
541
575
|
const finalAnswer = answerParts.join("\n\n").trim();
|
|
542
576
|
const streamedAnswer = streamedAnswerParts.join("").trim();
|
|
543
577
|
// Throw to advance the chain whenever Codex emitted nothing but image
|
package/src/web/search/types.ts
CHANGED
|
@@ -32,7 +32,7 @@ export const SEARCH_PROVIDER_OPTIONS = [
|
|
|
32
32
|
},
|
|
33
33
|
{ value: "xai", label: "xAI", description: "Grok web search via xAI Responses API (requires XAI_API_KEY)" },
|
|
34
34
|
{ value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
|
|
35
|
-
{ value: "exa", label: "Exa", description: "
|
|
35
|
+
{ value: "exa", label: "Exa", description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP" },
|
|
36
36
|
{ value: "tinyfish", label: "TinyFish", description: "Requires TINYFISH_API_KEY" },
|
|
37
37
|
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
|
|
38
38
|
{ value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
|