tau-coding-agent 0.1.6 → 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.
Files changed (80) hide show
  1. package/README.md +15 -12
  2. package/extensions/answer.ts +129 -79
  3. package/extensions/branch-term/README.md +7 -0
  4. package/extensions/{branch-term.ts → branch-term/index.ts} +113 -104
  5. package/extensions/btw.ts +17 -2
  6. package/extensions/caffeinate/README.md +5 -0
  7. package/extensions/caffeinate/index.ts +144 -0
  8. package/extensions/fast.ts +292 -0
  9. package/extensions/ghostty.ts +214 -211
  10. package/extensions/git-diff-stats.ts +124 -84
  11. package/extensions/git-pr-status.ts +274 -208
  12. package/extensions/insights.ts +109 -191
  13. package/extensions/loop.ts +149 -145
  14. package/extensions/memory.ts +138 -86
  15. package/extensions/notify.ts +14 -26
  16. package/extensions/openai-verbosity.ts +108 -42
  17. package/extensions/review/fix.ts +15 -6
  18. package/extensions/review/git.ts +93 -103
  19. package/extensions/review/index.ts +60 -62
  20. package/extensions/review/interrupt.ts +117 -0
  21. package/extensions/review/message-queue.ts +45 -12
  22. package/extensions/review/models.ts +79 -3
  23. package/extensions/review/prompts.ts +41 -38
  24. package/extensions/review/review.ts +161 -166
  25. package/extensions/review/runner.ts +139 -147
  26. package/extensions/review/runtime.ts +173 -120
  27. package/extensions/review/triage.ts +41 -44
  28. package/extensions/sandbox/bash.ts +775 -0
  29. package/extensions/sandbox/command.ts +605 -0
  30. package/extensions/sandbox/config.ts +764 -0
  31. package/extensions/sandbox/index.ts +132 -3338
  32. package/extensions/sandbox/permissions/dialog.ts +118 -0
  33. package/extensions/sandbox/permissions/filesystem.ts +559 -0
  34. package/extensions/sandbox/permissions/mach-lookup.ts +186 -0
  35. package/extensions/sandbox/permissions/network.ts +164 -0
  36. package/extensions/sandbox/permissions/unsandboxed.ts +270 -0
  37. package/extensions/sandbox/runtime.ts +616 -0
  38. package/extensions/stash.ts +28 -15
  39. package/extensions/subagent/README.md +73 -0
  40. package/extensions/subagent/index.ts +822 -0
  41. package/extensions/subagent/interrupt.ts +117 -0
  42. package/extensions/subagent/permissions.ts +101 -0
  43. package/extensions/subagent/rpc.ts +177 -0
  44. package/extensions/tool-display-mode.ts +267 -64
  45. package/extensions/usage/index.ts +249 -356
  46. package/extensions/usage/openrouter.ts +10 -2
  47. package/extensions/websearch/README.md +12 -47
  48. package/extensions/websearch/config.ts +5 -2
  49. package/extensions/websearch/index.ts +94 -109
  50. package/extensions/websearch/output.ts +51 -0
  51. package/extensions/websearch/providers/anthropic.pi.ts +27 -44
  52. package/extensions/websearch/providers/gemini.browser.ts +68 -77
  53. package/extensions/websearch/providers/gemini.pi.ts +17 -17
  54. package/extensions/websearch/providers/openai-codex.pi.ts +145 -7
  55. package/extensions/websearch/providers/pi-model.shared.ts +33 -39
  56. package/extensions/websearch/providers/shared.ts +87 -2
  57. package/extensions/websearch/types.ts +0 -3
  58. package/extensions/worktree.ts +132 -172
  59. package/package.json +10 -5
  60. package/skills/browser-tools/SKILL.md +29 -234
  61. package/skills/browser-tools/references/cookies.md +36 -0
  62. package/skills/browser-tools/references/interaction.md +90 -0
  63. package/skills/browser-tools/references/logging.md +34 -0
  64. package/skills/git-clean-history/SKILL.md +6 -6
  65. package/skills/git-commit/SKILL.md +5 -3
  66. package/skills/github-pull-request/SKILL.md +60 -0
  67. package/skills/github-pull-request/references/create.md +40 -0
  68. package/skills/github-pull-request/references/stewardship.md +60 -0
  69. package/skills/oracle/SKILL.md +3 -3
  70. package/skills/oracle/scripts/oracle +10 -1
  71. package/skills/sentry/SKILL.md +15 -185
  72. package/skills/sentry/references/events.md +79 -0
  73. package/skills/sentry/references/issues.md +62 -0
  74. package/skills/sentry/references/logs.md +46 -0
  75. package/skills/update-changelog/SKILL.md +27 -121
  76. package/skills/web-design/SKILL.md +16 -105
  77. package/themes/tau-dark.json +4 -0
  78. package/extensions/openai-fast.ts +0 -229
  79. package/extensions/websearch/providers/openai-codex.browser.ts +0 -77
  80. package/extensions/websearch/providers/openai-codex.shared.ts +0 -123
