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.
- package/README.md +15 -12
- package/extensions/answer.ts +129 -79
- package/extensions/branch-term/README.md +7 -0
- package/extensions/{branch-term.ts → branch-term/index.ts} +113 -104
- package/extensions/btw.ts +17 -2
- package/extensions/caffeinate/README.md +5 -0
- package/extensions/caffeinate/index.ts +144 -0
- package/extensions/fast.ts +292 -0
- package/extensions/ghostty.ts +214 -211
- package/extensions/git-diff-stats.ts +124 -84
- package/extensions/git-pr-status.ts +274 -208
- package/extensions/insights.ts +109 -191
- package/extensions/loop.ts +149 -145
- package/extensions/memory.ts +138 -86
- package/extensions/notify.ts +14 -26
- package/extensions/openai-verbosity.ts +108 -42
- package/extensions/review/fix.ts +15 -6
- package/extensions/review/git.ts +93 -103
- package/extensions/review/index.ts +60 -62
- package/extensions/review/interrupt.ts +117 -0
- package/extensions/review/message-queue.ts +45 -12
- package/extensions/review/models.ts +79 -3
- package/extensions/review/prompts.ts +41 -38
- package/extensions/review/review.ts +161 -166
- package/extensions/review/runner.ts +139 -147
- package/extensions/review/runtime.ts +173 -120
- package/extensions/review/triage.ts +41 -44
- package/extensions/sandbox/bash.ts +775 -0
- package/extensions/sandbox/command.ts +605 -0
- package/extensions/sandbox/config.ts +764 -0
- package/extensions/sandbox/index.ts +132 -3338
- package/extensions/sandbox/permissions/dialog.ts +118 -0
- package/extensions/sandbox/permissions/filesystem.ts +559 -0
- package/extensions/sandbox/permissions/mach-lookup.ts +186 -0
- package/extensions/sandbox/permissions/network.ts +164 -0
- package/extensions/sandbox/permissions/unsandboxed.ts +270 -0
- package/extensions/sandbox/runtime.ts +616 -0
- package/extensions/stash.ts +28 -15
- package/extensions/subagent/README.md +73 -0
- package/extensions/subagent/index.ts +822 -0
- package/extensions/subagent/interrupt.ts +117 -0
- package/extensions/subagent/permissions.ts +101 -0
- package/extensions/subagent/rpc.ts +177 -0
- package/extensions/tool-display-mode.ts +267 -64
- package/extensions/usage/index.ts +249 -356
- package/extensions/usage/openrouter.ts +10 -2
- package/extensions/websearch/README.md +12 -47
- package/extensions/websearch/config.ts +5 -2
- package/extensions/websearch/index.ts +94 -109
- package/extensions/websearch/output.ts +51 -0
- package/extensions/websearch/providers/anthropic.pi.ts +27 -44
- package/extensions/websearch/providers/gemini.browser.ts +68 -77
- package/extensions/websearch/providers/gemini.pi.ts +17 -17
- package/extensions/websearch/providers/openai-codex.pi.ts +145 -7
- package/extensions/websearch/providers/pi-model.shared.ts +33 -39
- package/extensions/websearch/providers/shared.ts +87 -2
- package/extensions/websearch/types.ts +0 -3
- package/extensions/worktree.ts +132 -172
- package/package.json +10 -5
- package/skills/browser-tools/SKILL.md +29 -234
- package/skills/browser-tools/references/cookies.md +36 -0
- package/skills/browser-tools/references/interaction.md +90 -0
- package/skills/browser-tools/references/logging.md +34 -0
- package/skills/git-clean-history/SKILL.md +6 -6
- package/skills/git-commit/SKILL.md +5 -3
- package/skills/github-pull-request/SKILL.md +60 -0
- package/skills/github-pull-request/references/create.md +40 -0
- package/skills/github-pull-request/references/stewardship.md +60 -0
- package/skills/oracle/SKILL.md +3 -3
- package/skills/oracle/scripts/oracle +10 -1
- package/skills/sentry/SKILL.md +15 -185
- package/skills/sentry/references/events.md +79 -0
- package/skills/sentry/references/issues.md +62 -0
- package/skills/sentry/references/logs.md +46 -0
- package/skills/update-changelog/SKILL.md +27 -121
- package/skills/web-design/SKILL.md +16 -105
- package/themes/tau-dark.json +4 -0
- package/extensions/openai-fast.ts +0 -229
- package/extensions/websearch/providers/openai-codex.browser.ts +0 -77
- 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
|
|
3
|
+
description: "Update CHANGELOG.md with notable user-facing changes using Keep a Changelog conventions."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Update Changelog
|
|
6
|
+
# Update Changelog
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
https://keepachangelog.com/en/1.1.0/
|
|
10
|
+
## Choose the scope
|
|
12
11
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
18
|
+
## Entry rules
|
|
22
19
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
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
|
-
##
|
|
28
|
+
## Categories
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
Create only needed headings. Prefer the six standard types:
|
|
29
31
|
|
|
30
|
-
|
|
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
|
-
|
|
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
|
|
6
|
+
# Web Design
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
##
|
|
10
|
+
## Workflow
|
|
11
11
|
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
## Design standards
|
|
22
22
|
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
package/themes/tau-dark.json
CHANGED
|
@@ -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
|
-
}
|