@@ -1,135 +1,41 @@
1
1
  ---
2
2
  name: update-changelog
3
- description: "Update CHANGELOG.md following Keep a Changelog (https://keepachangelog.com/en/1.1.0/)"
3
+ description: "Update CHANGELOG.md with notable user-facing changes using Keep a Changelog conventions."
4
4
  ---
5
5
 
6
- # Update Changelog (Keep a Changelog)
6
+ # Update Changelog
7
7
 
8
- Update the repository changelog with user-facing changes that landed since the last release.
8
+ Use [Keep a Changelog 2.0.0](https://keepachangelog.com/en/2.0.0/) and preserve the repository's established style. Prefer `CHANGELOG.md`, or `CHANGELOG` when that is the existing file.
9
9
 
10
- This skill is **explicitly based on** Keep a Changelog v1.1.0:
11
- https://keepachangelog.com/en/1.1.0/
10
+ ## Choose the scope
12
11
 
13
- ## Rules (non-negotiable)
12
+ - **Document a specific change:** inspect the relevant diff, issue, or PR and the existing `Unreleased` entries. Update only that change; a complete release-history audit is unnecessary.
13
+ - **Audit changes since a release:** identify the requested baseline, or the latest release tag (`git describe --tags --abbrev=0`). If tags are missing or inconsistent, use the newest release section as evidence for the baseline. Inspect `git log <baseline>..HEAD --oneline` and relevant diffs or PRs to find notable omissions.
14
+ - **Prepare a release:** move `Unreleased` entries into a versioned section only when the release operation is requested.
14
15
 
15
- - **Do not add installation instructions** to the changelog.
16
- - Only include **notable, user-visible** changes.
17
- - **Never add raw commit SHAs**. Prefer PR numbers (e.g. `#123`) and/or issue IDs.
18
- - Add entries **only under `Unreleased`** (unless you are also cutting a release and moving items into a versioned section).
19
- - Preserve the project’s existing formatting where possible, but align new content to Keep a Changelog.
16
+ A changelog request does not authorize committing, publishing, or creating a release.
20
17
 
21
- ## File to edit
18
+ ## Entry rules
22
19
 
23
- - Prefer `CHANGELOG.md`.
24
- - If missing, use `CHANGELOG`.
20
+ - Include notable user-visible behavior, APIs, flags, bug fixes, and security changes. Exclude internal cleanup, tests, typo-only documentation edits, dependency bumps, and other changes without visible user impact.
21
+ - Write concise, concrete entries matching the repository's bullet grammar. Explain user impact rather than copying commit subjects or listing implementation details.
22
+ - Link issues or pull requests when useful, but prefer plain prose over lists of references.
23
+ - Mark breaking changes with `**Breaking:**` within their change category, and say what breaks.
24
+ - Keep upgrade notes brief. Link substantial procedures to a migration guide or release notes.
25
+ - Add entries under `Unreleased` unless an explicitly requested release requires moving them. Preserve released content during ordinary updates.
26
+ - Merge with an existing entry when it describes the same change rather than adding a duplicate.
25
27
 
26
- ## Step-by-step
28
+ ## Categories
27
29
 
28
- ### 1) Identify the baseline (last released version)
30
+ Create only needed headings. Prefer the six standard types:
29
31
 
30
- Pick a baseline tag/version to compare against.
32
+ | Heading | Changes |
33
+ | ---------- | ------------------------------------------------- |
34
+ | Added | New features or capabilities |
35
+ | Changed | Intentional changes to existing behavior |
36
+ | Deprecated | Features scheduled for removal |
37
+ | Removed | Removed features |
38
+ | Fixed | Corrections to faulty behavior |
39
+ | Security | Vulnerabilities (lead with CVE when one is known) |
31
40
 
32
- - If the project uses git tags:
33
-
34
- ```bash
35
- git describe --tags --abbrev=0
36
- ```
37
-
38
- - If tags are missing/inconsistent, use the newest release section in the changelog as the baseline.
39
-
40
- ### 2) Collect candidate changes
41
-
42
- Gather commits/PRs since the baseline and identify user-facing changes.
43
-
44
- ```bash
45
- git log <baseline>..HEAD --oneline
46
- ```
47
-
48
- If you have PR metadata available (e.g., via GitHub), use it to improve wording and include PR numbers.
49
-
50
- ### 3) Ensure the changelog structure matches Keep a Changelog
51
-
52
- At minimum, Keep a Changelog expects:
53
-
54
- - A top `Unreleased` section
55
- - Optional subsections under `Unreleased` (and under each release):
56
- - `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`
57
-
58
- If `Unreleased` exists but is missing subsections, create only the ones you need for the new entries.
59
-
60
- ### 4) Add entries under `Unreleased`
61
-
62
- Classify each change into one of the standard headings:
63
-
64
- - **Added**: new features
65
- - **Changed**: changes in existing functionality (including behavior changes)
66
- - **Deprecated**: soon-to-be removed features
67
- - **Removed**: removed features
68
- - **Fixed**: bug fixes
69
- - **Security**: vulnerability fixes
70
-
71
- Write entries as **consistent bullet points**:
72
-
73
- - Start with the category verb (Added/Changed/Deprecated/Removed/Fixed/Security) _only via the section heading_, not inside each bullet.
74
- - Each bullet should be a single, past-tense sentence fragment.
75
- - Prefer: `- Added … (#123)` / `- Fixed … (#456)` style (consistent grammar).
76
-
77
- ### 5) Keep it user-facing
78
-
79
- Include:
80
-
81
- - visible behavior changes
82
- - new CLI flags/API additions
83
- - bug fixes with clear impact
84
- - security fixes (without leaking sensitive details)
85
-
86
- Exclude (unless they change user-visible behavior):
87
-
88
- - pure refactors
89
- - internal cleanup
90
- - dependency bumps with no user impact
91
- - typo-only doc edits
92
-
93
- ### 6) Links (only if the file already uses them)
94
-
95
- Keep a Changelog commonly includes link references at the bottom, e.g.:
96
-
97
- - `[Unreleased]: <compare link>`
98
- - `[1.2.3]: <compare link>`
99
-
100
- If the project already uses these, update them accordingly (don’t introduce link refs if the changelog doesn’t use them).
101
-
102
- ## Example (consistent Keep a Changelog format)
103
-
104
- ```markdown
105
- ## [Unreleased]
106
-
107
- ### Added
108
-
109
- - Added widget-level caching for faster dashboard loads. (#123)
110
-
111
- ### Changed
112
-
113
- - Changed default retry policy to exponential backoff. (#140)
114
-
115
- ### Fixed
116
-
117
- - Fixed crash when importing a config with empty sections. (#155)
118
-
119
- ## [1.4.0] - 2026-02-01
120
-
121
- ### Added
122
-
123
- - Added support for exporting reports as CSV. (#110)
124
-
125
- ### Fixed
126
-
127
- - Fixed incorrect timezone handling in scheduled jobs. (#117)
128
- ```
129
-
130
- ## Quality checklist
131
-
132
- - Entries are under **`Unreleased`** and categorized correctly.
133
- - Wording is consistent (same tense/style across bullets).
134
- - No installation instructions, no commit SHAs.
135
- - The changelog remains easy to scan and matches the repo’s established conventions.
41
+ Maintain comparison/release links when affected. Preserve the existing link style.
@@ -3,115 +3,26 @@ name: web-design
3
3
  description: "Design and implement distinctive, production-ready web interfaces with strong aesthetic direction. Use when asked to create or restyle web pages, components, or applications (HTML/CSS/JS, React, Vue, etc.)."
4
4
  ---
5
5
 
6
- # Web Design Skill
6
+ # Web Design
7
7
 
8
- Design and implement memorable web interfaces with a clear, intentional aesthetic. The output must be real, working code — not just mood boards. This skill is about **design thinking + execution**: every visual choice should be rooted in purpose and context.
8
+ When implementing, deliver real, working interfaces with purposeful visual choices, not just mockups or mood boards. Distinctive aesthetics are the default for open-ended design. Scoped changes should fit the existing product rather than impose a redesign.
9
9
 
10
- ## When to Use
10
+ ## Workflow
11
11
 
12
- Use this skill when the user wants to:
12
+ 1. **Understand the brief and scope.** Identify purpose, audience, required content and behavior, technical constraints, and any brand or accessibility requirements from the request and existing code. Clarify missing requirements when they materially affect the result. Resolve routine choices from context and state important assumptions.
13
+ 2. **Choose a coherent direction.** When the visual direction is open, explore contrasting concepts grounded in the brief, the user's taste, and concrete visual references. Vary composition and interaction, not just color. Use available feedback to refine the direction, then commit to a coherent, memorable aesthetic. For a scoped edit, preserve the existing design system, tokens, fonts, components, and interaction conventions unless the user requests a change.
13
14
 
14
- - Create a new web page, landing page, dashboard, or app UI
15
- - Design or redesign frontend components or screens
16
- - Improve typography, layout, color, motion, or overall visual polish
17
- - Convert a concept or brief into a high‑fidelity, coded interface
15
+ When exploring alternative designs for early user direction, generate a fresh random alphanumeric string with a shell command for each option. Use patterns in the seed to inspire typography, palette, composition, or interaction. Keep the seed out of the UI and product copy.
18
16
 
19
- ## Inputs to Gather (or Assume)
17
+ 3. **Implement within the project.** Reuse the established stack and components. Build the requested behavior, content, and relevant loading, empty, error, and success states. Make styling easy to maintain through the project's tokens or CSS variables. Choose photography, illustration, SVG, or CSS artwork to serve the content and direction. Reuse suitable project or user-provided assets, and use image generation when appropriate tools are available and authorized. Deliver usable assets, not broken placeholders.
18
+ 4. **Critique and refine.** Inspect the rendered result, not just the code. For substantial visual work, use an independent reviewer in a fresh context when available. Provide the brief, constraints, current screenshots, and visual references—not code, prior critiques, or implementation rationale. Ask for specific, prioritized improvements to hierarchy, composition, clarity, and detail. Remove redundant copy, containers, and decoration that weaken the task or aesthetic, while preserving essential information and usable controls.
19
+ 5. **Verify and finish.** Check the working result at relevant viewport sizes and interaction states. Verify keyboard use, focus, legibility, and reduced-motion behavior. Fix problems within scope. Keep the explanation proportional to the task, covering changed files, meaningful design decisions, verification, and remaining gaps. State when visual verification was unavailable. If delivering standalone code, include complete runnable files and necessary setup. For code-only requests, omit the design narration.
20
20
 
21
- Before coding, identify:
21
+ ## Design standards
22
22
 
23
- - **Purpose & audience**: What problem does this UI solve? Who uses it?
24
- - **Brand/voice**: Any reference brands, tone, or visual inspiration?
25
- - **Technical constraints**: Framework, library, CSS strategy, accessibility, performance
26
- - **Content constraints**: Required copy, assets, data, features
27
-
28
- If the user did not provide this, ask **2–4 targeted questions**, or state reasonable assumptions in a short preface.
29
-
30
- ## Design Thinking (Required)
31
-
32
- Commit to a **single, bold aesthetic direction**. Name it and execute it consistently. Examples:
33
-
34
- - Brutalist / raw / utilitarian
35
- - Editorial / magazine / typographic
36
- - Luxury / refined / minimal
37
- - Retro‑futuristic / cyber / neon
38
- - Art‑deco / geometric / ornamental
39
- - Handcrafted / organic / textured
40
-
41
- **Avoid generic AI aesthetics.** No “default” fonts, color schemes, or stock layouts.
42
-
43
- Before writing code, define the system:
44
-
45
- 1. **Visual direction** — one sentence that describes the vibe
46
- 2. **Differentiator** — what should be memorable about this UI?
47
- 3. **Typography system** — display + body fonts, scale, weight, casing
48
- 4. **Color system** — dominant, accent, neutral; define as CSS variables
49
- 5. **Layout strategy** — grid rhythm, spacing scale, hierarchy plan
50
- 6. **Motion strategy** — 1–2 meaningful interaction moments
51
-
52
- If the user wants code only, skip the explanation but still follow this internally.
53
-
54
- ## Implementation Principles
55
-
56
- - **Working code**: HTML/CSS/JS or framework code that runs as‑is
57
- - **Semantic & accessible**: headings, labels, focus states, keyboard nav
58
- - **Responsive**: fluid layouts, breakpoints, responsive typography
59
- - **Tokenized styling**: CSS variables for colors, spacing, radii, shadows
60
- - **Modern layout**: prefer CSS Grid/Flex, avoid brittle positioning hacks
61
-
62
- ## Aesthetic Guidelines
63
-
64
- ### Typography
65
-
66
- - Typography should define the voice of the design
67
- - Avoid default fonts (Inter, Roboto, Arial, system stacks)
68
- - Use a **distinct display font** + a **refined body font**
69
- - Implement a clear hierarchy (size, weight, spacing, casing)
70
-
71
- ### Color & Theme
72
-
73
- - Commit to a palette with a strong point‑of‑view
74
- - Avoid timid, overused gradients (e.g., purple‑to‑pink on white)
75
- - Use contrast intentionally and check legibility
76
-
77
- ### Composition & Layout
78
-
79
- - Encourage asymmetry, scale contrast, overlap, or grid breaks
80
- - Use negative space deliberately (or controlled density if maximalist)
81
- - Create visual rhythm and hierarchy through spacing and alignment
82
-
83
- ### Detail & Atmosphere
84
-
85
- - Add texture or depth when appropriate (noise, grain, subtle patterns)
86
- - Use shadows/glows only when they serve the concept
87
- - Consider unique borders, masks, or clip‑paths for distinct shapes
88
-
89
- ### Motion & Interaction
90
-
91
- - Use motion sparingly but meaningfully
92
- - Favor one standout interaction over many tiny ones
93
- - Honor `prefers-reduced-motion`
94
-
95
- ## Avoid
96
-
97
- - Cookie‑cutter hero + 3 card layouts
98
- - Generic gradients and default font choices
99
- - Unmotivated decorative elements
100
- - Overly flat, characterless component libraries
101
-
102
- ## Deliverables
103
-
104
- - Provide full code with file names or component boundaries
105
- - Make customization easy with CSS variables or config objects
106
- - If assets are needed, provide inline SVGs or generative CSS patterns
107
-
108
- ## Quality Checklist (Self‑validate)
109
-
110
- - Aesthetic direction is unmistakable
111
- - Typography feels intentional and expressive
112
- - Layout and spacing are consistent and purposeful
113
- - Color palette feels cohesive and legible
114
- - Interactions enhance the experience without clutter
115
- - Code runs as provided and is production‑ready
116
-
117
- **Remember:** a design is only as strong as its commitment. Choose a direction and execute it relentlessly.
23
+ - **Intentional typography.** Establish a clear hierarchy through size, weight, spacing, and casing. For open-ended work, choose expressive type rather than defaulting to generic font choices. Pair display and body faces when useful.
24
+ - **Cohesive color and composition.** Use a purposeful palette with readable contrast. Create rhythm with spacing, alignment, scale, and deliberate negative space or density. Asymmetry, grid breaks, texture, borders, and depth should serve the concept, not become decoration for its own sake.
25
+ - **Distinctiveness without novelty for its own sake.** Avoid interchangeable hero-and-card compositions or fashionable gradients as an automatic answer to an open brief. A restrained layout or familiar component can be right when it serves the content and product.
26
+ - **Semantic, accessible behavior.** Use meaningful headings and landmarks, labeled controls, visible focus, keyboard navigation, and sufficient contrast. Preserve usability across interaction states, not just in a static screenshot.
27
+ - **Responsive implementation.** Use fluid layouts, appropriate breakpoints and typography, and robust Grid/Flex composition instead of brittle positioning. Check overflow and content variation on small and large screens.
28
+ - **Purposeful motion.** Use restrained animation to support interactions or the visual direction. Avoid distracting loops and competing effects. Honor `prefers-reduced-motion`, and keep essential information available without animation.
@@ -32,6 +32,10 @@
32
32
  "thinkingText": "gray",
33
33
 
34
34
  "selectedBg": "selectedBg",
35
+ "scrollbarTrack": "darkGray",
36
+ "scrollbarThumb": "",
37
+ "searchMatchBg": "selectedBg",
38
+ "searchMatchText": "",
35
39
  "userMessageBg": "userMsgBg",
36
40
  "userMessageText": "",
37
41
  "customMessageBg": "customMsgBg",
@@ -1,229 +0,0 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import {
4
- getAgentDir,
5
- type ExtensionAPI,
6
- type ExtensionContext,
7
- } from "@earendil-works/pi-coding-agent";
8
-
9
- const STATUS_KEY = "openai-fast";
10
- const SUPPORTED_APIS = new Set([
11
- "openai-responses",
12
- "openai-codex-responses",
13
- "azure-openai-responses",
14
- ]);
15
-
16
- type FastMode = "fast";
17
- type FastSetting = FastMode | "auto";
18
- type JsonObject = Record<string, unknown>;
19
- type ModelInfo = NonNullable<ExtensionContext["model"]>;
20
- type FastConfig = {
21
- models: Record<string, FastMode>;
22
- };
23
-
24
- function emptyConfig(): FastConfig {
25
- return { models: {} };
26
- }
27
-
28
- function getConfigPath(): string {
29
- return path.join(getAgentDir(), "openai-fast.json");
30
- }
31
-
32
- function isObject(value: unknown): value is JsonObject {
33
- return typeof value === "object" && value !== null && !Array.isArray(value);
34
- }
35
-
36
- function normalizeFastMode(value: string): FastMode | undefined {
37
- return value.trim().toLowerCase() === "fast" ? "fast" : undefined;
38
- }
39
-
40
- function parseFastSetting(value: string): FastSetting | undefined {
41
- const normalized = value.trim().toLowerCase();
42
-
43
- if (normalized === "on" || normalized === "enabled") return "fast";
44
- if (normalized === "off" || normalized === "disabled") return "auto";
45
-
46
- return undefined;
47
- }
48
-
49
- function getFastArgumentCompletions(
50
- prefix: string,
51
- ): Array<{ value: string; label: string }> | null {
52
- const trimmed = prefix.trim().toLowerCase();
53
- if (trimmed.includes(" ")) return null;
54
-
55
- const options = ["on", "off", "enabled", "disabled"];
56
- const matches = options.filter((option) => option.startsWith(trimmed));
57
- if (!matches.length) return null;
58
-
59
- return matches.map((option) => ({ value: option, label: option }));
60
- }
61
-
62
- function parseConfig(value: unknown): FastConfig {
63
- if (!isObject(value) || !isObject(value.models)) return emptyConfig();
64
-
65
- const models: Record<string, FastMode> = {};
66
- for (const [key, rawMode] of Object.entries(value.models)) {
67
- const normalizedKey = key.trim();
68
- if (!normalizedKey || typeof rawMode !== "string") continue;
69
-
70
- const mode = normalizeFastMode(rawMode);
71
- if (!mode) continue;
72
-
73
- models[normalizedKey] = mode;
74
- }
75
-
76
- return { models };
77
- }
78
-
79
- async function loadConfig(): Promise<FastConfig> {
80
- try {
81
- const raw = await readFile(getConfigPath(), "utf8");
82
- return parseConfig(JSON.parse(raw) as unknown);
83
- } catch (error) {
84
- const code = (error as NodeJS.ErrnoException).code;
85
- if (code !== "ENOENT") {
86
- const message = error instanceof Error ? error.message : String(error);
87
- console.warn(`[openai-fast] Failed to load config: ${message}`);
88
- }
89
-
90
- return emptyConfig();
91
- }
92
- }
93
-
94
- async function saveConfig(config: FastConfig): Promise<void> {
95
- const configPath = getConfigPath();
96
- await mkdir(path.dirname(configPath), { recursive: true });
97
- await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
98
- }
99
-
100
- function isSupportedModel(model: ExtensionContext["model"]): model is ModelInfo {
101
- return !!model && SUPPORTED_APIS.has(model.api);
102
- }
103
-
104
- function getExactModelKey(model: Pick<ModelInfo, "provider" | "id">): string {
105
- return `${model.provider}/${model.id}`;
106
- }
107
-
108
- function resolveFastMode(
109
- config: FastConfig,
110
- model: Pick<ModelInfo, "provider" | "id">,
111
- ): { key?: string; mode?: FastMode } {
112
- const exactKey = getExactModelKey(model);
113
- const exactMode = config.models[exactKey];
114
- if (exactMode) return { key: exactKey, mode: exactMode };
115
-
116
- const sharedMode = config.models[model.id];
117
- if (sharedMode) return { key: model.id, mode: sharedMode };
118
-
119
- return {};
120
- }
121
-
122
- function setFastMode(config: FastConfig, key: string, setting: FastSetting): FastConfig {
123
- const models = { ...config.models };
124
-
125
- if (setting === "auto") delete models[key];
126
- else models[key] = setting;
127
-
128
- return { models };
129
- }
130
-
131
- function updateStatus(ctx: ExtensionContext, config: FastConfig): void {
132
- if (!ctx.hasUI) return;
133
- if (!isSupportedModel(ctx.model)) {
134
- ctx.ui.setStatus(STATUS_KEY, undefined);
135
- return;
136
- }
137
-
138
- const { mode } = resolveFastMode(config, ctx.model);
139
- ctx.ui.setStatus(STATUS_KEY, mode ? ctx.ui.theme.fg("dim", mode) : undefined);
140
- }
141
-
142
- export default function openaiFastExtension(pi: ExtensionAPI): void {
143
- let config = emptyConfig();
144
-
145
- async function applySetting(setting: FastSetting, ctx: ExtensionContext): Promise<void> {
146
- const model = ctx.model;
147
- if (!model) {
148
- ctx.ui.notify("No active model.", "warning");
149
- return;
150
- }
151
-
152
- if (!isSupportedModel(model)) {
153
- ctx.ui.notify(
154
- "Current model does not support fast mode. Supported APIs: openai-responses, openai-codex-responses, azure-openai-responses.",
155
- "warning",
156
- );
157
- updateStatus(ctx, config);
158
- return;
159
- }
160
-
161
- const resolved = resolveFastMode(config, model);
162
- const configKey = resolved.key ?? getExactModelKey(model);
163
- const nextConfig = setFastMode(config, configKey, setting);
164
-
165
- try {
166
- await saveConfig(nextConfig);
167
- } catch (error) {
168
- const message = error instanceof Error ? error.message : String(error);
169
- ctx.ui.notify(`Failed to save ${getConfigPath()}: ${message}`, "error");
170
- return;
171
- }
172
-
173
- config = nextConfig;
174
- updateStatus(ctx, config);
175
-
176
- const modelLabel = getExactModelKey(model);
177
- if (setting === "auto") {
178
- ctx.ui.notify(`Fast mode reset to auto for ${modelLabel}`, "info");
179
- return;
180
- }
181
-
182
- ctx.ui.notify(`Fast mode enabled for ${modelLabel}`, "info");
183
- }
184
-
185
- pi.registerCommand("fast", {
186
- description: "Toggle priority service tier for the current OpenAI model",
187
- getArgumentCompletions: getFastArgumentCompletions,
188
- handler: async (args, ctx) => {
189
- const arg = args.trim();
190
- if (arg === "") {
191
- const nextSetting: FastSetting =
192
- isSupportedModel(ctx.model) && resolveFastMode(config, ctx.model).mode ? "auto" : "fast";
193
- await applySetting(nextSetting, ctx);
194
- return;
195
- }
196
-
197
- const setting = parseFastSetting(arg);
198
- if (!setting) {
199
- ctx.ui.notify("Usage: /fast [on|off|enabled|disabled]", "error");
200
- return;
201
- }
202
-
203
- await applySetting(setting, ctx);
204
- },
205
- });
206
-
207
- pi.on("session_start", async (_event, ctx) => {
208
- config = await loadConfig();
209
- updateStatus(ctx, config);
210
- });
211
-
212
- pi.on("model_select", async (_event, ctx) => {
213
- updateStatus(ctx, config);
214
- });
215
-
216
- pi.on("before_provider_request", (event, ctx) => {
217
- const model = ctx.model;
218
- if (!isSupportedModel(model)) return;
219
- if (!resolveFastMode(config, model).mode) return;
220
-
221
- const payload = event.payload;
222
- if (!isObject(payload)) return;
223
-
224
- return {
225
- ...payload,
226
- service_tier: "priority",
227
- };
228
- });
229
- }
@@ -1,77 +0,0 @@
1
- import type { BrowserSession, WebsearchResult } from "../types.js";
2
- import { browserHeaders, buildCookieHeader } from "../normalize.js";
3
- import { fetchJson, withTimeout } from "./shared.js";
4
- import { decodeJwtAccountId, runOpenAICodexSearch } from "./openai-codex.shared.js";
5
-
6
- const CHATGPT_BASE_URL = "https://chatgpt.com";
7
- const SESSION_URL = `${CHATGPT_BASE_URL}/api/auth/session`;
8
- const PREFERRED_MODELS = ["gpt-5.4", "gpt-5.3-codex-spark", "gpt-5.1-codex-mini"];
9
-
10
- interface ChatGptSessionPayload {
11
- accessToken?: string;
12
- user?: {
13
- email?: string;
14
- name?: string;
15
- };
16
- }
17
-
18
- export const browserOpenAICodex = {
19
- backend: "openai-codex" as const,
20
- domains: ["chatgpt.com"],
21
- async search(
22
- session: BrowserSession,
23
- query: string,
24
- signal?: AbortSignal,
25
- ): Promise<WebsearchResult> {
26
- const cookieHeader = buildCookieHeader(SESSION_URL, session.cookies);
27
- if (!cookieHeader) {
28
- throw new Error("No ChatGPT session cookies found in browser profile.");
29
- }
30
-
31
- const chatSession = await fetchJson<ChatGptSessionPayload>(SESSION_URL, {
32
- headers: browserHeaders({
33
- cookieHeader,
34
- origin: CHATGPT_BASE_URL,
35
- referer: `${CHATGPT_BASE_URL}/`,
36
- }),
37
- signal: withTimeout(signal, 20_000),
38
- });
39
-
40
- if (!chatSession.accessToken) {
41
- throw new Error("No ChatGPT access token found in session.");
42
- }
43
-
44
- const result = await searchWithPreferredModel(chatSession.accessToken, query, signal);
45
-
46
- return {
47
- backend: "openai-codex",
48
- authSource: session.profile.family,
49
- browserName: session.profile.browserName,
50
- profile: session.profile.profileName,
51
- accountLabel: chatSession.user?.email ?? chatSession.user?.name,
52
- answer: result.answer,
53
- sources: result.sources,
54
- };
55
- },
56
- };
57
-
58
- async function searchWithPreferredModel(accessToken: string, query: string, signal?: AbortSignal) {
59
- let lastError: string | null = null;
60
-
61
- for (const model of PREFERRED_MODELS) {
62
- try {
63
- return await runOpenAICodexSearch({
64
- apiKey: accessToken,
65
- accountId: decodeJwtAccountId(accessToken),
66
- model,
67
- query,
68
- baseUrl: `${CHATGPT_BASE_URL}/backend-api`,
69
- signal,
70
- });
71
- } catch (error) {
72
- lastError = error instanceof Error ? error.message : String(error);
73
- }
74
- }
75
-
76
- throw new Error(lastError ?? "ChatGPT Codex search failed.");
77
- }