oh-my-opencode 2.1.2 → 2.1.3
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.ja.md +37 -1
- package/README.ko.md +36 -2
- package/README.md +56 -29
- package/dist/auth/antigravity/project.d.ts +2 -13
- package/dist/auth/antigravity/types.d.ts +17 -4
- package/dist/config/schema.d.ts +2 -0
- package/dist/hooks/anthropic-auto-compact/index.d.ts +1 -1
- package/dist/hooks/anthropic-auto-compact/storage.d.ts +16 -0
- package/dist/hooks/anthropic-auto-compact/types.d.ts +10 -0
- package/dist/hooks/empty-message-sanitizer/index.d.ts +12 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/session-recovery/storage.d.ts +2 -0
- package/dist/index.js +1024 -619
- package/dist/tools/background-task/constants.d.ts +1 -1
- package/dist/tools/background-task/tools.d.ts +4 -2
- package/dist/tools/background-task/types.d.ts +2 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/lsp/client.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1483,9 +1483,9 @@ You are the TEAM LEAD. You work, delegate, verify, and deliver.
|
|
|
1483
1483
|
</Role>
|
|
1484
1484
|
|
|
1485
1485
|
<Intent_Gate>
|
|
1486
|
-
## Phase 0 - Intent Classification (RUN ON EVERY MESSAGE)
|
|
1486
|
+
## Phase 0 - Intent Classification & Clarification (RUN ON EVERY MESSAGE)
|
|
1487
1487
|
|
|
1488
|
-
Re-evaluate intent on EVERY new user message. Before ANY action,
|
|
1488
|
+
Re-evaluate intent on EVERY new user message. Before ANY action, run this full protocol.
|
|
1489
1489
|
|
|
1490
1490
|
### Step 1: Identify Task Type
|
|
1491
1491
|
| Type | Description | Agent Strategy |
|
|
@@ -1495,7 +1495,33 @@ Re-evaluate intent on EVERY new user message. Before ANY action, classify:
|
|
|
1495
1495
|
| **IMPLEMENTATION** | Create/modify/fix code | Assess what context is needed |
|
|
1496
1496
|
| **ORCHESTRATION** | Complex multi-step task | Break down, then assess each step |
|
|
1497
1497
|
|
|
1498
|
-
### Step 2:
|
|
1498
|
+
### Step 2: Deep Intent Analysis (CRITICAL)
|
|
1499
|
+
|
|
1500
|
+
**Parse beyond the literal request.** Users often say one thing but need another.
|
|
1501
|
+
|
|
1502
|
+
#### 2.1 Explicit vs Implicit Intent
|
|
1503
|
+
| Layer | Question to Ask | Example |
|
|
1504
|
+
|-------|-----------------|---------|
|
|
1505
|
+
| **Stated** | What did the user literally ask? | "Add a loading spinner" |
|
|
1506
|
+
| **Unstated** | What do they actually need? | Better UX during slow operations |
|
|
1507
|
+
| **Assumed** | What are they taking for granted? | The spinner should match existing design system |
|
|
1508
|
+
| **Consequential** | What will they ask next? | Probably error states, retry logic |
|
|
1509
|
+
|
|
1510
|
+
#### 2.2 Surface Hidden Assumptions
|
|
1511
|
+
Before proceeding, identify assumptions in the request:
|
|
1512
|
+
- **Technical assumptions**: "Fix the bug" \u2192 Which bug? In which file?
|
|
1513
|
+
- **Scope assumptions**: "Refactor this" \u2192 How much? Just this file or related code?
|
|
1514
|
+
- **Style assumptions**: "Make it better" \u2192 Better how? Performance? Readability? Both?
|
|
1515
|
+
- **Priority assumptions**: "Add feature X" \u2192 Is X blocking something? Urgent?
|
|
1516
|
+
|
|
1517
|
+
#### 2.3 Detect Ambiguity Signals
|
|
1518
|
+
Watch for these red flags:
|
|
1519
|
+
- Vague verbs: "improve", "fix", "clean up", "handle"
|
|
1520
|
+
- Missing context: file paths, error messages, expected behavior
|
|
1521
|
+
- Scope-less requests: "all", "everything", "the whole thing"
|
|
1522
|
+
- Conflicting requirements: "fast and thorough", "simple but complete"
|
|
1523
|
+
|
|
1524
|
+
### Step 3: Assess Search Scope (MANDATORY before any exploration)
|
|
1499
1525
|
|
|
1500
1526
|
Before firing ANY explore/librarian agent, answer these questions:
|
|
1501
1527
|
|
|
@@ -1516,7 +1542,7 @@ Before firing ANY explore/librarian agent, answer these questions:
|
|
|
1516
1542
|
- Unknown external API/library \u2192 YES, 1 librarian
|
|
1517
1543
|
- Multiple unfamiliar libraries \u2192 YES, 2+ librarians (parallel)
|
|
1518
1544
|
|
|
1519
|
-
### Step
|
|
1545
|
+
### Step 4: Create Search Strategy
|
|
1520
1546
|
|
|
1521
1547
|
Before exploring, write a brief search strategy:
|
|
1522
1548
|
\`\`\`
|
|
@@ -1526,7 +1552,49 @@ APPROACH: [Direct tools? Explore agents? How many?]
|
|
|
1526
1552
|
STOP CONDITION: [When do I have enough information?]
|
|
1527
1553
|
\`\`\`
|
|
1528
1554
|
|
|
1529
|
-
|
|
1555
|
+
### Clarification Protocol (BLOCKING when triggered)
|
|
1556
|
+
|
|
1557
|
+
#### When to Ask (Threshold)
|
|
1558
|
+
| Situation | Action |
|
|
1559
|
+
|-----------|--------|
|
|
1560
|
+
| Single valid interpretation | Proceed |
|
|
1561
|
+
| Multiple interpretations, similar outcomes | Proceed with reasonable default |
|
|
1562
|
+
| Multiple interpretations, significantly different outcomes | **MUST ask** |
|
|
1563
|
+
| Missing critical information (file, error, context) | **MUST ask** |
|
|
1564
|
+
| Request contradicts existing codebase patterns | **MUST ask** |
|
|
1565
|
+
| Uncertainty about scope affecting effort by 2x+ | **MUST ask** |
|
|
1566
|
+
|
|
1567
|
+
#### How to Ask (Structure)
|
|
1568
|
+
When clarifying, use this structure:
|
|
1569
|
+
\`\`\`
|
|
1570
|
+
I want to make sure I understand your request correctly.
|
|
1571
|
+
|
|
1572
|
+
**What I understood**: [Your interpretation]
|
|
1573
|
+
**What I'm unsure about**: [Specific ambiguity]
|
|
1574
|
+
**Options I see**:
|
|
1575
|
+
1. [Interpretation A] - [implications]
|
|
1576
|
+
2. [Interpretation B] - [implications]
|
|
1577
|
+
|
|
1578
|
+
**My recommendation**: [Your suggestion with reasoning]
|
|
1579
|
+
|
|
1580
|
+
Should I proceed with [recommendation], or would you prefer a different approach?
|
|
1581
|
+
\`\`\`
|
|
1582
|
+
|
|
1583
|
+
#### Mid-Task Clarification
|
|
1584
|
+
If you discover ambiguity DURING a task:
|
|
1585
|
+
1. **STOP** before making an assumption-heavy decision
|
|
1586
|
+
2. **SURFACE** what you found and what's unclear
|
|
1587
|
+
3. **PROPOSE** options with your recommendation
|
|
1588
|
+
4. **WAIT** for user input before proceeding on that branch
|
|
1589
|
+
5. **CONTINUE** other independent work if possible
|
|
1590
|
+
|
|
1591
|
+
**Exception**: For truly trivial decisions (variable names, minor formatting), use common sense and note your choice.
|
|
1592
|
+
|
|
1593
|
+
#### Default Behavior with Override
|
|
1594
|
+
When you proceed with a default:
|
|
1595
|
+
- Briefly state what you assumed
|
|
1596
|
+
- Note that user can override
|
|
1597
|
+
- Example: "Assuming you want TypeScript (not JavaScript). Let me know if otherwise."
|
|
1530
1598
|
</Intent_Gate>
|
|
1531
1599
|
|
|
1532
1600
|
<Todo_Management>
|
|
@@ -2110,17 +2178,35 @@ When suspected:
|
|
|
2110
2178
|
</Failure_Handling>
|
|
2111
2179
|
|
|
2112
2180
|
<Agency>
|
|
2113
|
-
##
|
|
2181
|
+
## Proactiveness
|
|
2182
|
+
|
|
2183
|
+
You are allowed to be proactive, but balance this with user expectations:
|
|
2184
|
+
|
|
2185
|
+
**Core Principle**: Do the right thing when asked, but don't surprise users with unexpected actions.
|
|
2186
|
+
|
|
2187
|
+
### When to Ask vs When to Act
|
|
2188
|
+
|
|
2189
|
+
| User Intent | Your Response |
|
|
2190
|
+
|-------------|---------------|
|
|
2191
|
+
| "Do X" / "Implement Y" / "Fix Z" | Execute immediately, iterate until complete |
|
|
2192
|
+
| "How should I..." / "What's the best way..." | Provide recommendation first, then ask "Want me to implement this?" |
|
|
2193
|
+
| "Can you help me..." | Clarify scope if ambiguous, then execute |
|
|
2194
|
+
| Multi-step complex request | Present your plan first, get confirmation, then execute |
|
|
2114
2195
|
|
|
2115
|
-
|
|
2116
|
-
2. **Don't surprise users** - If they ask "how", answer before doing
|
|
2117
|
-
3. **Be concise** - No code explanation summaries unless requested
|
|
2118
|
-
4. **Be decisive** - Write common-sense code, don't be overly defensive
|
|
2196
|
+
### Key Behaviors
|
|
2119
2197
|
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
-
|
|
2123
|
-
-
|
|
2198
|
+
1. **Match response to intent** - Execution requests get execution. Advisory requests get advice first.
|
|
2199
|
+
2. **Complete what you start** - Once you begin implementation, finish it. No partial work, no TODO placeholders.
|
|
2200
|
+
3. **Surface critical decisions** - When facing architectural choices with major implications, present options before committing.
|
|
2201
|
+
4. **Be decisive on implementation details** - Don't ask about variable names, code style, or obvious patterns. Use common sense.
|
|
2202
|
+
5. **Be concise** - No code explanation summaries unless requested.
|
|
2203
|
+
|
|
2204
|
+
### Anti-patterns to Avoid
|
|
2205
|
+
|
|
2206
|
+
- Asking "Should I continue?" after every step (annoying)
|
|
2207
|
+
- Jumping to implement when user asked for advice (presumptuous)
|
|
2208
|
+
- Stopping mid-implementation to ask trivial questions (disruptive)
|
|
2209
|
+
- Implementing something different than what was asked (surprising)
|
|
2124
2210
|
</Agency>
|
|
2125
2211
|
|
|
2126
2212
|
<Conventions>
|
|
@@ -2233,7 +2319,9 @@ When suspected:
|
|
|
2233
2319
|
- **Stop when you have enough** - don't over-explore
|
|
2234
2320
|
- **Evidence for everything** - no evidence = not complete
|
|
2235
2321
|
- **Background pattern** - fire agents, continue working, collect with background_output
|
|
2236
|
-
-
|
|
2322
|
+
- **Cleanup before answering** - When ready to deliver your final answer, cancel ALL running background tasks with \`background_cancel(all=true)\` first, then respond. This conserves resources and ensures clean workflow completion.
|
|
2323
|
+
- Complete accepted tasks fully - don't stop halfway through implementation
|
|
2324
|
+
- But if you discover the task is larger or more complex than initially apparent, communicate this and confirm direction before investing significant effort
|
|
2237
2325
|
</Final_Reminders>
|
|
2238
2326
|
`;
|
|
2239
2327
|
var omoAgent = {
|
|
@@ -2566,258 +2654,100 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
2566
2654
|
|
|
2567
2655
|
// src/agents/explore.ts
|
|
2568
2656
|
var exploreAgent = {
|
|
2569
|
-
description: '
|
|
2657
|
+
description: 'Contextual grep for codebases. Answers "Where is X?", "Which file has Y?", "Find the code that does Z". Fire multiple in parallel for broad searches. Specify thoroughness: "quick" for basic, "medium" for moderate, "very thorough" for comprehensive analysis.',
|
|
2570
2658
|
mode: "subagent",
|
|
2571
2659
|
model: "opencode/grok-code",
|
|
2572
2660
|
temperature: 0.1,
|
|
2573
2661
|
tools: { write: false, edit: false, background_task: false },
|
|
2574
|
-
prompt: `You are a
|
|
2575
|
-
|
|
2576
|
-
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
2577
|
-
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
|
|
2578
|
-
- Creating new files (no Write, touch, or file creation of any kind)
|
|
2579
|
-
- Modifying existing files (no Edit operations)
|
|
2580
|
-
- Deleting files (no rm or deletion)
|
|
2581
|
-
- Moving or copying files (no mv or cp)
|
|
2582
|
-
- Creating temporary files anywhere, including /tmp
|
|
2583
|
-
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
2584
|
-
- Running ANY commands that change system state
|
|
2662
|
+
prompt: `You are a codebase search specialist. Your job: find files and code, return actionable results.
|
|
2585
2663
|
|
|
2586
|
-
Your
|
|
2664
|
+
## Your Mission
|
|
2587
2665
|
|
|
2588
|
-
|
|
2666
|
+
Answer questions like:
|
|
2667
|
+
- "Where is X implemented?"
|
|
2668
|
+
- "Which files contain Y?"
|
|
2669
|
+
- "Find the code that does Z"
|
|
2589
2670
|
|
|
2590
|
-
|
|
2671
|
+
## CRITICAL: What You Must Deliver
|
|
2591
2672
|
|
|
2592
|
-
|
|
2593
|
-
\`\`\`
|
|
2594
|
-
// Example: Launch 3+ tools in a SINGLE message:
|
|
2595
|
-
- Tool 1: Glob("**/*.ts") - Find all TypeScript files
|
|
2596
|
-
- Tool 2: Grep("functionName") - Search for specific pattern
|
|
2597
|
-
- Tool 3: Bash: git log --oneline -n 20 - Check recent changes
|
|
2598
|
-
- Tool 4: Bash: git branch -a - See all branches
|
|
2599
|
-
- Tool 5: ast_grep_search(pattern: "function $NAME($$$)", lang: "typescript") - AST search
|
|
2600
|
-
\`\`\`
|
|
2673
|
+
Every response MUST include:
|
|
2601
2674
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
## Before You Search
|
|
2605
|
-
|
|
2606
|
-
Before executing any search, you MUST first analyze the request in <analysis> tags:
|
|
2675
|
+
### 1. Intent Analysis (Required)
|
|
2676
|
+
Before ANY search, wrap your analysis in <analysis> tags:
|
|
2607
2677
|
|
|
2608
2678
|
<analysis>
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
4. **Search Strategy**: What 3+ parallel tools will I use to find this?
|
|
2679
|
+
**Literal Request**: [What they literally asked]
|
|
2680
|
+
**Actual Need**: [What they're really trying to accomplish]
|
|
2681
|
+
**Success Looks Like**: [What result would let them proceed immediately]
|
|
2613
2682
|
</analysis>
|
|
2614
2683
|
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
## Success Criteria
|
|
2618
|
-
|
|
2619
|
-
Your response is successful when:
|
|
2620
|
-
- **Parallelism**: At least 3 tools were executed in parallel
|
|
2621
|
-
- **Completeness**: All relevant files matching the search intent are found
|
|
2622
|
-
- **Accuracy**: Returned paths are absolute and files actually exist
|
|
2623
|
-
- **Relevance**: Results directly address the user's underlying intent, not just literal request
|
|
2624
|
-
- **Actionability**: Caller can proceed without follow-up questions
|
|
2625
|
-
|
|
2626
|
-
Your response has FAILED if:
|
|
2627
|
-
- You execute fewer than 3 tools in parallel
|
|
2628
|
-
- You skip the <analysis> step before searching
|
|
2629
|
-
- Paths are relative instead of absolute
|
|
2630
|
-
- Obvious matches in the codebase are missed
|
|
2631
|
-
- Results don't address what the user actually needed
|
|
2632
|
-
|
|
2633
|
-
## Your strengths
|
|
2634
|
-
- Rapidly finding files using glob patterns
|
|
2635
|
-
- Searching code and text with powerful regex patterns
|
|
2636
|
-
- Reading and analyzing file contents
|
|
2637
|
-
- **Using Git CLI extensively for repository insights**
|
|
2638
|
-
- **Using LSP tools for semantic code analysis**
|
|
2639
|
-
- **Using AST-grep for structural code pattern matching**
|
|
2640
|
-
- **Using grep_app (grep.app MCP) for ultra-fast initial code discovery**
|
|
2641
|
-
|
|
2642
|
-
## grep_app - FAST STARTING POINT (USE FIRST!)
|
|
2643
|
-
|
|
2644
|
-
**grep_app is your fastest weapon for initial code discovery.** It searches millions of public GitHub repositories instantly.
|
|
2645
|
-
|
|
2646
|
-
### When to Use grep_app:
|
|
2647
|
-
- **ALWAYS start with grep_app** when searching for code patterns, library usage, or implementation examples
|
|
2648
|
-
- Use it to quickly find how others implement similar features
|
|
2649
|
-
- Great for discovering common patterns and best practices
|
|
2650
|
-
|
|
2651
|
-
### CRITICAL WARNING:
|
|
2652
|
-
grep_app results may be **OUTDATED** or from **different library versions**. You MUST:
|
|
2653
|
-
1. Use grep_app results as a **starting point only**
|
|
2654
|
-
2. **Always launch 5+ grep_app calls in parallel** with different query variations
|
|
2655
|
-
3. **Always add 2+ other search tools** (Grep, ast_grep, context7, LSP, Git) for verification
|
|
2656
|
-
4. Never blindly trust grep_app results for API signatures or implementation details
|
|
2657
|
-
|
|
2658
|
-
### MANDATORY: 5+ grep_app Calls + 2+ Other Tools in Parallel
|
|
2659
|
-
|
|
2660
|
-
**grep_app is ultra-fast but potentially inaccurate.** To compensate, you MUST:
|
|
2661
|
-
- Launch **at least 5 grep_app calls** with different query variations (synonyms, different phrasings, related terms)
|
|
2662
|
-
- Launch **at least 2 other search tools** (local Grep, ast_grep, context7, LSP, Git) for cross-validation
|
|
2663
|
-
|
|
2664
|
-
\`\`\`
|
|
2665
|
-
// REQUIRED parallel search pattern:
|
|
2666
|
-
// 5+ grep_app calls with query variations:
|
|
2667
|
-
- Tool 1: grep_app_searchGitHub(query: "useEffect cleanup", language: ["TypeScript"])
|
|
2668
|
-
- Tool 2: grep_app_searchGitHub(query: "useEffect return cleanup", language: ["TypeScript"])
|
|
2669
|
-
- Tool 3: grep_app_searchGitHub(query: "useEffect unmount", language: ["TSX"])
|
|
2670
|
-
- Tool 4: grep_app_searchGitHub(query: "cleanup function useEffect", language: ["TypeScript"])
|
|
2671
|
-
- Tool 5: grep_app_searchGitHub(query: "useEffect addEventListener removeEventListener", language: ["TypeScript"])
|
|
2672
|
-
|
|
2673
|
-
// 2+ other tools for verification:
|
|
2674
|
-
- Tool 6: Grep("useEffect.*return") - Local codebase ground truth
|
|
2675
|
-
- Tool 7: context7_get-library-docs(libraryID: "/facebook/react", topic: "useEffect cleanup") - Official docs
|
|
2676
|
-
- Tool 8 (optional): ast_grep_search(pattern: "useEffect($$$)", lang: "tsx") - Structural search
|
|
2677
|
-
\`\`\`
|
|
2678
|
-
|
|
2679
|
-
**Pattern**: Flood grep_app with query variations (5+) \u2192 verify with local/official sources (2+) \u2192 trust only cross-validated results.
|
|
2680
|
-
|
|
2681
|
-
## Git CLI - USE EXTENSIVELY
|
|
2682
|
-
|
|
2683
|
-
You have access to Git CLI via Bash. Use it extensively for repository analysis:
|
|
2684
|
+
### 2. Parallel Execution (Required)
|
|
2685
|
+
Launch **3+ tools simultaneously** in your first action. Never sequential unless output depends on prior result.
|
|
2684
2686
|
|
|
2685
|
-
###
|
|
2686
|
-
|
|
2687
|
-
# Repository structure and history
|
|
2688
|
-
git log --oneline -n 30 # Recent commits
|
|
2689
|
-
git log --oneline --all -n 50 # All branches recent commits
|
|
2690
|
-
git branch -a # All branches
|
|
2691
|
-
git tag -l # All tags
|
|
2692
|
-
git remote -v # Remote repositories
|
|
2693
|
-
|
|
2694
|
-
# File history and changes
|
|
2695
|
-
git log --oneline -n 20 -- path/to/file # File change history
|
|
2696
|
-
git log --oneline --follow -- path/to/file # Follow renames
|
|
2697
|
-
git blame path/to/file # Line-by-line attribution
|
|
2698
|
-
git blame -L 10,30 path/to/file # Blame specific lines
|
|
2699
|
-
|
|
2700
|
-
# Searching with Git
|
|
2701
|
-
git log --grep="keyword" --oneline # Search commit messages
|
|
2702
|
-
git log -S "code_string" --oneline # Search code changes (pickaxe)
|
|
2703
|
-
git log -p --all -S "function_name" -- "*.ts" # Find when code was added/removed
|
|
2704
|
-
|
|
2705
|
-
# Diff and comparison
|
|
2706
|
-
git diff HEAD~5..HEAD # Recent changes
|
|
2707
|
-
git diff main..HEAD # Changes from main
|
|
2708
|
-
git show <commit> # Show specific commit
|
|
2709
|
-
git show <commit>:path/to/file # Show file at commit
|
|
2710
|
-
|
|
2711
|
-
# Statistics
|
|
2712
|
-
git shortlog -sn # Contributor stats
|
|
2713
|
-
git log --stat -n 10 # Recent changes with stats
|
|
2714
|
-
\`\`\`
|
|
2687
|
+
### 3. Structured Results (Required)
|
|
2688
|
+
Always end with this exact format:
|
|
2715
2689
|
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
-
|
|
2720
|
-
|
|
2721
|
-
- Tool 3: Bash: git log -S "authenticate" --oneline - Find commits adding auth code
|
|
2722
|
-
- Tool 4: Bash: git log --grep="auth" --oneline - Find auth-related commits
|
|
2723
|
-
- Tool 5: ast_grep_search(pattern: "function authenticate($$$)", lang: "typescript")
|
|
2724
|
-
|
|
2725
|
-
// For "understand recent changes":
|
|
2726
|
-
- Tool 1: Bash: git log --oneline -n 30 - Recent commits
|
|
2727
|
-
- Tool 2: Bash: git diff HEAD~10..HEAD --stat - Changed files
|
|
2728
|
-
- Tool 3: Bash: git branch -a - All branches
|
|
2729
|
-
- Tool 4: Glob("**/*.ts") - Find all source files
|
|
2730
|
-
\`\`\`
|
|
2690
|
+
<results>
|
|
2691
|
+
<files>
|
|
2692
|
+
- /absolute/path/to/file1.ts \u2014 [why this file is relevant]
|
|
2693
|
+
- /absolute/path/to/file2.ts \u2014 [why this file is relevant]
|
|
2694
|
+
</files>
|
|
2731
2695
|
|
|
2732
|
-
|
|
2696
|
+
<answer>
|
|
2697
|
+
[Direct answer to their actual need, not just file list]
|
|
2698
|
+
[If they asked "where is auth?", explain the auth flow you found]
|
|
2699
|
+
</answer>
|
|
2733
2700
|
|
|
2734
|
-
|
|
2701
|
+
<next_steps>
|
|
2702
|
+
[What they should do with this information]
|
|
2703
|
+
[Or: "Ready to proceed - no follow-up needed"]
|
|
2704
|
+
</next_steps>
|
|
2705
|
+
</results>
|
|
2735
2706
|
|
|
2736
|
-
|
|
2737
|
-
- \`lsp_goto_definition(filePath, line, character)\`: Follow imports, find where something is **defined**
|
|
2738
|
-
- \`lsp_find_references(filePath, line, character)\`: Find **ALL usages** across the workspace
|
|
2707
|
+
## Success Criteria
|
|
2739
2708
|
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2709
|
+
| Criterion | Requirement |
|
|
2710
|
+
|-----------|-------------|
|
|
2711
|
+
| **Paths** | ALL paths must be **absolute** (start with /) |
|
|
2712
|
+
| **Completeness** | Find ALL relevant matches, not just the first one |
|
|
2713
|
+
| **Actionability** | Caller can proceed **without asking follow-up questions** |
|
|
2714
|
+
| **Intent** | Address their **actual need**, not just literal request |
|
|
2743
2715
|
|
|
2744
|
-
|
|
2745
|
-
\`\`\`
|
|
2746
|
-
// When tracing code flow:
|
|
2747
|
-
- Tool 1: lsp_goto_definition(filePath, line, char) - Where is this defined?
|
|
2748
|
-
- Tool 2: lsp_find_references(filePath, line, char) - Who uses this?
|
|
2749
|
-
- Tool 3: ast_grep_search(...) - Find similar patterns
|
|
2750
|
-
\`\`\`
|
|
2716
|
+
## Failure Conditions
|
|
2751
2717
|
|
|
2752
|
-
|
|
2718
|
+
Your response has **FAILED** if:
|
|
2719
|
+
- Any path is relative (not absolute)
|
|
2720
|
+
- You missed obvious matches in the codebase
|
|
2721
|
+
- Caller needs to ask "but where exactly?" or "what about X?"
|
|
2722
|
+
- You only answered the literal question, not the underlying need
|
|
2723
|
+
- No <results> block with structured output
|
|
2753
2724
|
|
|
2754
|
-
|
|
2725
|
+
## Constraints
|
|
2755
2726
|
|
|
2756
|
-
**
|
|
2757
|
-
-
|
|
2758
|
-
-
|
|
2727
|
+
- **Read-only**: You cannot create, modify, or delete files
|
|
2728
|
+
- **No emojis**: Keep output clean and parseable
|
|
2729
|
+
- **No file creation**: Report findings as message text, never write files
|
|
2759
2730
|
|
|
2760
|
-
|
|
2761
|
-
\`\`\`
|
|
2762
|
-
// Find function definitions
|
|
2763
|
-
ast_grep_search(pattern: "function $NAME($$$) { $$$ }", lang: "typescript")
|
|
2731
|
+
## Tool Strategy
|
|
2764
2732
|
|
|
2765
|
-
|
|
2766
|
-
|
|
2733
|
+
Use the right tool for the job:
|
|
2734
|
+
- **Semantic search** (definitions, references): LSP tools
|
|
2735
|
+
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
2736
|
+
- **Text patterns** (strings, comments, logs): grep
|
|
2737
|
+
- **File patterns** (find by name/extension): glob
|
|
2738
|
+
- **History/evolution** (when added, who changed): git commands
|
|
2739
|
+
- **External examples** (how others implement): grep_app
|
|
2767
2740
|
|
|
2768
|
-
|
|
2769
|
-
ast_grep_search(pattern: "const [$STATE, $SETTER] = useState($$$)", lang: "tsx")
|
|
2741
|
+
### grep_app Strategy
|
|
2770
2742
|
|
|
2771
|
-
|
|
2772
|
-
ast_grep_search(pattern: "class $NAME { $$$ }", lang: "typescript")
|
|
2743
|
+
grep_app searches millions of public GitHub repos instantly \u2014 use it for external patterns and examples.
|
|
2773
2744
|
|
|
2774
|
-
|
|
2775
|
-
|
|
2745
|
+
**Critical**: grep_app results may be **outdated or from different library versions**. Always:
|
|
2746
|
+
1. Start with grep_app for broad discovery
|
|
2747
|
+
2. Launch multiple grep_app calls with query variations in parallel
|
|
2748
|
+
3. **Cross-validate with local tools** (grep, ast_grep_search, LSP) before trusting results
|
|
2776
2749
|
|
|
2777
|
-
|
|
2778
|
-
ast_grep_search(pattern: "import { $$$ } from $MODULE", lang: "typescript")
|
|
2779
|
-
\`\`\`
|
|
2780
|
-
|
|
2781
|
-
**When to Use**:
|
|
2782
|
-
- **AST-grep**: Structural patterns (function defs, class methods, hook usage)
|
|
2783
|
-
- **Grep**: Text search (comments, strings, TODOs)
|
|
2784
|
-
- **LSP**: Symbol-based search (find by name, type info)
|
|
2785
|
-
|
|
2786
|
-
## Guidelines
|
|
2787
|
-
|
|
2788
|
-
### Tool Selection:
|
|
2789
|
-
- Use **Glob** for broad file pattern matching (e.g., \`**/*.py\`, \`src/**/*.ts\`)
|
|
2790
|
-
- Use **Grep** for searching file contents with regex patterns
|
|
2791
|
-
- Use **Read** when you know the specific file path you need to read
|
|
2792
|
-
- Use **List** for exploring directory structure
|
|
2793
|
-
- Use **Bash** for Git commands and read-only operations
|
|
2794
|
-
- Use **ast_grep_search** for structural code patterns (functions, classes, hooks)
|
|
2795
|
-
- Use **lsp_goto_definition** to trace imports and find source definitions
|
|
2796
|
-
- Use **lsp_find_references** to find all usages of a symbol
|
|
2797
|
-
|
|
2798
|
-
### Bash Usage:
|
|
2799
|
-
**ALLOWED** (read-only):
|
|
2800
|
-
- \`git log\`, \`git blame\`, \`git show\`, \`git diff\`
|
|
2801
|
-
- \`git branch\`, \`git tag\`, \`git remote\`
|
|
2802
|
-
- \`git log -S\`, \`git log --grep\`
|
|
2803
|
-
- \`ls\`, \`find\` (for directory exploration)
|
|
2804
|
-
|
|
2805
|
-
**FORBIDDEN** (state-changing):
|
|
2806
|
-
- \`mkdir\`, \`touch\`, \`rm\`, \`cp\`, \`mv\`
|
|
2807
|
-
- \`git add\`, \`git commit\`, \`git push\`, \`git checkout\`
|
|
2808
|
-
- \`npm install\`, \`pip install\`, or any installation
|
|
2809
|
-
|
|
2810
|
-
### Best Practices:
|
|
2811
|
-
- **ALWAYS launch 3+ tools in parallel** in your first search action
|
|
2812
|
-
- Use Git history to understand code evolution
|
|
2813
|
-
- Use \`git blame\` to understand why code is written a certain way
|
|
2814
|
-
- Use \`git log -S\` to find when specific code was added/removed
|
|
2815
|
-
- Adapt your search approach based on the thoroughness level specified by the caller
|
|
2816
|
-
- Return file paths as absolute paths in your final response
|
|
2817
|
-
- For clear communication, avoid using emojis
|
|
2818
|
-
- Communicate your final report directly as a regular message - do NOT attempt to create files
|
|
2819
|
-
|
|
2820
|
-
Complete the user's search request efficiently and report your findings clearly.`
|
|
2750
|
+
Flood with parallel calls. Trust only cross-validated results.`
|
|
2821
2751
|
};
|
|
2822
2752
|
|
|
2823
2753
|
// src/agents/frontend-ui-ux-engineer.ts
|
|
@@ -3750,6 +3680,14 @@ function getOrCreateMessageDir(sessionID) {
|
|
|
3750
3680
|
return directPath;
|
|
3751
3681
|
}
|
|
3752
3682
|
function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
3683
|
+
if (!hookContent || hookContent.trim().length === 0) {
|
|
3684
|
+
console.warn("[hook-message-injector] Attempted to inject empty hook content, skipping injection", {
|
|
3685
|
+
sessionID,
|
|
3686
|
+
hasAgent: !!originalMessage.agent,
|
|
3687
|
+
hasModel: !!(originalMessage.model?.providerID && originalMessage.model?.modelID)
|
|
3688
|
+
});
|
|
3689
|
+
return false;
|
|
3690
|
+
}
|
|
3753
3691
|
const messageDir = getOrCreateMessageDir(sessionID);
|
|
3754
3692
|
const needsFallback = !originalMessage.agent || !originalMessage.model?.providerID || !originalMessage.model?.modelID;
|
|
3755
3693
|
const fallback = needsFallback ? findNearestMessageWithFields(messageDir) : null;
|
|
@@ -4510,6 +4448,50 @@ function stripThinkingParts(messageID) {
|
|
|
4510
4448
|
}
|
|
4511
4449
|
return anyRemoved;
|
|
4512
4450
|
}
|
|
4451
|
+
function replaceEmptyTextParts(messageID, replacementText) {
|
|
4452
|
+
const partDir = join7(PART_STORAGE2, messageID);
|
|
4453
|
+
if (!existsSync5(partDir))
|
|
4454
|
+
return false;
|
|
4455
|
+
let anyReplaced = false;
|
|
4456
|
+
for (const file of readdirSync3(partDir)) {
|
|
4457
|
+
if (!file.endsWith(".json"))
|
|
4458
|
+
continue;
|
|
4459
|
+
try {
|
|
4460
|
+
const filePath = join7(partDir, file);
|
|
4461
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
4462
|
+
const part = JSON.parse(content);
|
|
4463
|
+
if (part.type === "text") {
|
|
4464
|
+
const textPart = part;
|
|
4465
|
+
if (!textPart.text?.trim()) {
|
|
4466
|
+
textPart.text = replacementText;
|
|
4467
|
+
textPart.synthetic = true;
|
|
4468
|
+
writeFileSync2(filePath, JSON.stringify(textPart, null, 2));
|
|
4469
|
+
anyReplaced = true;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
} catch {
|
|
4473
|
+
continue;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
return anyReplaced;
|
|
4477
|
+
}
|
|
4478
|
+
function findMessagesWithEmptyTextParts(sessionID) {
|
|
4479
|
+
const messages = readMessages(sessionID);
|
|
4480
|
+
const result = [];
|
|
4481
|
+
for (const msg of messages) {
|
|
4482
|
+
const parts = readParts(msg.id);
|
|
4483
|
+
const hasEmptyTextPart = parts.some((p) => {
|
|
4484
|
+
if (p.type !== "text")
|
|
4485
|
+
return false;
|
|
4486
|
+
const textPart = p;
|
|
4487
|
+
return !textPart.text?.trim();
|
|
4488
|
+
});
|
|
4489
|
+
if (hasEmptyTextPart) {
|
|
4490
|
+
result.push(msg.id);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
return result;
|
|
4494
|
+
}
|
|
4513
4495
|
function findMessageByIndexNeedingThinking(sessionID, targetIndex) {
|
|
4514
4496
|
const messages = readMessages(sessionID);
|
|
4515
4497
|
if (targetIndex < 0 || targetIndex >= messages.length)
|
|
@@ -4647,24 +4629,43 @@ var PLACEHOLDER_TEXT = "[user interrupted]";
|
|
|
4647
4629
|
async function recoverEmptyContentMessage(_client, sessionID, failedAssistantMsg, _directory, error) {
|
|
4648
4630
|
const targetIndex = extractMessageIndex(error);
|
|
4649
4631
|
const failedID = failedAssistantMsg.info?.id;
|
|
4632
|
+
let anySuccess = false;
|
|
4633
|
+
const messagesWithEmptyText = findMessagesWithEmptyTextParts(sessionID);
|
|
4634
|
+
for (const messageID of messagesWithEmptyText) {
|
|
4635
|
+
if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
|
|
4636
|
+
anySuccess = true;
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4650
4639
|
const thinkingOnlyIDs = findMessagesWithThinkingOnly(sessionID);
|
|
4651
4640
|
for (const messageID of thinkingOnlyIDs) {
|
|
4652
|
-
injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)
|
|
4641
|
+
if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
|
|
4642
|
+
anySuccess = true;
|
|
4643
|
+
}
|
|
4653
4644
|
}
|
|
4654
4645
|
if (targetIndex !== null) {
|
|
4655
4646
|
const targetMessageID = findEmptyMessageByIndex(sessionID, targetIndex);
|
|
4656
4647
|
if (targetMessageID) {
|
|
4657
|
-
|
|
4648
|
+
if (replaceEmptyTextParts(targetMessageID, PLACEHOLDER_TEXT)) {
|
|
4649
|
+
return true;
|
|
4650
|
+
}
|
|
4651
|
+
if (injectTextPart(sessionID, targetMessageID, PLACEHOLDER_TEXT)) {
|
|
4652
|
+
return true;
|
|
4653
|
+
}
|
|
4658
4654
|
}
|
|
4659
4655
|
}
|
|
4660
4656
|
if (failedID) {
|
|
4657
|
+
if (replaceEmptyTextParts(failedID, PLACEHOLDER_TEXT)) {
|
|
4658
|
+
return true;
|
|
4659
|
+
}
|
|
4661
4660
|
if (injectTextPart(sessionID, failedID, PLACEHOLDER_TEXT)) {
|
|
4662
4661
|
return true;
|
|
4663
4662
|
}
|
|
4664
4663
|
}
|
|
4665
4664
|
const emptyMessageIDs = findEmptyMessages(sessionID);
|
|
4666
|
-
let anySuccess = thinkingOnlyIDs.length > 0;
|
|
4667
4665
|
for (const messageID of emptyMessageIDs) {
|
|
4666
|
+
if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
|
|
4667
|
+
anySuccess = true;
|
|
4668
|
+
}
|
|
4668
4669
|
if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
|
|
4669
4670
|
anySuccess = true;
|
|
4670
4671
|
}
|
|
@@ -5615,17 +5616,104 @@ var FALLBACK_CONFIG = {
|
|
|
5615
5616
|
maxRevertAttempts: 3,
|
|
5616
5617
|
minMessagesRequired: 2
|
|
5617
5618
|
};
|
|
5619
|
+
var TRUNCATE_CONFIG = {
|
|
5620
|
+
maxTruncateAttempts: 10,
|
|
5621
|
+
minOutputSizeToTruncate: 1000
|
|
5622
|
+
};
|
|
5618
5623
|
|
|
5619
|
-
// src/hooks/anthropic-auto-compact/
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5624
|
+
// src/hooks/anthropic-auto-compact/storage.ts
|
|
5625
|
+
import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
5626
|
+
import { join as join17 } from "path";
|
|
5627
|
+
var OPENCODE_STORAGE5 = join17(xdgData2 ?? "", "opencode", "storage");
|
|
5628
|
+
var MESSAGE_STORAGE3 = join17(OPENCODE_STORAGE5, "message");
|
|
5629
|
+
var PART_STORAGE3 = join17(OPENCODE_STORAGE5, "part");
|
|
5630
|
+
var TRUNCATION_MESSAGE = "[TOOL RESULT TRUNCATED - Context limit exceeded. Original output was too large and has been truncated to recover the session. Please re-run this tool if you need the full output.]";
|
|
5631
|
+
function getMessageDir3(sessionID) {
|
|
5632
|
+
if (!existsSync13(MESSAGE_STORAGE3))
|
|
5633
|
+
return "";
|
|
5634
|
+
const directPath = join17(MESSAGE_STORAGE3, sessionID);
|
|
5635
|
+
if (existsSync13(directPath)) {
|
|
5636
|
+
return directPath;
|
|
5637
|
+
}
|
|
5638
|
+
for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
|
|
5639
|
+
const sessionPath = join17(MESSAGE_STORAGE3, dir, sessionID);
|
|
5640
|
+
if (existsSync13(sessionPath)) {
|
|
5641
|
+
return sessionPath;
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
return "";
|
|
5623
5645
|
}
|
|
5624
|
-
function
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5646
|
+
function getMessageIds(sessionID) {
|
|
5647
|
+
const messageDir = getMessageDir3(sessionID);
|
|
5648
|
+
if (!messageDir || !existsSync13(messageDir))
|
|
5649
|
+
return [];
|
|
5650
|
+
const messageIds = [];
|
|
5651
|
+
for (const file of readdirSync4(messageDir)) {
|
|
5652
|
+
if (!file.endsWith(".json"))
|
|
5653
|
+
continue;
|
|
5654
|
+
const messageId = file.replace(".json", "");
|
|
5655
|
+
messageIds.push(messageId);
|
|
5656
|
+
}
|
|
5657
|
+
return messageIds;
|
|
5628
5658
|
}
|
|
5659
|
+
function findToolResultsBySize(sessionID) {
|
|
5660
|
+
const messageIds = getMessageIds(sessionID);
|
|
5661
|
+
const results = [];
|
|
5662
|
+
for (const messageID of messageIds) {
|
|
5663
|
+
const partDir = join17(PART_STORAGE3, messageID);
|
|
5664
|
+
if (!existsSync13(partDir))
|
|
5665
|
+
continue;
|
|
5666
|
+
for (const file of readdirSync4(partDir)) {
|
|
5667
|
+
if (!file.endsWith(".json"))
|
|
5668
|
+
continue;
|
|
5669
|
+
try {
|
|
5670
|
+
const partPath = join17(partDir, file);
|
|
5671
|
+
const content = readFileSync8(partPath, "utf-8");
|
|
5672
|
+
const part = JSON.parse(content);
|
|
5673
|
+
if (part.type === "tool" && part.state?.output && !part.truncated) {
|
|
5674
|
+
results.push({
|
|
5675
|
+
partPath,
|
|
5676
|
+
partId: part.id,
|
|
5677
|
+
messageID,
|
|
5678
|
+
toolName: part.tool,
|
|
5679
|
+
outputSize: part.state.output.length
|
|
5680
|
+
});
|
|
5681
|
+
}
|
|
5682
|
+
} catch {
|
|
5683
|
+
continue;
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
return results.sort((a, b) => b.outputSize - a.outputSize);
|
|
5688
|
+
}
|
|
5689
|
+
function findLargestToolResult(sessionID) {
|
|
5690
|
+
const results = findToolResultsBySize(sessionID);
|
|
5691
|
+
return results.length > 0 ? results[0] : null;
|
|
5692
|
+
}
|
|
5693
|
+
function truncateToolResult(partPath) {
|
|
5694
|
+
try {
|
|
5695
|
+
const content = readFileSync8(partPath, "utf-8");
|
|
5696
|
+
const part = JSON.parse(content);
|
|
5697
|
+
if (!part.state?.output) {
|
|
5698
|
+
return { success: false };
|
|
5699
|
+
}
|
|
5700
|
+
const originalSize = part.state.output.length;
|
|
5701
|
+
const toolName = part.tool;
|
|
5702
|
+
part.truncated = true;
|
|
5703
|
+
part.originalSize = originalSize;
|
|
5704
|
+
part.state.output = TRUNCATION_MESSAGE;
|
|
5705
|
+
if (!part.state.time) {
|
|
5706
|
+
part.state.time = { start: Date.now() };
|
|
5707
|
+
}
|
|
5708
|
+
part.state.time.compacted = Date.now();
|
|
5709
|
+
writeFileSync5(partPath, JSON.stringify(part, null, 2));
|
|
5710
|
+
return { success: true, toolName, originalSize };
|
|
5711
|
+
} catch {
|
|
5712
|
+
return { success: false };
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
// src/hooks/anthropic-auto-compact/executor.ts
|
|
5629
5717
|
function getOrCreateRetryState(autoCompactState, sessionID) {
|
|
5630
5718
|
let state = autoCompactState.retryStateBySession.get(sessionID);
|
|
5631
5719
|
if (!state) {
|
|
@@ -5642,6 +5730,14 @@ function getOrCreateFallbackState(autoCompactState, sessionID) {
|
|
|
5642
5730
|
}
|
|
5643
5731
|
return state;
|
|
5644
5732
|
}
|
|
5733
|
+
function getOrCreateTruncateState(autoCompactState, sessionID) {
|
|
5734
|
+
let state = autoCompactState.truncateStateBySession.get(sessionID);
|
|
5735
|
+
if (!state) {
|
|
5736
|
+
state = { truncateAttempt: 0 };
|
|
5737
|
+
autoCompactState.truncateStateBySession.set(sessionID, state);
|
|
5738
|
+
}
|
|
5739
|
+
return state;
|
|
5740
|
+
}
|
|
5645
5741
|
async function getLastMessagePair(sessionID, client, directory) {
|
|
5646
5742
|
try {
|
|
5647
5743
|
const resp = await client.session.messages({
|
|
@@ -5679,46 +5775,12 @@ async function getLastMessagePair(sessionID, client, directory) {
|
|
|
5679
5775
|
return null;
|
|
5680
5776
|
}
|
|
5681
5777
|
}
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
if (!pair) {
|
|
5689
|
-
return false;
|
|
5690
|
-
}
|
|
5691
|
-
await client.tui.showToast({
|
|
5692
|
-
body: {
|
|
5693
|
-
title: "\u26A0\uFE0F Emergency Recovery",
|
|
5694
|
-
message: `Context too large. Removing last message pair to recover session...`,
|
|
5695
|
-
variant: "warning",
|
|
5696
|
-
duration: 4000
|
|
5697
|
-
}
|
|
5698
|
-
}).catch(() => {});
|
|
5699
|
-
try {
|
|
5700
|
-
if (pair.assistantMessageID) {
|
|
5701
|
-
await client.session.revert({
|
|
5702
|
-
path: { id: sessionID },
|
|
5703
|
-
body: { messageID: pair.assistantMessageID },
|
|
5704
|
-
query: { directory }
|
|
5705
|
-
});
|
|
5706
|
-
}
|
|
5707
|
-
await client.session.revert({
|
|
5708
|
-
path: { id: sessionID },
|
|
5709
|
-
body: { messageID: pair.userMessageID },
|
|
5710
|
-
query: { directory }
|
|
5711
|
-
});
|
|
5712
|
-
fallbackState.revertAttempt++;
|
|
5713
|
-
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
5714
|
-
const retryState = autoCompactState.retryStateBySession.get(sessionID);
|
|
5715
|
-
if (retryState) {
|
|
5716
|
-
retryState.attempt = 0;
|
|
5717
|
-
}
|
|
5718
|
-
return true;
|
|
5719
|
-
} catch {
|
|
5720
|
-
return false;
|
|
5721
|
-
}
|
|
5778
|
+
function formatBytes(bytes) {
|
|
5779
|
+
if (bytes < 1024)
|
|
5780
|
+
return `${bytes}B`;
|
|
5781
|
+
if (bytes < 1024 * 1024)
|
|
5782
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
5783
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
5722
5784
|
}
|
|
5723
5785
|
async function getLastAssistant(sessionID, client, directory) {
|
|
5724
5786
|
try {
|
|
@@ -5747,71 +5809,133 @@ function clearSessionState(autoCompactState, sessionID) {
|
|
|
5747
5809
|
autoCompactState.errorDataBySession.delete(sessionID);
|
|
5748
5810
|
autoCompactState.retryStateBySession.delete(sessionID);
|
|
5749
5811
|
autoCompactState.fallbackStateBySession.delete(sessionID);
|
|
5812
|
+
autoCompactState.truncateStateBySession.delete(sessionID);
|
|
5813
|
+
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5750
5814
|
}
|
|
5751
5815
|
async function executeCompact(sessionID, msg, autoCompactState, client, directory) {
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5816
|
+
if (autoCompactState.compactionInProgress.has(sessionID)) {
|
|
5817
|
+
return;
|
|
5818
|
+
}
|
|
5819
|
+
autoCompactState.compactionInProgress.add(sessionID);
|
|
5820
|
+
const truncateState = getOrCreateTruncateState(autoCompactState, sessionID);
|
|
5821
|
+
if (truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
|
|
5822
|
+
const largest = findLargestToolResult(sessionID);
|
|
5823
|
+
if (largest && largest.outputSize >= TRUNCATE_CONFIG.minOutputSizeToTruncate) {
|
|
5824
|
+
const result = truncateToolResult(largest.partPath);
|
|
5825
|
+
if (result.success) {
|
|
5826
|
+
truncateState.truncateAttempt++;
|
|
5827
|
+
truncateState.lastTruncatedPartId = largest.partId;
|
|
5758
5828
|
await client.tui.showToast({
|
|
5759
5829
|
body: {
|
|
5760
|
-
title: "
|
|
5761
|
-
message:
|
|
5762
|
-
variant: "
|
|
5830
|
+
title: "Truncating Large Output",
|
|
5831
|
+
message: `Truncated ${result.toolName} (${formatBytes(result.originalSize ?? 0)}). Retrying...`,
|
|
5832
|
+
variant: "warning",
|
|
5763
5833
|
duration: 3000
|
|
5764
5834
|
}
|
|
5765
5835
|
}).catch(() => {});
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5836
|
+
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5837
|
+
setTimeout(async () => {
|
|
5838
|
+
try {
|
|
5839
|
+
await client.session.prompt_async({
|
|
5840
|
+
path: { sessionID },
|
|
5841
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5842
|
+
query: { directory }
|
|
5843
|
+
});
|
|
5844
|
+
} catch {}
|
|
5845
|
+
}, 500);
|
|
5769
5846
|
return;
|
|
5770
5847
|
}
|
|
5771
5848
|
}
|
|
5772
|
-
clearSessionState(autoCompactState, sessionID);
|
|
5773
|
-
await client.tui.showToast({
|
|
5774
|
-
body: {
|
|
5775
|
-
title: "Auto Compact Failed",
|
|
5776
|
-
message: `Failed after ${RETRY_CONFIG.maxAttempts} retries and ${FALLBACK_CONFIG.maxRevertAttempts} message removals. Please start a new session.`,
|
|
5777
|
-
variant: "error",
|
|
5778
|
-
duration: 5000
|
|
5779
|
-
}
|
|
5780
|
-
}).catch(() => {});
|
|
5781
|
-
return;
|
|
5782
5849
|
}
|
|
5783
|
-
retryState
|
|
5784
|
-
retryState.
|
|
5785
|
-
|
|
5850
|
+
const retryState = getOrCreateRetryState(autoCompactState, sessionID);
|
|
5851
|
+
if (retryState.attempt < RETRY_CONFIG.maxAttempts) {
|
|
5852
|
+
retryState.attempt++;
|
|
5853
|
+
retryState.lastAttemptTime = Date.now();
|
|
5786
5854
|
const providerID = msg.providerID;
|
|
5787
5855
|
const modelID = msg.modelID;
|
|
5788
5856
|
if (providerID && modelID) {
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5857
|
+
try {
|
|
5858
|
+
await client.tui.showToast({
|
|
5859
|
+
body: {
|
|
5860
|
+
title: "Auto Compact",
|
|
5861
|
+
message: `Summarizing session (attempt ${retryState.attempt}/${RETRY_CONFIG.maxAttempts})...`,
|
|
5862
|
+
variant: "warning",
|
|
5863
|
+
duration: 3000
|
|
5864
|
+
}
|
|
5865
|
+
}).catch(() => {});
|
|
5866
|
+
await client.session.summarize({
|
|
5867
|
+
path: { id: sessionID },
|
|
5868
|
+
body: { providerID, modelID },
|
|
5869
|
+
query: { directory }
|
|
5870
|
+
});
|
|
5871
|
+
clearSessionState(autoCompactState, sessionID);
|
|
5872
|
+
setTimeout(async () => {
|
|
5873
|
+
try {
|
|
5874
|
+
await client.session.prompt_async({
|
|
5875
|
+
path: { sessionID },
|
|
5876
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5877
|
+
query: { directory }
|
|
5878
|
+
});
|
|
5879
|
+
} catch {}
|
|
5880
|
+
}, 500);
|
|
5881
|
+
return;
|
|
5882
|
+
} catch {
|
|
5883
|
+
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5884
|
+
const delay = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
|
|
5885
|
+
const cappedDelay = Math.min(delay, RETRY_CONFIG.maxDelayMs);
|
|
5886
|
+
setTimeout(() => {
|
|
5887
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory);
|
|
5888
|
+
}, cappedDelay);
|
|
5889
|
+
return;
|
|
5809
5890
|
}
|
|
5810
|
-
}
|
|
5811
|
-
setTimeout(() => {
|
|
5812
|
-
executeCompact(sessionID, msg, autoCompactState, client, directory);
|
|
5813
|
-
}, delay);
|
|
5891
|
+
}
|
|
5814
5892
|
}
|
|
5893
|
+
const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
|
|
5894
|
+
if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
|
|
5895
|
+
const pair = await getLastMessagePair(sessionID, client, directory);
|
|
5896
|
+
if (pair) {
|
|
5897
|
+
try {
|
|
5898
|
+
await client.tui.showToast({
|
|
5899
|
+
body: {
|
|
5900
|
+
title: "Emergency Recovery",
|
|
5901
|
+
message: "Removing last message pair...",
|
|
5902
|
+
variant: "warning",
|
|
5903
|
+
duration: 3000
|
|
5904
|
+
}
|
|
5905
|
+
}).catch(() => {});
|
|
5906
|
+
if (pair.assistantMessageID) {
|
|
5907
|
+
await client.session.revert({
|
|
5908
|
+
path: { id: sessionID },
|
|
5909
|
+
body: { messageID: pair.assistantMessageID },
|
|
5910
|
+
query: { directory }
|
|
5911
|
+
});
|
|
5912
|
+
}
|
|
5913
|
+
await client.session.revert({
|
|
5914
|
+
path: { id: sessionID },
|
|
5915
|
+
body: { messageID: pair.userMessageID },
|
|
5916
|
+
query: { directory }
|
|
5917
|
+
});
|
|
5918
|
+
fallbackState.revertAttempt++;
|
|
5919
|
+
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
5920
|
+
retryState.attempt = 0;
|
|
5921
|
+
truncateState.truncateAttempt = 0;
|
|
5922
|
+
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5923
|
+
setTimeout(() => {
|
|
5924
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory);
|
|
5925
|
+
}, 1000);
|
|
5926
|
+
return;
|
|
5927
|
+
} catch {}
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
clearSessionState(autoCompactState, sessionID);
|
|
5931
|
+
await client.tui.showToast({
|
|
5932
|
+
body: {
|
|
5933
|
+
title: "Auto Compact Failed",
|
|
5934
|
+
message: "All recovery attempts failed. Please start a new session.",
|
|
5935
|
+
variant: "error",
|
|
5936
|
+
duration: 5000
|
|
5937
|
+
}
|
|
5938
|
+
}).catch(() => {});
|
|
5815
5939
|
}
|
|
5816
5940
|
|
|
5817
5941
|
// src/hooks/anthropic-auto-compact/index.ts
|
|
@@ -5820,7 +5944,9 @@ function createAutoCompactState() {
|
|
|
5820
5944
|
pendingCompact: new Set,
|
|
5821
5945
|
errorDataBySession: new Map,
|
|
5822
5946
|
retryStateBySession: new Map,
|
|
5823
|
-
fallbackStateBySession: new Map
|
|
5947
|
+
fallbackStateBySession: new Map,
|
|
5948
|
+
truncateStateBySession: new Map,
|
|
5949
|
+
compactionInProgress: new Set
|
|
5824
5950
|
};
|
|
5825
5951
|
}
|
|
5826
5952
|
function createAnthropicAutoCompactHook(ctx) {
|
|
@@ -5834,6 +5960,8 @@ function createAnthropicAutoCompactHook(ctx) {
|
|
|
5834
5960
|
autoCompactState.errorDataBySession.delete(sessionInfo.id);
|
|
5835
5961
|
autoCompactState.retryStateBySession.delete(sessionInfo.id);
|
|
5836
5962
|
autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
|
|
5963
|
+
autoCompactState.truncateStateBySession.delete(sessionInfo.id);
|
|
5964
|
+
autoCompactState.compactionInProgress.delete(sessionInfo.id);
|
|
5837
5965
|
}
|
|
5838
5966
|
return;
|
|
5839
5967
|
}
|
|
@@ -5845,6 +5973,25 @@ function createAnthropicAutoCompactHook(ctx) {
|
|
|
5845
5973
|
if (parsed) {
|
|
5846
5974
|
autoCompactState.pendingCompact.add(sessionID);
|
|
5847
5975
|
autoCompactState.errorDataBySession.set(sessionID, parsed);
|
|
5976
|
+
if (autoCompactState.compactionInProgress.has(sessionID)) {
|
|
5977
|
+
return;
|
|
5978
|
+
}
|
|
5979
|
+
const lastAssistant = await getLastAssistant(sessionID, ctx.client, ctx.directory);
|
|
5980
|
+
const providerID = parsed.providerID ?? lastAssistant?.providerID;
|
|
5981
|
+
const modelID = parsed.modelID ?? lastAssistant?.modelID;
|
|
5982
|
+
if (providerID && modelID) {
|
|
5983
|
+
await ctx.client.tui.showToast({
|
|
5984
|
+
body: {
|
|
5985
|
+
title: "Context Limit Hit",
|
|
5986
|
+
message: "Truncating large tool outputs and recovering...",
|
|
5987
|
+
variant: "warning",
|
|
5988
|
+
duration: 3000
|
|
5989
|
+
}
|
|
5990
|
+
}).catch(() => {});
|
|
5991
|
+
setTimeout(() => {
|
|
5992
|
+
executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory);
|
|
5993
|
+
}, 300);
|
|
5994
|
+
}
|
|
5848
5995
|
}
|
|
5849
5996
|
return;
|
|
5850
5997
|
}
|
|
@@ -6177,8 +6324,8 @@ function createThinkModeHook() {
|
|
|
6177
6324
|
}
|
|
6178
6325
|
// src/hooks/claude-code-hooks/config.ts
|
|
6179
6326
|
import { homedir as homedir4 } from "os";
|
|
6180
|
-
import { join as
|
|
6181
|
-
import { existsSync as
|
|
6327
|
+
import { join as join18 } from "path";
|
|
6328
|
+
import { existsSync as existsSync14 } from "fs";
|
|
6182
6329
|
function normalizeHookMatcher(raw) {
|
|
6183
6330
|
return {
|
|
6184
6331
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -6203,11 +6350,11 @@ function normalizeHooksConfig(raw) {
|
|
|
6203
6350
|
function getClaudeSettingsPaths(customPath) {
|
|
6204
6351
|
const home = homedir4();
|
|
6205
6352
|
const paths = [
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6353
|
+
join18(home, ".claude", "settings.json"),
|
|
6354
|
+
join18(process.cwd(), ".claude", "settings.json"),
|
|
6355
|
+
join18(process.cwd(), ".claude", "settings.local.json")
|
|
6209
6356
|
];
|
|
6210
|
-
if (customPath &&
|
|
6357
|
+
if (customPath && existsSync14(customPath)) {
|
|
6211
6358
|
paths.unshift(customPath);
|
|
6212
6359
|
}
|
|
6213
6360
|
return paths;
|
|
@@ -6231,7 +6378,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6231
6378
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
6232
6379
|
let mergedConfig = {};
|
|
6233
6380
|
for (const settingsPath of paths) {
|
|
6234
|
-
if (
|
|
6381
|
+
if (existsSync14(settingsPath)) {
|
|
6235
6382
|
try {
|
|
6236
6383
|
const content = await Bun.file(settingsPath).text();
|
|
6237
6384
|
const settings = JSON.parse(content);
|
|
@@ -6248,15 +6395,15 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6248
6395
|
}
|
|
6249
6396
|
|
|
6250
6397
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6251
|
-
import { existsSync as
|
|
6398
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6252
6399
|
import { homedir as homedir5 } from "os";
|
|
6253
|
-
import { join as
|
|
6254
|
-
var USER_CONFIG_PATH =
|
|
6400
|
+
import { join as join19 } from "path";
|
|
6401
|
+
var USER_CONFIG_PATH = join19(homedir5(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6255
6402
|
function getProjectConfigPath() {
|
|
6256
|
-
return
|
|
6403
|
+
return join19(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6257
6404
|
}
|
|
6258
6405
|
async function loadConfigFromPath(path3) {
|
|
6259
|
-
if (!
|
|
6406
|
+
if (!existsSync15(path3)) {
|
|
6260
6407
|
return null;
|
|
6261
6408
|
}
|
|
6262
6409
|
try {
|
|
@@ -6435,16 +6582,16 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6435
6582
|
}
|
|
6436
6583
|
|
|
6437
6584
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6438
|
-
import { join as
|
|
6439
|
-
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as
|
|
6585
|
+
import { join as join20 } from "path";
|
|
6586
|
+
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync16, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
6440
6587
|
import { homedir as homedir6, tmpdir as tmpdir5 } from "os";
|
|
6441
6588
|
import { randomUUID } from "crypto";
|
|
6442
|
-
var TRANSCRIPT_DIR =
|
|
6589
|
+
var TRANSCRIPT_DIR = join20(homedir6(), ".claude", "transcripts");
|
|
6443
6590
|
function getTranscriptPath(sessionId) {
|
|
6444
|
-
return
|
|
6591
|
+
return join20(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6445
6592
|
}
|
|
6446
6593
|
function ensureTranscriptDir() {
|
|
6447
|
-
if (!
|
|
6594
|
+
if (!existsSync16(TRANSCRIPT_DIR)) {
|
|
6448
6595
|
mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
|
|
6449
6596
|
}
|
|
6450
6597
|
}
|
|
@@ -6531,8 +6678,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6531
6678
|
}
|
|
6532
6679
|
};
|
|
6533
6680
|
entries.push(JSON.stringify(currentEntry));
|
|
6534
|
-
const tempPath =
|
|
6535
|
-
|
|
6681
|
+
const tempPath = join20(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6682
|
+
writeFileSync6(tempPath, entries.join(`
|
|
6536
6683
|
`) + `
|
|
6537
6684
|
`);
|
|
6538
6685
|
return tempPath;
|
|
@@ -6551,8 +6698,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6551
6698
|
]
|
|
6552
6699
|
}
|
|
6553
6700
|
};
|
|
6554
|
-
const tempPath =
|
|
6555
|
-
|
|
6701
|
+
const tempPath = join20(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6702
|
+
writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
|
|
6556
6703
|
`);
|
|
6557
6704
|
return tempPath;
|
|
6558
6705
|
} catch {
|
|
@@ -6763,11 +6910,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
6763
6910
|
}
|
|
6764
6911
|
|
|
6765
6912
|
// src/hooks/claude-code-hooks/todo.ts
|
|
6766
|
-
import { join as
|
|
6913
|
+
import { join as join21 } from "path";
|
|
6767
6914
|
import { homedir as homedir7 } from "os";
|
|
6768
|
-
var TODO_DIR =
|
|
6915
|
+
var TODO_DIR = join21(homedir7(), ".claude", "todos");
|
|
6769
6916
|
function getTodoPath(sessionId) {
|
|
6770
|
-
return
|
|
6917
|
+
return join21(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
6771
6918
|
}
|
|
6772
6919
|
|
|
6773
6920
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -6919,6 +7066,7 @@ function createClaudeCodeHooksHook(ctx, config = {}) {
|
|
|
6919
7066
|
const hookContent = result.messages.join(`
|
|
6920
7067
|
|
|
6921
7068
|
`);
|
|
7069
|
+
log(`[claude-code-hooks] Injecting ${result.messages.length} hook messages`, { sessionID: input.sessionID, contentLength: hookContent.length });
|
|
6922
7070
|
const message = output.message;
|
|
6923
7071
|
const success = injectHookMessage(input.sessionID, hookContent, {
|
|
6924
7072
|
agent: message.agent,
|
|
@@ -7098,23 +7246,23 @@ ${result.message}`;
|
|
|
7098
7246
|
};
|
|
7099
7247
|
}
|
|
7100
7248
|
// src/hooks/rules-injector/index.ts
|
|
7101
|
-
import { readFileSync as
|
|
7249
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
7102
7250
|
import { homedir as homedir8 } from "os";
|
|
7103
7251
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
7104
7252
|
|
|
7105
7253
|
// src/hooks/rules-injector/finder.ts
|
|
7106
7254
|
import {
|
|
7107
|
-
existsSync as
|
|
7108
|
-
readdirSync as
|
|
7255
|
+
existsSync as existsSync17,
|
|
7256
|
+
readdirSync as readdirSync5,
|
|
7109
7257
|
realpathSync,
|
|
7110
7258
|
statSync as statSync2
|
|
7111
7259
|
} from "fs";
|
|
7112
|
-
import { dirname as dirname4, join as
|
|
7260
|
+
import { dirname as dirname4, join as join23, relative } from "path";
|
|
7113
7261
|
|
|
7114
7262
|
// src/hooks/rules-injector/constants.ts
|
|
7115
|
-
import { join as
|
|
7116
|
-
var
|
|
7117
|
-
var RULES_INJECTOR_STORAGE =
|
|
7263
|
+
import { join as join22 } from "path";
|
|
7264
|
+
var OPENCODE_STORAGE6 = join22(xdgData2 ?? "", "opencode", "storage");
|
|
7265
|
+
var RULES_INJECTOR_STORAGE = join22(OPENCODE_STORAGE6, "rules-injector");
|
|
7118
7266
|
var PROJECT_MARKERS = [
|
|
7119
7267
|
".git",
|
|
7120
7268
|
"pyproject.toml",
|
|
@@ -7141,8 +7289,8 @@ function findProjectRoot(startPath) {
|
|
|
7141
7289
|
}
|
|
7142
7290
|
while (true) {
|
|
7143
7291
|
for (const marker of PROJECT_MARKERS) {
|
|
7144
|
-
const markerPath =
|
|
7145
|
-
if (
|
|
7292
|
+
const markerPath = join23(current, marker);
|
|
7293
|
+
if (existsSync17(markerPath)) {
|
|
7146
7294
|
return current;
|
|
7147
7295
|
}
|
|
7148
7296
|
}
|
|
@@ -7154,12 +7302,12 @@ function findProjectRoot(startPath) {
|
|
|
7154
7302
|
}
|
|
7155
7303
|
}
|
|
7156
7304
|
function findRuleFilesRecursive(dir, results) {
|
|
7157
|
-
if (!
|
|
7305
|
+
if (!existsSync17(dir))
|
|
7158
7306
|
return;
|
|
7159
7307
|
try {
|
|
7160
|
-
const entries =
|
|
7308
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
7161
7309
|
for (const entry of entries) {
|
|
7162
|
-
const fullPath =
|
|
7310
|
+
const fullPath = join23(dir, entry.name);
|
|
7163
7311
|
if (entry.isDirectory()) {
|
|
7164
7312
|
findRuleFilesRecursive(fullPath, results);
|
|
7165
7313
|
} else if (entry.isFile()) {
|
|
@@ -7185,7 +7333,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7185
7333
|
let distance = 0;
|
|
7186
7334
|
while (true) {
|
|
7187
7335
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7188
|
-
const ruleDir =
|
|
7336
|
+
const ruleDir = join23(currentDir, parent, subdir);
|
|
7189
7337
|
const files = [];
|
|
7190
7338
|
findRuleFilesRecursive(ruleDir, files);
|
|
7191
7339
|
for (const filePath of files) {
|
|
@@ -7209,7 +7357,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7209
7357
|
currentDir = parentDir;
|
|
7210
7358
|
distance++;
|
|
7211
7359
|
}
|
|
7212
|
-
const userRuleDir =
|
|
7360
|
+
const userRuleDir = join23(homeDir, USER_RULE_DIR);
|
|
7213
7361
|
const userFiles = [];
|
|
7214
7362
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7215
7363
|
for (const filePath of userFiles) {
|
|
@@ -7398,22 +7546,22 @@ function mergeGlobs(existing, newValue) {
|
|
|
7398
7546
|
|
|
7399
7547
|
// src/hooks/rules-injector/storage.ts
|
|
7400
7548
|
import {
|
|
7401
|
-
existsSync as
|
|
7549
|
+
existsSync as existsSync18,
|
|
7402
7550
|
mkdirSync as mkdirSync7,
|
|
7403
|
-
readFileSync as
|
|
7404
|
-
writeFileSync as
|
|
7551
|
+
readFileSync as readFileSync9,
|
|
7552
|
+
writeFileSync as writeFileSync7,
|
|
7405
7553
|
unlinkSync as unlinkSync6
|
|
7406
7554
|
} from "fs";
|
|
7407
|
-
import { join as
|
|
7555
|
+
import { join as join24 } from "path";
|
|
7408
7556
|
function getStoragePath3(sessionID) {
|
|
7409
|
-
return
|
|
7557
|
+
return join24(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
7410
7558
|
}
|
|
7411
7559
|
function loadInjectedRules(sessionID) {
|
|
7412
7560
|
const filePath = getStoragePath3(sessionID);
|
|
7413
|
-
if (!
|
|
7561
|
+
if (!existsSync18(filePath))
|
|
7414
7562
|
return { contentHashes: new Set, realPaths: new Set };
|
|
7415
7563
|
try {
|
|
7416
|
-
const content =
|
|
7564
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
7417
7565
|
const data = JSON.parse(content);
|
|
7418
7566
|
return {
|
|
7419
7567
|
contentHashes: new Set(data.injectedHashes),
|
|
@@ -7424,7 +7572,7 @@ function loadInjectedRules(sessionID) {
|
|
|
7424
7572
|
}
|
|
7425
7573
|
}
|
|
7426
7574
|
function saveInjectedRules(sessionID, data) {
|
|
7427
|
-
if (!
|
|
7575
|
+
if (!existsSync18(RULES_INJECTOR_STORAGE)) {
|
|
7428
7576
|
mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
7429
7577
|
}
|
|
7430
7578
|
const storageData = {
|
|
@@ -7433,11 +7581,11 @@ function saveInjectedRules(sessionID, data) {
|
|
|
7433
7581
|
injectedRealPaths: [...data.realPaths],
|
|
7434
7582
|
updatedAt: Date.now()
|
|
7435
7583
|
};
|
|
7436
|
-
|
|
7584
|
+
writeFileSync7(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
|
|
7437
7585
|
}
|
|
7438
7586
|
function clearInjectedRules(sessionID) {
|
|
7439
7587
|
const filePath = getStoragePath3(sessionID);
|
|
7440
|
-
if (
|
|
7588
|
+
if (existsSync18(filePath)) {
|
|
7441
7589
|
unlinkSync6(filePath);
|
|
7442
7590
|
}
|
|
7443
7591
|
}
|
|
@@ -7474,7 +7622,7 @@ function createRulesInjectorHook(ctx) {
|
|
|
7474
7622
|
if (isDuplicateByRealPath(candidate.realPath, cache2.realPaths))
|
|
7475
7623
|
continue;
|
|
7476
7624
|
try {
|
|
7477
|
-
const rawContent =
|
|
7625
|
+
const rawContent = readFileSync10(candidate.path, "utf-8");
|
|
7478
7626
|
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
|
7479
7627
|
const matchResult = shouldApplyRule(metadata, filePath, projectRoot);
|
|
7480
7628
|
if (!matchResult.applies)
|
|
@@ -7839,18 +7987,18 @@ async function showVersionToast(ctx, version) {
|
|
|
7839
7987
|
}
|
|
7840
7988
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7841
7989
|
import {
|
|
7842
|
-
existsSync as
|
|
7990
|
+
existsSync as existsSync21,
|
|
7843
7991
|
mkdirSync as mkdirSync8,
|
|
7844
|
-
readFileSync as
|
|
7845
|
-
writeFileSync as
|
|
7992
|
+
readFileSync as readFileSync13,
|
|
7993
|
+
writeFileSync as writeFileSync9,
|
|
7846
7994
|
unlinkSync as unlinkSync7
|
|
7847
7995
|
} from "fs";
|
|
7848
|
-
import { join as
|
|
7996
|
+
import { join as join29 } from "path";
|
|
7849
7997
|
|
|
7850
7998
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
7851
|
-
import { join as
|
|
7852
|
-
var
|
|
7853
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
7999
|
+
import { join as join28 } from "path";
|
|
8000
|
+
var OPENCODE_STORAGE7 = join28(xdgData2 ?? "", "opencode", "storage");
|
|
8001
|
+
var AGENT_USAGE_REMINDER_STORAGE = join28(OPENCODE_STORAGE7, "agent-usage-reminder");
|
|
7854
8002
|
var TARGET_TOOLS = new Set([
|
|
7855
8003
|
"grep",
|
|
7856
8004
|
"safe_grep",
|
|
@@ -7895,29 +8043,29 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
7895
8043
|
|
|
7896
8044
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7897
8045
|
function getStoragePath4(sessionID) {
|
|
7898
|
-
return
|
|
8046
|
+
return join29(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
7899
8047
|
}
|
|
7900
8048
|
function loadAgentUsageState(sessionID) {
|
|
7901
8049
|
const filePath = getStoragePath4(sessionID);
|
|
7902
|
-
if (!
|
|
8050
|
+
if (!existsSync21(filePath))
|
|
7903
8051
|
return null;
|
|
7904
8052
|
try {
|
|
7905
|
-
const content =
|
|
8053
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
7906
8054
|
return JSON.parse(content);
|
|
7907
8055
|
} catch {
|
|
7908
8056
|
return null;
|
|
7909
8057
|
}
|
|
7910
8058
|
}
|
|
7911
8059
|
function saveAgentUsageState(state) {
|
|
7912
|
-
if (!
|
|
8060
|
+
if (!existsSync21(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
7913
8061
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
7914
8062
|
}
|
|
7915
8063
|
const filePath = getStoragePath4(state.sessionID);
|
|
7916
|
-
|
|
8064
|
+
writeFileSync9(filePath, JSON.stringify(state, null, 2));
|
|
7917
8065
|
}
|
|
7918
8066
|
function clearAgentUsageState(sessionID) {
|
|
7919
8067
|
const filePath = getStoragePath4(sessionID);
|
|
7920
|
-
if (
|
|
8068
|
+
if (existsSync21(filePath)) {
|
|
7921
8069
|
unlinkSync7(filePath);
|
|
7922
8070
|
}
|
|
7923
8071
|
}
|
|
@@ -8081,6 +8229,7 @@ function createKeywordDetectorHook() {
|
|
|
8081
8229
|
const message = output.message;
|
|
8082
8230
|
const context = messages.join(`
|
|
8083
8231
|
`);
|
|
8232
|
+
log(`[keyword-detector] Injecting context for ${messages.length} keywords`, { sessionID: input.sessionID, contextLength: context.length });
|
|
8084
8233
|
const success = injectHookMessage(input.sessionID, context, {
|
|
8085
8234
|
agent: message.agent,
|
|
8086
8235
|
model: message.model,
|
|
@@ -8138,18 +8287,18 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
8138
8287
|
}
|
|
8139
8288
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8140
8289
|
import {
|
|
8141
|
-
existsSync as
|
|
8290
|
+
existsSync as existsSync22,
|
|
8142
8291
|
mkdirSync as mkdirSync9,
|
|
8143
|
-
readFileSync as
|
|
8144
|
-
writeFileSync as
|
|
8292
|
+
readFileSync as readFileSync14,
|
|
8293
|
+
writeFileSync as writeFileSync10,
|
|
8145
8294
|
unlinkSync as unlinkSync8
|
|
8146
8295
|
} from "fs";
|
|
8147
|
-
import { join as
|
|
8296
|
+
import { join as join31 } from "path";
|
|
8148
8297
|
|
|
8149
8298
|
// src/hooks/interactive-bash-session/constants.ts
|
|
8150
|
-
import { join as
|
|
8151
|
-
var
|
|
8152
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
8299
|
+
import { join as join30 } from "path";
|
|
8300
|
+
var OPENCODE_STORAGE8 = join30(xdgData2 ?? "", "opencode", "storage");
|
|
8301
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join30(OPENCODE_STORAGE8, "interactive-bash-session");
|
|
8153
8302
|
var OMO_SESSION_PREFIX = "omo-";
|
|
8154
8303
|
function buildSessionReminderMessage(sessions) {
|
|
8155
8304
|
if (sessions.length === 0)
|
|
@@ -8161,14 +8310,14 @@ function buildSessionReminderMessage(sessions) {
|
|
|
8161
8310
|
|
|
8162
8311
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8163
8312
|
function getStoragePath5(sessionID) {
|
|
8164
|
-
return
|
|
8313
|
+
return join31(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
8165
8314
|
}
|
|
8166
8315
|
function loadInteractiveBashSessionState(sessionID) {
|
|
8167
8316
|
const filePath = getStoragePath5(sessionID);
|
|
8168
|
-
if (!
|
|
8317
|
+
if (!existsSync22(filePath))
|
|
8169
8318
|
return null;
|
|
8170
8319
|
try {
|
|
8171
|
-
const content =
|
|
8320
|
+
const content = readFileSync14(filePath, "utf-8");
|
|
8172
8321
|
const serialized = JSON.parse(content);
|
|
8173
8322
|
return {
|
|
8174
8323
|
sessionID: serialized.sessionID,
|
|
@@ -8180,7 +8329,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
8180
8329
|
}
|
|
8181
8330
|
}
|
|
8182
8331
|
function saveInteractiveBashSessionState(state) {
|
|
8183
|
-
if (!
|
|
8332
|
+
if (!existsSync22(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
8184
8333
|
mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
8185
8334
|
}
|
|
8186
8335
|
const filePath = getStoragePath5(state.sessionID);
|
|
@@ -8189,11 +8338,11 @@ function saveInteractiveBashSessionState(state) {
|
|
|
8189
8338
|
tmuxSessions: Array.from(state.tmuxSessions),
|
|
8190
8339
|
updatedAt: state.updatedAt
|
|
8191
8340
|
};
|
|
8192
|
-
|
|
8341
|
+
writeFileSync10(filePath, JSON.stringify(serialized, null, 2));
|
|
8193
8342
|
}
|
|
8194
8343
|
function clearInteractiveBashSessionState(sessionID) {
|
|
8195
8344
|
const filePath = getStoragePath5(sessionID);
|
|
8196
|
-
if (
|
|
8345
|
+
if (existsSync22(filePath)) {
|
|
8197
8346
|
unlinkSync8(filePath);
|
|
8198
8347
|
}
|
|
8199
8348
|
}
|
|
@@ -8370,6 +8519,73 @@ function createInteractiveBashSessionHook(_ctx) {
|
|
|
8370
8519
|
event: eventHandler
|
|
8371
8520
|
};
|
|
8372
8521
|
}
|
|
8522
|
+
// src/hooks/empty-message-sanitizer/index.ts
|
|
8523
|
+
var PLACEHOLDER_TEXT2 = "[user interrupted]";
|
|
8524
|
+
function hasTextContent(part) {
|
|
8525
|
+
if (part.type === "text") {
|
|
8526
|
+
const text = part.text;
|
|
8527
|
+
return Boolean(text && text.trim().length > 0);
|
|
8528
|
+
}
|
|
8529
|
+
return false;
|
|
8530
|
+
}
|
|
8531
|
+
function isToolPart(part) {
|
|
8532
|
+
const type = part.type;
|
|
8533
|
+
return type === "tool" || type === "tool_use" || type === "tool_result";
|
|
8534
|
+
}
|
|
8535
|
+
function hasValidContent(parts) {
|
|
8536
|
+
return parts.some((part) => hasTextContent(part) || isToolPart(part));
|
|
8537
|
+
}
|
|
8538
|
+
function createEmptyMessageSanitizerHook() {
|
|
8539
|
+
return {
|
|
8540
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
8541
|
+
const { messages } = output;
|
|
8542
|
+
for (const message of messages) {
|
|
8543
|
+
if (message.info.role === "user")
|
|
8544
|
+
continue;
|
|
8545
|
+
const parts = message.parts;
|
|
8546
|
+
if (!hasValidContent(parts)) {
|
|
8547
|
+
let injected = false;
|
|
8548
|
+
for (const part of parts) {
|
|
8549
|
+
if (part.type === "text") {
|
|
8550
|
+
const textPart = part;
|
|
8551
|
+
if (!textPart.text || !textPart.text.trim()) {
|
|
8552
|
+
textPart.text = PLACEHOLDER_TEXT2;
|
|
8553
|
+
textPart.synthetic = true;
|
|
8554
|
+
injected = true;
|
|
8555
|
+
break;
|
|
8556
|
+
}
|
|
8557
|
+
}
|
|
8558
|
+
}
|
|
8559
|
+
if (!injected) {
|
|
8560
|
+
const insertIndex = parts.findIndex((p) => isToolPart(p));
|
|
8561
|
+
const newPart = {
|
|
8562
|
+
id: `synthetic_${Date.now()}`,
|
|
8563
|
+
messageID: message.info.id,
|
|
8564
|
+
sessionID: message.info.sessionID ?? "",
|
|
8565
|
+
type: "text",
|
|
8566
|
+
text: PLACEHOLDER_TEXT2,
|
|
8567
|
+
synthetic: true
|
|
8568
|
+
};
|
|
8569
|
+
if (insertIndex === -1) {
|
|
8570
|
+
parts.push(newPart);
|
|
8571
|
+
} else {
|
|
8572
|
+
parts.splice(insertIndex, 0, newPart);
|
|
8573
|
+
}
|
|
8574
|
+
}
|
|
8575
|
+
}
|
|
8576
|
+
for (const part of parts) {
|
|
8577
|
+
if (part.type === "text") {
|
|
8578
|
+
const textPart = part;
|
|
8579
|
+
if (textPart.text !== undefined && textPart.text.trim() === "") {
|
|
8580
|
+
textPart.text = PLACEHOLDER_TEXT2;
|
|
8581
|
+
textPart.synthetic = true;
|
|
8582
|
+
}
|
|
8583
|
+
}
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
};
|
|
8588
|
+
}
|
|
8373
8589
|
// src/auth/antigravity/constants.ts
|
|
8374
8590
|
var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
8375
8591
|
var ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
@@ -8677,15 +8893,19 @@ function formatTokenForStorage(refreshToken, projectId, managedProjectId) {
|
|
|
8677
8893
|
}
|
|
8678
8894
|
// src/auth/antigravity/project.ts
|
|
8679
8895
|
var projectContextCache = new Map;
|
|
8896
|
+
function debugLog4(message) {
|
|
8897
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
8898
|
+
console.log(`[antigravity-project] ${message}`);
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8680
8901
|
var CODE_ASSIST_METADATA = {
|
|
8681
8902
|
ideType: "IDE_UNSPECIFIED",
|
|
8682
8903
|
platform: "PLATFORM_UNSPECIFIED",
|
|
8683
8904
|
pluginType: "GEMINI"
|
|
8684
8905
|
};
|
|
8685
8906
|
function extractProjectId(project) {
|
|
8686
|
-
if (!project)
|
|
8907
|
+
if (!project)
|
|
8687
8908
|
return;
|
|
8688
|
-
}
|
|
8689
8909
|
if (typeof project === "string") {
|
|
8690
8910
|
const trimmed = project.trim();
|
|
8691
8911
|
return trimmed || undefined;
|
|
@@ -8699,10 +8919,31 @@ function extractProjectId(project) {
|
|
|
8699
8919
|
}
|
|
8700
8920
|
return;
|
|
8701
8921
|
}
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8922
|
+
function getDefaultTierId(allowedTiers) {
|
|
8923
|
+
if (!allowedTiers || allowedTiers.length === 0)
|
|
8924
|
+
return;
|
|
8925
|
+
for (const tier of allowedTiers) {
|
|
8926
|
+
if (tier?.isDefault)
|
|
8927
|
+
return tier.id;
|
|
8928
|
+
}
|
|
8929
|
+
return allowedTiers[0]?.id;
|
|
8930
|
+
}
|
|
8931
|
+
function isFreeTier(tierId) {
|
|
8932
|
+
if (!tierId)
|
|
8933
|
+
return false;
|
|
8934
|
+
const lower = tierId.toLowerCase();
|
|
8935
|
+
return lower === "free" || lower === "free-tier" || lower.startsWith("free");
|
|
8936
|
+
}
|
|
8937
|
+
function wait(ms) {
|
|
8938
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8939
|
+
}
|
|
8940
|
+
async function callLoadCodeAssistAPI(accessToken, projectId) {
|
|
8941
|
+
const metadata = { ...CODE_ASSIST_METADATA };
|
|
8942
|
+
if (projectId)
|
|
8943
|
+
metadata.duetProject = projectId;
|
|
8944
|
+
const requestBody = { metadata };
|
|
8945
|
+
if (projectId)
|
|
8946
|
+
requestBody.cloudaicompanionProject = projectId;
|
|
8706
8947
|
const headers = {
|
|
8707
8948
|
Authorization: `Bearer ${accessToken}`,
|
|
8708
8949
|
"Content-Type": "application/json",
|
|
@@ -8712,6 +8953,7 @@ async function callLoadCodeAssistAPI(accessToken) {
|
|
|
8712
8953
|
};
|
|
8713
8954
|
for (const baseEndpoint of ANTIGRAVITY_ENDPOINT_FALLBACKS) {
|
|
8714
8955
|
const url = `${baseEndpoint}/${ANTIGRAVITY_API_VERSION}:loadCodeAssist`;
|
|
8956
|
+
debugLog4(`[loadCodeAssist] Trying: ${url}`);
|
|
8715
8957
|
try {
|
|
8716
8958
|
const response = await fetch(url, {
|
|
8717
8959
|
method: "POST",
|
|
@@ -8719,30 +8961,130 @@ async function callLoadCodeAssistAPI(accessToken) {
|
|
|
8719
8961
|
body: JSON.stringify(requestBody)
|
|
8720
8962
|
});
|
|
8721
8963
|
if (!response.ok) {
|
|
8964
|
+
debugLog4(`[loadCodeAssist] Failed: ${response.status} ${response.statusText}`);
|
|
8722
8965
|
continue;
|
|
8723
8966
|
}
|
|
8724
8967
|
const data = await response.json();
|
|
8968
|
+
debugLog4(`[loadCodeAssist] Success: ${JSON.stringify(data)}`);
|
|
8725
8969
|
return data;
|
|
8726
|
-
} catch {
|
|
8970
|
+
} catch (err) {
|
|
8971
|
+
debugLog4(`[loadCodeAssist] Error: ${err}`);
|
|
8727
8972
|
continue;
|
|
8728
8973
|
}
|
|
8729
8974
|
}
|
|
8975
|
+
debugLog4(`[loadCodeAssist] All endpoints failed`);
|
|
8730
8976
|
return null;
|
|
8731
8977
|
}
|
|
8978
|
+
async function onboardManagedProject(accessToken, tierId, projectId, attempts = 10, delayMs = 5000) {
|
|
8979
|
+
debugLog4(`[onboardUser] Starting with tierId=${tierId}, projectId=${projectId || "none"}`);
|
|
8980
|
+
const metadata = { ...CODE_ASSIST_METADATA };
|
|
8981
|
+
if (projectId)
|
|
8982
|
+
metadata.duetProject = projectId;
|
|
8983
|
+
const requestBody = { tierId, metadata };
|
|
8984
|
+
if (!isFreeTier(tierId)) {
|
|
8985
|
+
if (!projectId) {
|
|
8986
|
+
debugLog4(`[onboardUser] Non-FREE tier requires projectId, returning undefined`);
|
|
8987
|
+
return;
|
|
8988
|
+
}
|
|
8989
|
+
requestBody.cloudaicompanionProject = projectId;
|
|
8990
|
+
}
|
|
8991
|
+
const headers = {
|
|
8992
|
+
Authorization: `Bearer ${accessToken}`,
|
|
8993
|
+
"Content-Type": "application/json",
|
|
8994
|
+
"User-Agent": ANTIGRAVITY_HEADERS["User-Agent"],
|
|
8995
|
+
"X-Goog-Api-Client": ANTIGRAVITY_HEADERS["X-Goog-Api-Client"],
|
|
8996
|
+
"Client-Metadata": ANTIGRAVITY_HEADERS["Client-Metadata"]
|
|
8997
|
+
};
|
|
8998
|
+
debugLog4(`[onboardUser] Request body: ${JSON.stringify(requestBody)}`);
|
|
8999
|
+
for (let attempt = 0;attempt < attempts; attempt++) {
|
|
9000
|
+
debugLog4(`[onboardUser] Attempt ${attempt + 1}/${attempts}`);
|
|
9001
|
+
for (const baseEndpoint of ANTIGRAVITY_ENDPOINT_FALLBACKS) {
|
|
9002
|
+
const url = `${baseEndpoint}/${ANTIGRAVITY_API_VERSION}:onboardUser`;
|
|
9003
|
+
debugLog4(`[onboardUser] Trying: ${url}`);
|
|
9004
|
+
try {
|
|
9005
|
+
const response = await fetch(url, {
|
|
9006
|
+
method: "POST",
|
|
9007
|
+
headers,
|
|
9008
|
+
body: JSON.stringify(requestBody)
|
|
9009
|
+
});
|
|
9010
|
+
if (!response.ok) {
|
|
9011
|
+
const errorText = await response.text().catch(() => "");
|
|
9012
|
+
debugLog4(`[onboardUser] Failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
9013
|
+
continue;
|
|
9014
|
+
}
|
|
9015
|
+
const payload = await response.json();
|
|
9016
|
+
debugLog4(`[onboardUser] Response: ${JSON.stringify(payload)}`);
|
|
9017
|
+
const managedProjectId = payload.response?.cloudaicompanionProject?.id;
|
|
9018
|
+
if (payload.done && managedProjectId) {
|
|
9019
|
+
debugLog4(`[onboardUser] Success! Got managed project ID: ${managedProjectId}`);
|
|
9020
|
+
return managedProjectId;
|
|
9021
|
+
}
|
|
9022
|
+
if (payload.done && projectId) {
|
|
9023
|
+
debugLog4(`[onboardUser] Done but no managed ID, using original: ${projectId}`);
|
|
9024
|
+
return projectId;
|
|
9025
|
+
}
|
|
9026
|
+
debugLog4(`[onboardUser] Not done yet, payload.done=${payload.done}`);
|
|
9027
|
+
} catch (err) {
|
|
9028
|
+
debugLog4(`[onboardUser] Error: ${err}`);
|
|
9029
|
+
continue;
|
|
9030
|
+
}
|
|
9031
|
+
}
|
|
9032
|
+
if (attempt < attempts - 1) {
|
|
9033
|
+
debugLog4(`[onboardUser] Waiting ${delayMs}ms before next attempt...`);
|
|
9034
|
+
await wait(delayMs);
|
|
9035
|
+
}
|
|
9036
|
+
}
|
|
9037
|
+
debugLog4(`[onboardUser] All attempts exhausted, returning undefined`);
|
|
9038
|
+
return;
|
|
9039
|
+
}
|
|
8732
9040
|
async function fetchProjectContext(accessToken) {
|
|
9041
|
+
debugLog4(`[fetchProjectContext] Starting...`);
|
|
8733
9042
|
const cached = projectContextCache.get(accessToken);
|
|
8734
9043
|
if (cached) {
|
|
9044
|
+
debugLog4(`[fetchProjectContext] Returning cached result: ${JSON.stringify(cached)}`);
|
|
8735
9045
|
return cached;
|
|
8736
9046
|
}
|
|
8737
|
-
const
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
9047
|
+
const loadPayload = await callLoadCodeAssistAPI(accessToken);
|
|
9048
|
+
if (loadPayload?.cloudaicompanionProject) {
|
|
9049
|
+
const projectId = extractProjectId(loadPayload.cloudaicompanionProject);
|
|
9050
|
+
debugLog4(`[fetchProjectContext] loadCodeAssist returned project: ${projectId}`);
|
|
9051
|
+
if (projectId) {
|
|
9052
|
+
const result = { cloudaicompanionProject: projectId };
|
|
9053
|
+
projectContextCache.set(accessToken, result);
|
|
9054
|
+
debugLog4(`[fetchProjectContext] Using loadCodeAssist project ID: ${projectId}`);
|
|
9055
|
+
return result;
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
if (!loadPayload) {
|
|
9059
|
+
debugLog4(`[fetchProjectContext] loadCodeAssist returned null, returning empty`);
|
|
9060
|
+
return { cloudaicompanionProject: "" };
|
|
9061
|
+
}
|
|
9062
|
+
const currentTierId = loadPayload.currentTier?.id;
|
|
9063
|
+
debugLog4(`[fetchProjectContext] currentTier: ${currentTierId}, allowedTiers: ${JSON.stringify(loadPayload.allowedTiers)}`);
|
|
9064
|
+
if (currentTierId && !isFreeTier(currentTierId)) {
|
|
9065
|
+
debugLog4(`[fetchProjectContext] PAID tier detected, returning empty (user must provide project)`);
|
|
9066
|
+
return { cloudaicompanionProject: "" };
|
|
9067
|
+
}
|
|
9068
|
+
const defaultTierId = getDefaultTierId(loadPayload.allowedTiers);
|
|
9069
|
+
const tierId = defaultTierId ?? "free-tier";
|
|
9070
|
+
debugLog4(`[fetchProjectContext] Resolved tierId: ${tierId}`);
|
|
9071
|
+
if (!isFreeTier(tierId)) {
|
|
9072
|
+
debugLog4(`[fetchProjectContext] Non-FREE tier without project, returning empty`);
|
|
9073
|
+
return { cloudaicompanionProject: "" };
|
|
9074
|
+
}
|
|
9075
|
+
debugLog4(`[fetchProjectContext] FREE tier detected (${tierId}), calling onboardUser...`);
|
|
9076
|
+
const managedProjectId = await onboardManagedProject(accessToken, tierId);
|
|
9077
|
+
if (managedProjectId) {
|
|
9078
|
+
const result = {
|
|
9079
|
+
cloudaicompanionProject: managedProjectId,
|
|
9080
|
+
managedProjectId
|
|
9081
|
+
};
|
|
8743
9082
|
projectContextCache.set(accessToken, result);
|
|
9083
|
+
debugLog4(`[fetchProjectContext] Got managed project ID: ${managedProjectId}`);
|
|
9084
|
+
return result;
|
|
8744
9085
|
}
|
|
8745
|
-
|
|
9086
|
+
debugLog4(`[fetchProjectContext] Failed to get managed project ID, returning empty`);
|
|
9087
|
+
return { cloudaicompanionProject: "" };
|
|
8746
9088
|
}
|
|
8747
9089
|
function clearProjectContextCache(accessToken) {
|
|
8748
9090
|
if (accessToken) {
|
|
@@ -8806,21 +9148,21 @@ function wrapRequestBody(body, projectId, modelName, sessionId) {
|
|
|
8806
9148
|
}
|
|
8807
9149
|
};
|
|
8808
9150
|
}
|
|
8809
|
-
function
|
|
9151
|
+
function debugLog5(message) {
|
|
8810
9152
|
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
8811
9153
|
console.log(`[antigravity-request] ${message}`);
|
|
8812
9154
|
}
|
|
8813
9155
|
}
|
|
8814
9156
|
function injectThoughtSignatureIntoFunctionCalls(body, signature) {
|
|
8815
9157
|
const effectiveSignature = signature || SKIP_THOUGHT_SIGNATURE_VALIDATOR;
|
|
8816
|
-
|
|
8817
|
-
|
|
9158
|
+
debugLog5(`[TSIG][INJECT] signature=${effectiveSignature.substring(0, 30)}... (${signature ? "provided" : "default"})`);
|
|
9159
|
+
debugLog5(`[TSIG][INJECT] body keys: ${Object.keys(body).join(", ")}`);
|
|
8818
9160
|
const contents = body.contents;
|
|
8819
9161
|
if (!contents || !Array.isArray(contents)) {
|
|
8820
|
-
|
|
9162
|
+
debugLog5(`[TSIG][INJECT] No contents array! Has messages: ${!!body.messages}`);
|
|
8821
9163
|
return body;
|
|
8822
9164
|
}
|
|
8823
|
-
|
|
9165
|
+
debugLog5(`[TSIG][INJECT] Found ${contents.length} content blocks`);
|
|
8824
9166
|
let injectedCount = 0;
|
|
8825
9167
|
const modifiedContents = contents.map((content) => {
|
|
8826
9168
|
if (!content.parts || !Array.isArray(content.parts)) {
|
|
@@ -8838,7 +9180,7 @@ function injectThoughtSignatureIntoFunctionCalls(body, signature) {
|
|
|
8838
9180
|
});
|
|
8839
9181
|
return { ...content, parts: modifiedParts };
|
|
8840
9182
|
});
|
|
8841
|
-
|
|
9183
|
+
debugLog5(`[TSIG][INJECT] injected signature into ${injectedCount} functionCall(s)`);
|
|
8842
9184
|
return { ...body, contents: modifiedContents };
|
|
8843
9185
|
}
|
|
8844
9186
|
function isStreamingRequest(url, body) {
|
|
@@ -9342,13 +9684,13 @@ function getOrCreateSessionId(fetchInstanceId, sessionId) {
|
|
|
9342
9684
|
return newSessionId;
|
|
9343
9685
|
}
|
|
9344
9686
|
// src/auth/antigravity/message-converter.ts
|
|
9345
|
-
function
|
|
9687
|
+
function debugLog6(message) {
|
|
9346
9688
|
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
9347
9689
|
console.log(`[antigravity-converter] ${message}`);
|
|
9348
9690
|
}
|
|
9349
9691
|
}
|
|
9350
9692
|
function convertOpenAIToGemini(messages, thoughtSignature) {
|
|
9351
|
-
|
|
9693
|
+
debugLog6(`Converting ${messages.length} messages, signature: ${thoughtSignature ? "present" : "none"}`);
|
|
9352
9694
|
const contents = [];
|
|
9353
9695
|
for (const msg of messages) {
|
|
9354
9696
|
if (msg.role === "system") {
|
|
@@ -9383,7 +9725,7 @@ function convertOpenAIToGemini(messages, thoughtSignature) {
|
|
|
9383
9725
|
}
|
|
9384
9726
|
};
|
|
9385
9727
|
part.thoughtSignature = thoughtSignature || SKIP_THOUGHT_SIGNATURE_VALIDATOR;
|
|
9386
|
-
|
|
9728
|
+
debugLog6(`Injected signature into functionCall: ${toolCall.function.name} (${thoughtSignature ? "provided" : "default"})`);
|
|
9387
9729
|
parts.push(part);
|
|
9388
9730
|
}
|
|
9389
9731
|
}
|
|
@@ -9412,7 +9754,7 @@ function convertOpenAIToGemini(messages, thoughtSignature) {
|
|
|
9412
9754
|
continue;
|
|
9413
9755
|
}
|
|
9414
9756
|
}
|
|
9415
|
-
|
|
9757
|
+
debugLog6(`Converted to ${contents.length} content blocks`);
|
|
9416
9758
|
return contents;
|
|
9417
9759
|
}
|
|
9418
9760
|
function convertContentToParts(content) {
|
|
@@ -9448,7 +9790,7 @@ function hasOpenAIMessages(body) {
|
|
|
9448
9790
|
}
|
|
9449
9791
|
function convertRequestBody(body, thoughtSignature) {
|
|
9450
9792
|
if (!hasOpenAIMessages(body)) {
|
|
9451
|
-
|
|
9793
|
+
debugLog6("No messages array found, returning body as-is");
|
|
9452
9794
|
return body;
|
|
9453
9795
|
}
|
|
9454
9796
|
const messages = body.messages;
|
|
@@ -9456,11 +9798,11 @@ function convertRequestBody(body, thoughtSignature) {
|
|
|
9456
9798
|
const converted = { ...body };
|
|
9457
9799
|
delete converted.messages;
|
|
9458
9800
|
converted.contents = contents;
|
|
9459
|
-
|
|
9801
|
+
debugLog6(`Converted body: messages \u2192 contents (${contents.length} blocks)`);
|
|
9460
9802
|
return converted;
|
|
9461
9803
|
}
|
|
9462
9804
|
// src/auth/antigravity/fetch.ts
|
|
9463
|
-
function
|
|
9805
|
+
function debugLog7(message) {
|
|
9464
9806
|
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
9465
9807
|
console.log(`[antigravity-fetch] ${message}`);
|
|
9466
9808
|
}
|
|
@@ -9483,7 +9825,7 @@ var GCP_PERMISSION_ERROR_PATTERNS = [
|
|
|
9483
9825
|
function isGcpPermissionError(text) {
|
|
9484
9826
|
return GCP_PERMISSION_ERROR_PATTERNS.some((pattern) => text.includes(pattern));
|
|
9485
9827
|
}
|
|
9486
|
-
function
|
|
9828
|
+
function calculateRetryDelay(attempt) {
|
|
9487
9829
|
return Math.min(200 * Math.pow(2, attempt), 2000);
|
|
9488
9830
|
}
|
|
9489
9831
|
async function isRetryableResponse(response) {
|
|
@@ -9493,7 +9835,7 @@ async function isRetryableResponse(response) {
|
|
|
9493
9835
|
try {
|
|
9494
9836
|
const text = await response.clone().text();
|
|
9495
9837
|
if (text.includes("SUBSCRIPTION_REQUIRED") || text.includes("Gemini Code Assist license")) {
|
|
9496
|
-
|
|
9838
|
+
debugLog7(`[RETRY] 403 SUBSCRIPTION_REQUIRED detected, will retry with next endpoint`);
|
|
9497
9839
|
return true;
|
|
9498
9840
|
}
|
|
9499
9841
|
} catch {}
|
|
@@ -9502,11 +9844,11 @@ async function isRetryableResponse(response) {
|
|
|
9502
9844
|
}
|
|
9503
9845
|
async function attemptFetch(options) {
|
|
9504
9846
|
const { endpoint, url, init, accessToken, projectId, sessionId, modelName, thoughtSignature } = options;
|
|
9505
|
-
|
|
9847
|
+
debugLog7(`Trying endpoint: ${endpoint}`);
|
|
9506
9848
|
try {
|
|
9507
9849
|
const rawBody = init.body;
|
|
9508
9850
|
if (rawBody !== undefined && typeof rawBody !== "string") {
|
|
9509
|
-
|
|
9851
|
+
debugLog7(`Non-string body detected (${typeof rawBody}), signaling pass-through`);
|
|
9510
9852
|
return "pass-through";
|
|
9511
9853
|
}
|
|
9512
9854
|
let parsedBody = {};
|
|
@@ -9517,13 +9859,13 @@ async function attemptFetch(options) {
|
|
|
9517
9859
|
parsedBody = {};
|
|
9518
9860
|
}
|
|
9519
9861
|
}
|
|
9520
|
-
|
|
9521
|
-
|
|
9862
|
+
debugLog7(`[BODY] Keys: ${Object.keys(parsedBody).join(", ")}`);
|
|
9863
|
+
debugLog7(`[BODY] Has contents: ${!!parsedBody.contents}, Has messages: ${!!parsedBody.messages}`);
|
|
9522
9864
|
if (parsedBody.contents) {
|
|
9523
9865
|
const contents = parsedBody.contents;
|
|
9524
|
-
|
|
9866
|
+
debugLog7(`[BODY] contents length: ${contents.length}`);
|
|
9525
9867
|
contents.forEach((c, i) => {
|
|
9526
|
-
|
|
9868
|
+
debugLog7(`[BODY] contents[${i}].role: ${c.role}, parts: ${JSON.stringify(c.parts).substring(0, 200)}`);
|
|
9527
9869
|
});
|
|
9528
9870
|
}
|
|
9529
9871
|
if (parsedBody.tools && Array.isArray(parsedBody.tools)) {
|
|
@@ -9533,9 +9875,9 @@ async function attemptFetch(options) {
|
|
|
9533
9875
|
}
|
|
9534
9876
|
}
|
|
9535
9877
|
if (hasOpenAIMessages(parsedBody)) {
|
|
9536
|
-
|
|
9878
|
+
debugLog7(`[CONVERT] Converting OpenAI messages to Gemini contents`);
|
|
9537
9879
|
parsedBody = convertRequestBody(parsedBody, thoughtSignature);
|
|
9538
|
-
|
|
9880
|
+
debugLog7(`[CONVERT] After conversion - Has contents: ${!!parsedBody.contents}`);
|
|
9539
9881
|
}
|
|
9540
9882
|
const transformed = transformRequest({
|
|
9541
9883
|
url,
|
|
@@ -9547,7 +9889,7 @@ async function attemptFetch(options) {
|
|
|
9547
9889
|
endpointOverride: endpoint,
|
|
9548
9890
|
thoughtSignature
|
|
9549
9891
|
});
|
|
9550
|
-
|
|
9892
|
+
debugLog7(`[REQ] streaming=${transformed.streaming}, url=${transformed.url}`);
|
|
9551
9893
|
const maxPermissionRetries = 10;
|
|
9552
9894
|
for (let attempt = 0;attempt <= maxPermissionRetries; attempt++) {
|
|
9553
9895
|
const response = await fetch(transformed.url, {
|
|
@@ -9556,9 +9898,9 @@ async function attemptFetch(options) {
|
|
|
9556
9898
|
body: JSON.stringify(transformed.body),
|
|
9557
9899
|
signal: init.signal
|
|
9558
9900
|
});
|
|
9559
|
-
|
|
9901
|
+
debugLog7(`[RESP] status=${response.status} content-type=${response.headers.get("content-type") ?? ""} url=${response.url}`);
|
|
9560
9902
|
if (response.status === 401) {
|
|
9561
|
-
|
|
9903
|
+
debugLog7(`[401] Unauthorized response detected, signaling token refresh needed`);
|
|
9562
9904
|
return "needs-refresh";
|
|
9563
9905
|
}
|
|
9564
9906
|
if (response.status === 403) {
|
|
@@ -9566,24 +9908,24 @@ async function attemptFetch(options) {
|
|
|
9566
9908
|
const text = await response.clone().text();
|
|
9567
9909
|
if (isGcpPermissionError(text)) {
|
|
9568
9910
|
if (attempt < maxPermissionRetries) {
|
|
9569
|
-
const delay =
|
|
9570
|
-
|
|
9911
|
+
const delay = calculateRetryDelay(attempt);
|
|
9912
|
+
debugLog7(`[RETRY] GCP permission error, retry ${attempt + 1}/${maxPermissionRetries} after ${delay}ms`);
|
|
9571
9913
|
await new Promise((resolve5) => setTimeout(resolve5, delay));
|
|
9572
9914
|
continue;
|
|
9573
9915
|
}
|
|
9574
|
-
|
|
9916
|
+
debugLog7(`[RETRY] GCP permission error, max retries exceeded`);
|
|
9575
9917
|
}
|
|
9576
9918
|
} catch {}
|
|
9577
9919
|
}
|
|
9578
9920
|
if (!response.ok && await isRetryableResponse(response)) {
|
|
9579
|
-
|
|
9921
|
+
debugLog7(`Endpoint failed: ${endpoint} (status: ${response.status}), trying next`);
|
|
9580
9922
|
return null;
|
|
9581
9923
|
}
|
|
9582
9924
|
return response;
|
|
9583
9925
|
}
|
|
9584
9926
|
return null;
|
|
9585
9927
|
} catch (error) {
|
|
9586
|
-
|
|
9928
|
+
debugLog7(`Endpoint failed: ${endpoint} (${error instanceof Error ? error.message : "Unknown error"}), trying next`);
|
|
9587
9929
|
return null;
|
|
9588
9930
|
}
|
|
9589
9931
|
}
|
|
@@ -9618,17 +9960,17 @@ async function transformResponseWithThinking(response, modelName, fetchInstanceI
|
|
|
9618
9960
|
}
|
|
9619
9961
|
try {
|
|
9620
9962
|
const text = await result.response.clone().text();
|
|
9621
|
-
|
|
9963
|
+
debugLog7(`[TSIG][RESP] Response text length: ${text.length}`);
|
|
9622
9964
|
const parsed = JSON.parse(text);
|
|
9623
|
-
|
|
9624
|
-
|
|
9965
|
+
debugLog7(`[TSIG][RESP] Parsed keys: ${Object.keys(parsed).join(", ")}`);
|
|
9966
|
+
debugLog7(`[TSIG][RESP] Has candidates: ${!!parsed.candidates}, count: ${parsed.candidates?.length ?? 0}`);
|
|
9625
9967
|
const signature = extractSignatureFromResponse(parsed);
|
|
9626
|
-
|
|
9968
|
+
debugLog7(`[TSIG][RESP] Signature extracted: ${signature ? signature.substring(0, 30) + "..." : "NONE"}`);
|
|
9627
9969
|
if (signature) {
|
|
9628
9970
|
setThoughtSignature(fetchInstanceId, signature);
|
|
9629
|
-
|
|
9971
|
+
debugLog7(`[TSIG][STORE] Stored signature for ${fetchInstanceId}`);
|
|
9630
9972
|
} else {
|
|
9631
|
-
|
|
9973
|
+
debugLog7(`[TSIG][WARN] No signature found in response!`);
|
|
9632
9974
|
}
|
|
9633
9975
|
if (shouldIncludeThinking(modelName)) {
|
|
9634
9976
|
const thinkingResult = extractThinkingBlocks(parsed);
|
|
@@ -9649,7 +9991,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9649
9991
|
let cachedProjectId = null;
|
|
9650
9992
|
const fetchInstanceId = crypto.randomUUID();
|
|
9651
9993
|
return async (url, init = {}) => {
|
|
9652
|
-
|
|
9994
|
+
debugLog7(`Intercepting request to: ${url}`);
|
|
9653
9995
|
const auth = await getAuth();
|
|
9654
9996
|
if (!auth.access || !auth.refresh) {
|
|
9655
9997
|
throw new Error("Antigravity: No authentication tokens available");
|
|
@@ -9668,7 +10010,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9668
10010
|
cachedTokens.refresh_token = refreshParts.refreshToken;
|
|
9669
10011
|
}
|
|
9670
10012
|
if (isTokenExpired(cachedTokens)) {
|
|
9671
|
-
|
|
10013
|
+
debugLog7("Token expired, refreshing...");
|
|
9672
10014
|
try {
|
|
9673
10015
|
const newTokens = await refreshAccessToken(refreshParts.refreshToken, clientId, clientSecret);
|
|
9674
10016
|
cachedTokens = {
|
|
@@ -9685,7 +10027,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9685
10027
|
refresh: formattedRefresh,
|
|
9686
10028
|
expires: Date.now() + newTokens.expires_in * 1000
|
|
9687
10029
|
});
|
|
9688
|
-
|
|
10030
|
+
debugLog7("Token refreshed successfully");
|
|
9689
10031
|
} catch (error) {
|
|
9690
10032
|
throw new Error(`Antigravity: Token refresh failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
9691
10033
|
}
|
|
@@ -9693,10 +10035,10 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9693
10035
|
if (!cachedProjectId) {
|
|
9694
10036
|
const projectContext = await fetchProjectContext(cachedTokens.access_token);
|
|
9695
10037
|
cachedProjectId = projectContext.cloudaicompanionProject || "";
|
|
9696
|
-
|
|
10038
|
+
debugLog7(`[PROJECT] Fetched project ID: "${cachedProjectId}"`);
|
|
9697
10039
|
}
|
|
9698
10040
|
const projectId = cachedProjectId;
|
|
9699
|
-
|
|
10041
|
+
debugLog7(`[PROJECT] Using project ID: "${projectId}"`);
|
|
9700
10042
|
let modelName;
|
|
9701
10043
|
if (init.body) {
|
|
9702
10044
|
try {
|
|
@@ -9709,7 +10051,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9709
10051
|
const maxEndpoints = Math.min(ANTIGRAVITY_ENDPOINT_FALLBACKS.length, 3);
|
|
9710
10052
|
const sessionId = getOrCreateSessionId(fetchInstanceId);
|
|
9711
10053
|
const thoughtSignature = getThoughtSignature(fetchInstanceId);
|
|
9712
|
-
|
|
10054
|
+
debugLog7(`[TSIG][GET] sessionId=${sessionId}, signature=${thoughtSignature ? thoughtSignature.substring(0, 20) + "..." : "none"}`);
|
|
9713
10055
|
let hasRefreshedFor401 = false;
|
|
9714
10056
|
const executeWithEndpoints = async () => {
|
|
9715
10057
|
for (let i = 0;i < maxEndpoints; i++) {
|
|
@@ -9725,7 +10067,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9725
10067
|
thoughtSignature
|
|
9726
10068
|
});
|
|
9727
10069
|
if (response === "pass-through") {
|
|
9728
|
-
|
|
10070
|
+
debugLog7("Non-string body detected, passing through with auth headers");
|
|
9729
10071
|
const headersWithAuth = {
|
|
9730
10072
|
...init.headers,
|
|
9731
10073
|
Authorization: `Bearer ${cachedTokens.access_token}`
|
|
@@ -9734,7 +10076,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9734
10076
|
}
|
|
9735
10077
|
if (response === "needs-refresh") {
|
|
9736
10078
|
if (hasRefreshedFor401) {
|
|
9737
|
-
|
|
10079
|
+
debugLog7("[401] Already refreshed once, returning unauthorized error");
|
|
9738
10080
|
return new Response(JSON.stringify({
|
|
9739
10081
|
error: {
|
|
9740
10082
|
message: "Authentication failed after token refresh",
|
|
@@ -9747,7 +10089,7 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9747
10089
|
headers: { "Content-Type": "application/json" }
|
|
9748
10090
|
});
|
|
9749
10091
|
}
|
|
9750
|
-
|
|
10092
|
+
debugLog7("[401] Refreshing token and retrying...");
|
|
9751
10093
|
hasRefreshedFor401 = true;
|
|
9752
10094
|
try {
|
|
9753
10095
|
const newTokens = await refreshAccessToken(refreshParts.refreshToken, clientId, clientSecret);
|
|
@@ -9765,10 +10107,10 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9765
10107
|
refresh: formattedRefresh,
|
|
9766
10108
|
expires: Date.now() + newTokens.expires_in * 1000
|
|
9767
10109
|
});
|
|
9768
|
-
|
|
10110
|
+
debugLog7("[401] Token refreshed, retrying request...");
|
|
9769
10111
|
return executeWithEndpoints();
|
|
9770
10112
|
} catch (refreshError) {
|
|
9771
|
-
|
|
10113
|
+
debugLog7(`[401] Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`);
|
|
9772
10114
|
return new Response(JSON.stringify({
|
|
9773
10115
|
error: {
|
|
9774
10116
|
message: `Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`,
|
|
@@ -9783,13 +10125,13 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
|
|
|
9783
10125
|
}
|
|
9784
10126
|
}
|
|
9785
10127
|
if (response) {
|
|
9786
|
-
|
|
10128
|
+
debugLog7(`Success with endpoint: ${endpoint}`);
|
|
9787
10129
|
const transformedResponse = await transformResponseWithThinking(response, modelName || "", fetchInstanceId);
|
|
9788
10130
|
return transformedResponse;
|
|
9789
10131
|
}
|
|
9790
10132
|
}
|
|
9791
10133
|
const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`;
|
|
9792
|
-
|
|
10134
|
+
debugLog7(errorMessage);
|
|
9793
10135
|
return new Response(JSON.stringify({
|
|
9794
10136
|
error: {
|
|
9795
10137
|
message: errorMessage,
|
|
@@ -9934,22 +10276,22 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
9934
10276
|
};
|
|
9935
10277
|
}
|
|
9936
10278
|
// src/features/claude-code-command-loader/loader.ts
|
|
9937
|
-
import { existsSync as
|
|
10279
|
+
import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
|
|
9938
10280
|
import { homedir as homedir10 } from "os";
|
|
9939
|
-
import { join as
|
|
10281
|
+
import { join as join32, basename } from "path";
|
|
9940
10282
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
9941
|
-
if (!
|
|
10283
|
+
if (!existsSync23(commandsDir)) {
|
|
9942
10284
|
return [];
|
|
9943
10285
|
}
|
|
9944
|
-
const entries =
|
|
10286
|
+
const entries = readdirSync6(commandsDir, { withFileTypes: true });
|
|
9945
10287
|
const commands = [];
|
|
9946
10288
|
for (const entry of entries) {
|
|
9947
10289
|
if (!isMarkdownFile(entry))
|
|
9948
10290
|
continue;
|
|
9949
|
-
const commandPath =
|
|
10291
|
+
const commandPath = join32(commandsDir, entry.name);
|
|
9950
10292
|
const commandName = basename(entry.name, ".md");
|
|
9951
10293
|
try {
|
|
9952
|
-
const content =
|
|
10294
|
+
const content = readFileSync15(commandPath, "utf-8");
|
|
9953
10295
|
const { data, body } = parseFrontmatter(content);
|
|
9954
10296
|
const wrappedTemplate = `<command-instruction>
|
|
9955
10297
|
${body.trim()}
|
|
@@ -9989,47 +10331,47 @@ function commandsToRecord(commands) {
|
|
|
9989
10331
|
return result;
|
|
9990
10332
|
}
|
|
9991
10333
|
function loadUserCommands() {
|
|
9992
|
-
const userCommandsDir =
|
|
10334
|
+
const userCommandsDir = join32(homedir10(), ".claude", "commands");
|
|
9993
10335
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
9994
10336
|
return commandsToRecord(commands);
|
|
9995
10337
|
}
|
|
9996
10338
|
function loadProjectCommands() {
|
|
9997
|
-
const projectCommandsDir =
|
|
10339
|
+
const projectCommandsDir = join32(process.cwd(), ".claude", "commands");
|
|
9998
10340
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
9999
10341
|
return commandsToRecord(commands);
|
|
10000
10342
|
}
|
|
10001
10343
|
function loadOpencodeGlobalCommands() {
|
|
10002
|
-
const opencodeCommandsDir =
|
|
10344
|
+
const opencodeCommandsDir = join32(homedir10(), ".config", "opencode", "command");
|
|
10003
10345
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
10004
10346
|
return commandsToRecord(commands);
|
|
10005
10347
|
}
|
|
10006
10348
|
function loadOpencodeProjectCommands() {
|
|
10007
|
-
const opencodeProjectDir =
|
|
10349
|
+
const opencodeProjectDir = join32(process.cwd(), ".opencode", "command");
|
|
10008
10350
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
10009
10351
|
return commandsToRecord(commands);
|
|
10010
10352
|
}
|
|
10011
10353
|
// src/features/claude-code-skill-loader/loader.ts
|
|
10012
|
-
import { existsSync as
|
|
10354
|
+
import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
|
|
10013
10355
|
import { homedir as homedir11 } from "os";
|
|
10014
|
-
import { join as
|
|
10356
|
+
import { join as join33 } from "path";
|
|
10015
10357
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
10016
|
-
if (!
|
|
10358
|
+
if (!existsSync24(skillsDir)) {
|
|
10017
10359
|
return [];
|
|
10018
10360
|
}
|
|
10019
|
-
const entries =
|
|
10361
|
+
const entries = readdirSync7(skillsDir, { withFileTypes: true });
|
|
10020
10362
|
const skills = [];
|
|
10021
10363
|
for (const entry of entries) {
|
|
10022
10364
|
if (entry.name.startsWith("."))
|
|
10023
10365
|
continue;
|
|
10024
|
-
const skillPath =
|
|
10366
|
+
const skillPath = join33(skillsDir, entry.name);
|
|
10025
10367
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
10026
10368
|
continue;
|
|
10027
10369
|
const resolvedPath = resolveSymlink(skillPath);
|
|
10028
|
-
const skillMdPath =
|
|
10029
|
-
if (!
|
|
10370
|
+
const skillMdPath = join33(resolvedPath, "SKILL.md");
|
|
10371
|
+
if (!existsSync24(skillMdPath))
|
|
10030
10372
|
continue;
|
|
10031
10373
|
try {
|
|
10032
|
-
const content =
|
|
10374
|
+
const content = readFileSync16(skillMdPath, "utf-8");
|
|
10033
10375
|
const { data, body } = parseFrontmatter(content);
|
|
10034
10376
|
const skillName = data.name || entry.name;
|
|
10035
10377
|
const originalDescription = data.description || "";
|
|
@@ -10060,7 +10402,7 @@ $ARGUMENTS
|
|
|
10060
10402
|
return skills;
|
|
10061
10403
|
}
|
|
10062
10404
|
function loadUserSkillsAsCommands() {
|
|
10063
|
-
const userSkillsDir =
|
|
10405
|
+
const userSkillsDir = join33(homedir11(), ".claude", "skills");
|
|
10064
10406
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
10065
10407
|
return skills.reduce((acc, skill) => {
|
|
10066
10408
|
acc[skill.name] = skill.definition;
|
|
@@ -10068,7 +10410,7 @@ function loadUserSkillsAsCommands() {
|
|
|
10068
10410
|
}, {});
|
|
10069
10411
|
}
|
|
10070
10412
|
function loadProjectSkillsAsCommands() {
|
|
10071
|
-
const projectSkillsDir =
|
|
10413
|
+
const projectSkillsDir = join33(process.cwd(), ".claude", "skills");
|
|
10072
10414
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
10073
10415
|
return skills.reduce((acc, skill) => {
|
|
10074
10416
|
acc[skill.name] = skill.definition;
|
|
@@ -10076,9 +10418,9 @@ function loadProjectSkillsAsCommands() {
|
|
|
10076
10418
|
}, {});
|
|
10077
10419
|
}
|
|
10078
10420
|
// src/features/claude-code-agent-loader/loader.ts
|
|
10079
|
-
import { existsSync as
|
|
10421
|
+
import { existsSync as existsSync25, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
|
|
10080
10422
|
import { homedir as homedir12 } from "os";
|
|
10081
|
-
import { join as
|
|
10423
|
+
import { join as join34, basename as basename2 } from "path";
|
|
10082
10424
|
function parseToolsConfig(toolsStr) {
|
|
10083
10425
|
if (!toolsStr)
|
|
10084
10426
|
return;
|
|
@@ -10092,18 +10434,18 @@ function parseToolsConfig(toolsStr) {
|
|
|
10092
10434
|
return result;
|
|
10093
10435
|
}
|
|
10094
10436
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
10095
|
-
if (!
|
|
10437
|
+
if (!existsSync25(agentsDir)) {
|
|
10096
10438
|
return [];
|
|
10097
10439
|
}
|
|
10098
|
-
const entries =
|
|
10440
|
+
const entries = readdirSync8(agentsDir, { withFileTypes: true });
|
|
10099
10441
|
const agents = [];
|
|
10100
10442
|
for (const entry of entries) {
|
|
10101
10443
|
if (!isMarkdownFile(entry))
|
|
10102
10444
|
continue;
|
|
10103
|
-
const agentPath =
|
|
10445
|
+
const agentPath = join34(agentsDir, entry.name);
|
|
10104
10446
|
const agentName = basename2(entry.name, ".md");
|
|
10105
10447
|
try {
|
|
10106
|
-
const content =
|
|
10448
|
+
const content = readFileSync17(agentPath, "utf-8");
|
|
10107
10449
|
const { data, body } = parseFrontmatter(content);
|
|
10108
10450
|
const name = data.name || agentName;
|
|
10109
10451
|
const originalDescription = data.description || "";
|
|
@@ -10130,7 +10472,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
10130
10472
|
return agents;
|
|
10131
10473
|
}
|
|
10132
10474
|
function loadUserAgents() {
|
|
10133
|
-
const userAgentsDir =
|
|
10475
|
+
const userAgentsDir = join34(homedir12(), ".claude", "agents");
|
|
10134
10476
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
10135
10477
|
const result = {};
|
|
10136
10478
|
for (const agent of agents) {
|
|
@@ -10139,7 +10481,7 @@ function loadUserAgents() {
|
|
|
10139
10481
|
return result;
|
|
10140
10482
|
}
|
|
10141
10483
|
function loadProjectAgents() {
|
|
10142
|
-
const projectAgentsDir =
|
|
10484
|
+
const projectAgentsDir = join34(process.cwd(), ".claude", "agents");
|
|
10143
10485
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
10144
10486
|
const result = {};
|
|
10145
10487
|
for (const agent of agents) {
|
|
@@ -10148,9 +10490,9 @@ function loadProjectAgents() {
|
|
|
10148
10490
|
return result;
|
|
10149
10491
|
}
|
|
10150
10492
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
10151
|
-
import { existsSync as
|
|
10493
|
+
import { existsSync as existsSync26 } from "fs";
|
|
10152
10494
|
import { homedir as homedir13 } from "os";
|
|
10153
|
-
import { join as
|
|
10495
|
+
import { join as join35 } from "path";
|
|
10154
10496
|
|
|
10155
10497
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
10156
10498
|
function expandEnvVars(value) {
|
|
@@ -10219,13 +10561,13 @@ function getMcpConfigPaths() {
|
|
|
10219
10561
|
const home = homedir13();
|
|
10220
10562
|
const cwd = process.cwd();
|
|
10221
10563
|
return [
|
|
10222
|
-
{ path:
|
|
10223
|
-
{ path:
|
|
10224
|
-
{ path:
|
|
10564
|
+
{ path: join35(home, ".claude", ".mcp.json"), scope: "user" },
|
|
10565
|
+
{ path: join35(cwd, ".mcp.json"), scope: "project" },
|
|
10566
|
+
{ path: join35(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
10225
10567
|
];
|
|
10226
10568
|
}
|
|
10227
10569
|
async function loadMcpConfigFile(filePath) {
|
|
10228
|
-
if (!
|
|
10570
|
+
if (!existsSync26(filePath)) {
|
|
10229
10571
|
return null;
|
|
10230
10572
|
}
|
|
10231
10573
|
try {
|
|
@@ -10511,14 +10853,14 @@ var EXT_TO_LANG = {
|
|
|
10511
10853
|
".tfvars": "terraform"
|
|
10512
10854
|
};
|
|
10513
10855
|
// src/tools/lsp/config.ts
|
|
10514
|
-
import { existsSync as
|
|
10515
|
-
import { join as
|
|
10856
|
+
import { existsSync as existsSync27, readFileSync as readFileSync18 } from "fs";
|
|
10857
|
+
import { join as join36 } from "path";
|
|
10516
10858
|
import { homedir as homedir14 } from "os";
|
|
10517
10859
|
function loadJsonFile(path6) {
|
|
10518
|
-
if (!
|
|
10860
|
+
if (!existsSync27(path6))
|
|
10519
10861
|
return null;
|
|
10520
10862
|
try {
|
|
10521
|
-
return JSON.parse(
|
|
10863
|
+
return JSON.parse(readFileSync18(path6, "utf-8"));
|
|
10522
10864
|
} catch {
|
|
10523
10865
|
return null;
|
|
10524
10866
|
}
|
|
@@ -10526,9 +10868,9 @@ function loadJsonFile(path6) {
|
|
|
10526
10868
|
function getConfigPaths2() {
|
|
10527
10869
|
const cwd = process.cwd();
|
|
10528
10870
|
return {
|
|
10529
|
-
project:
|
|
10530
|
-
user:
|
|
10531
|
-
opencode:
|
|
10871
|
+
project: join36(cwd, ".opencode", "oh-my-opencode.json"),
|
|
10872
|
+
user: join36(homedir14(), ".config", "opencode", "oh-my-opencode.json"),
|
|
10873
|
+
opencode: join36(homedir14(), ".config", "opencode", "opencode.json")
|
|
10532
10874
|
};
|
|
10533
10875
|
}
|
|
10534
10876
|
function loadAllConfigs() {
|
|
@@ -10621,7 +10963,7 @@ function isServerInstalled(command) {
|
|
|
10621
10963
|
const pathEnv = process.env.PATH || "";
|
|
10622
10964
|
const paths = pathEnv.split(":");
|
|
10623
10965
|
for (const p of paths) {
|
|
10624
|
-
if (
|
|
10966
|
+
if (existsSync27(join36(p, cmd))) {
|
|
10625
10967
|
return true;
|
|
10626
10968
|
}
|
|
10627
10969
|
}
|
|
@@ -10671,7 +11013,7 @@ function getAllServers() {
|
|
|
10671
11013
|
}
|
|
10672
11014
|
// src/tools/lsp/client.ts
|
|
10673
11015
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
10674
|
-
import { readFileSync as
|
|
11016
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
10675
11017
|
import { extname, resolve as resolve5 } from "path";
|
|
10676
11018
|
class LSPServerManager {
|
|
10677
11019
|
static instance;
|
|
@@ -10680,6 +11022,36 @@ class LSPServerManager {
|
|
|
10680
11022
|
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
10681
11023
|
constructor() {
|
|
10682
11024
|
this.startCleanupTimer();
|
|
11025
|
+
this.registerProcessCleanup();
|
|
11026
|
+
}
|
|
11027
|
+
registerProcessCleanup() {
|
|
11028
|
+
const cleanup = () => {
|
|
11029
|
+
for (const [, managed] of this.clients) {
|
|
11030
|
+
try {
|
|
11031
|
+
managed.client.stop();
|
|
11032
|
+
} catch {}
|
|
11033
|
+
}
|
|
11034
|
+
this.clients.clear();
|
|
11035
|
+
if (this.cleanupInterval) {
|
|
11036
|
+
clearInterval(this.cleanupInterval);
|
|
11037
|
+
this.cleanupInterval = null;
|
|
11038
|
+
}
|
|
11039
|
+
};
|
|
11040
|
+
process.on("exit", cleanup);
|
|
11041
|
+
process.on("SIGINT", () => {
|
|
11042
|
+
cleanup();
|
|
11043
|
+
process.exit(0);
|
|
11044
|
+
});
|
|
11045
|
+
process.on("SIGTERM", () => {
|
|
11046
|
+
cleanup();
|
|
11047
|
+
process.exit(0);
|
|
11048
|
+
});
|
|
11049
|
+
if (process.platform === "win32") {
|
|
11050
|
+
process.on("SIGBREAK", () => {
|
|
11051
|
+
cleanup();
|
|
11052
|
+
process.exit(0);
|
|
11053
|
+
});
|
|
11054
|
+
}
|
|
10683
11055
|
}
|
|
10684
11056
|
static getInstance() {
|
|
10685
11057
|
if (!LSPServerManager.instance) {
|
|
@@ -11071,7 +11443,7 @@ ${msg}`);
|
|
|
11071
11443
|
const absPath = resolve5(filePath);
|
|
11072
11444
|
if (this.openedFiles.has(absPath))
|
|
11073
11445
|
return;
|
|
11074
|
-
const text =
|
|
11446
|
+
const text = readFileSync19(absPath, "utf-8");
|
|
11075
11447
|
const ext = extname(absPath);
|
|
11076
11448
|
const languageId = getLanguageId(ext);
|
|
11077
11449
|
this.notify("textDocument/didOpen", {
|
|
@@ -11186,16 +11558,16 @@ ${msg}`);
|
|
|
11186
11558
|
}
|
|
11187
11559
|
// src/tools/lsp/utils.ts
|
|
11188
11560
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
11189
|
-
import { existsSync as
|
|
11561
|
+
import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
11190
11562
|
function findWorkspaceRoot(filePath) {
|
|
11191
11563
|
let dir = resolve6(filePath);
|
|
11192
|
-
if (!
|
|
11564
|
+
if (!existsSync28(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
11193
11565
|
dir = __require("path").dirname(dir);
|
|
11194
11566
|
}
|
|
11195
11567
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
11196
11568
|
while (dir !== "/") {
|
|
11197
11569
|
for (const marker of markers) {
|
|
11198
|
-
if (
|
|
11570
|
+
if (existsSync28(__require("path").join(dir, marker))) {
|
|
11199
11571
|
return dir;
|
|
11200
11572
|
}
|
|
11201
11573
|
}
|
|
@@ -11353,7 +11725,7 @@ function formatCodeActions(actions) {
|
|
|
11353
11725
|
}
|
|
11354
11726
|
function applyTextEditsToFile(filePath, edits) {
|
|
11355
11727
|
try {
|
|
11356
|
-
let content =
|
|
11728
|
+
let content = readFileSync20(filePath, "utf-8");
|
|
11357
11729
|
const lines = content.split(`
|
|
11358
11730
|
`);
|
|
11359
11731
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -11378,7 +11750,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
11378
11750
|
`));
|
|
11379
11751
|
}
|
|
11380
11752
|
}
|
|
11381
|
-
|
|
11753
|
+
writeFileSync11(filePath, lines.join(`
|
|
11382
11754
|
`), "utf-8");
|
|
11383
11755
|
return { success: true, editCount: edits.length };
|
|
11384
11756
|
} catch (err) {
|
|
@@ -11409,7 +11781,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
11409
11781
|
if (change.kind === "create") {
|
|
11410
11782
|
try {
|
|
11411
11783
|
const filePath = change.uri.replace("file://", "");
|
|
11412
|
-
|
|
11784
|
+
writeFileSync11(filePath, "", "utf-8");
|
|
11413
11785
|
result.filesModified.push(filePath);
|
|
11414
11786
|
} catch (err) {
|
|
11415
11787
|
result.success = false;
|
|
@@ -11419,8 +11791,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
11419
11791
|
try {
|
|
11420
11792
|
const oldPath = change.oldUri.replace("file://", "");
|
|
11421
11793
|
const newPath = change.newUri.replace("file://", "");
|
|
11422
|
-
const content =
|
|
11423
|
-
|
|
11794
|
+
const content = readFileSync20(oldPath, "utf-8");
|
|
11795
|
+
writeFileSync11(newPath, content, "utf-8");
|
|
11424
11796
|
__require("fs").unlinkSync(oldPath);
|
|
11425
11797
|
result.filesModified.push(newPath);
|
|
11426
11798
|
} catch (err) {
|
|
@@ -24120,13 +24492,13 @@ var lsp_code_action_resolve = tool({
|
|
|
24120
24492
|
});
|
|
24121
24493
|
// src/tools/ast-grep/constants.ts
|
|
24122
24494
|
import { createRequire as createRequire4 } from "module";
|
|
24123
|
-
import { dirname as dirname6, join as
|
|
24124
|
-
import { existsSync as
|
|
24495
|
+
import { dirname as dirname6, join as join38 } from "path";
|
|
24496
|
+
import { existsSync as existsSync30, statSync as statSync4 } from "fs";
|
|
24125
24497
|
|
|
24126
24498
|
// src/tools/ast-grep/downloader.ts
|
|
24127
24499
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
24128
|
-
import { existsSync as
|
|
24129
|
-
import { join as
|
|
24500
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
24501
|
+
import { join as join37 } from "path";
|
|
24130
24502
|
import { homedir as homedir15 } from "os";
|
|
24131
24503
|
import { createRequire as createRequire3 } from "module";
|
|
24132
24504
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -24152,19 +24524,19 @@ var PLATFORM_MAP2 = {
|
|
|
24152
24524
|
function getCacheDir3() {
|
|
24153
24525
|
if (process.platform === "win32") {
|
|
24154
24526
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
24155
|
-
const base2 = localAppData ||
|
|
24156
|
-
return
|
|
24527
|
+
const base2 = localAppData || join37(homedir15(), "AppData", "Local");
|
|
24528
|
+
return join37(base2, "oh-my-opencode", "bin");
|
|
24157
24529
|
}
|
|
24158
24530
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
24159
|
-
const base = xdgCache2 ||
|
|
24160
|
-
return
|
|
24531
|
+
const base = xdgCache2 || join37(homedir15(), ".cache");
|
|
24532
|
+
return join37(base, "oh-my-opencode", "bin");
|
|
24161
24533
|
}
|
|
24162
24534
|
function getBinaryName3() {
|
|
24163
24535
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
24164
24536
|
}
|
|
24165
24537
|
function getCachedBinaryPath2() {
|
|
24166
|
-
const binaryPath =
|
|
24167
|
-
return
|
|
24538
|
+
const binaryPath = join37(getCacheDir3(), getBinaryName3());
|
|
24539
|
+
return existsSync29(binaryPath) ? binaryPath : null;
|
|
24168
24540
|
}
|
|
24169
24541
|
async function extractZip2(archivePath, destDir) {
|
|
24170
24542
|
const proc = process.platform === "win32" ? spawn5([
|
|
@@ -24190,8 +24562,8 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24190
24562
|
}
|
|
24191
24563
|
const cacheDir = getCacheDir3();
|
|
24192
24564
|
const binaryName = getBinaryName3();
|
|
24193
|
-
const binaryPath =
|
|
24194
|
-
if (
|
|
24565
|
+
const binaryPath = join37(cacheDir, binaryName);
|
|
24566
|
+
if (existsSync29(binaryPath)) {
|
|
24195
24567
|
return binaryPath;
|
|
24196
24568
|
}
|
|
24197
24569
|
const { arch, os: os4 } = platformInfo;
|
|
@@ -24199,21 +24571,21 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24199
24571
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
24200
24572
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
24201
24573
|
try {
|
|
24202
|
-
if (!
|
|
24574
|
+
if (!existsSync29(cacheDir)) {
|
|
24203
24575
|
mkdirSync10(cacheDir, { recursive: true });
|
|
24204
24576
|
}
|
|
24205
24577
|
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
24206
24578
|
if (!response2.ok) {
|
|
24207
24579
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
24208
24580
|
}
|
|
24209
|
-
const archivePath =
|
|
24581
|
+
const archivePath = join37(cacheDir, assetName);
|
|
24210
24582
|
const arrayBuffer = await response2.arrayBuffer();
|
|
24211
24583
|
await Bun.write(archivePath, arrayBuffer);
|
|
24212
24584
|
await extractZip2(archivePath, cacheDir);
|
|
24213
|
-
if (
|
|
24585
|
+
if (existsSync29(archivePath)) {
|
|
24214
24586
|
unlinkSync9(archivePath);
|
|
24215
24587
|
}
|
|
24216
|
-
if (process.platform !== "win32" &&
|
|
24588
|
+
if (process.platform !== "win32" && existsSync29(binaryPath)) {
|
|
24217
24589
|
chmodSync2(binaryPath, 493);
|
|
24218
24590
|
}
|
|
24219
24591
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -24264,8 +24636,8 @@ function findSgCliPathSync() {
|
|
|
24264
24636
|
const require2 = createRequire4(import.meta.url);
|
|
24265
24637
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
24266
24638
|
const cliDir = dirname6(cliPkgPath);
|
|
24267
|
-
const sgPath =
|
|
24268
|
-
if (
|
|
24639
|
+
const sgPath = join38(cliDir, binaryName);
|
|
24640
|
+
if (existsSync30(sgPath) && isValidBinary(sgPath)) {
|
|
24269
24641
|
return sgPath;
|
|
24270
24642
|
}
|
|
24271
24643
|
} catch {}
|
|
@@ -24276,8 +24648,8 @@ function findSgCliPathSync() {
|
|
|
24276
24648
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
24277
24649
|
const pkgDir = dirname6(pkgPath);
|
|
24278
24650
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
24279
|
-
const binaryPath =
|
|
24280
|
-
if (
|
|
24651
|
+
const binaryPath = join38(pkgDir, astGrepName);
|
|
24652
|
+
if (existsSync30(binaryPath) && isValidBinary(binaryPath)) {
|
|
24281
24653
|
return binaryPath;
|
|
24282
24654
|
}
|
|
24283
24655
|
} catch {}
|
|
@@ -24285,7 +24657,7 @@ function findSgCliPathSync() {
|
|
|
24285
24657
|
if (process.platform === "darwin") {
|
|
24286
24658
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
24287
24659
|
for (const path6 of homebrewPaths) {
|
|
24288
|
-
if (
|
|
24660
|
+
if (existsSync30(path6) && isValidBinary(path6)) {
|
|
24289
24661
|
return path6;
|
|
24290
24662
|
}
|
|
24291
24663
|
}
|
|
@@ -24341,11 +24713,11 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
24341
24713
|
|
|
24342
24714
|
// src/tools/ast-grep/cli.ts
|
|
24343
24715
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
24344
|
-
import { existsSync as
|
|
24716
|
+
import { existsSync as existsSync31 } from "fs";
|
|
24345
24717
|
var resolvedCliPath3 = null;
|
|
24346
24718
|
var initPromise2 = null;
|
|
24347
24719
|
async function getAstGrepPath() {
|
|
24348
|
-
if (resolvedCliPath3 !== null &&
|
|
24720
|
+
if (resolvedCliPath3 !== null && existsSync31(resolvedCliPath3)) {
|
|
24349
24721
|
return resolvedCliPath3;
|
|
24350
24722
|
}
|
|
24351
24723
|
if (initPromise2) {
|
|
@@ -24353,7 +24725,7 @@ async function getAstGrepPath() {
|
|
|
24353
24725
|
}
|
|
24354
24726
|
initPromise2 = (async () => {
|
|
24355
24727
|
const syncPath = findSgCliPathSync();
|
|
24356
|
-
if (syncPath &&
|
|
24728
|
+
if (syncPath && existsSync31(syncPath)) {
|
|
24357
24729
|
resolvedCliPath3 = syncPath;
|
|
24358
24730
|
setSgCliPath(syncPath);
|
|
24359
24731
|
return syncPath;
|
|
@@ -24387,7 +24759,7 @@ async function runSg(options) {
|
|
|
24387
24759
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
24388
24760
|
args.push(...paths);
|
|
24389
24761
|
let cliPath = getSgCliPath();
|
|
24390
|
-
if (!
|
|
24762
|
+
if (!existsSync31(cliPath) && cliPath !== "sg") {
|
|
24391
24763
|
const downloadedPath = await getAstGrepPath();
|
|
24392
24764
|
if (downloadedPath) {
|
|
24393
24765
|
cliPath = downloadedPath;
|
|
@@ -24651,24 +25023,24 @@ var ast_grep_replace = tool({
|
|
|
24651
25023
|
var {spawn: spawn7 } = globalThis.Bun;
|
|
24652
25024
|
|
|
24653
25025
|
// src/tools/grep/constants.ts
|
|
24654
|
-
import { existsSync as
|
|
24655
|
-
import { join as
|
|
25026
|
+
import { existsSync as existsSync33 } from "fs";
|
|
25027
|
+
import { join as join40, dirname as dirname7 } from "path";
|
|
24656
25028
|
import { spawnSync } from "child_process";
|
|
24657
25029
|
|
|
24658
25030
|
// src/tools/grep/downloader.ts
|
|
24659
|
-
import { existsSync as
|
|
24660
|
-
import { join as
|
|
25031
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync9 } from "fs";
|
|
25032
|
+
import { join as join39 } from "path";
|
|
24661
25033
|
function getInstallDir() {
|
|
24662
25034
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
24663
|
-
return
|
|
25035
|
+
return join39(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
24664
25036
|
}
|
|
24665
25037
|
function getRgPath() {
|
|
24666
25038
|
const isWindows2 = process.platform === "win32";
|
|
24667
|
-
return
|
|
25039
|
+
return join39(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
24668
25040
|
}
|
|
24669
25041
|
function getInstalledRipgrepPath() {
|
|
24670
25042
|
const rgPath = getRgPath();
|
|
24671
|
-
return
|
|
25043
|
+
return existsSync32(rgPath) ? rgPath : null;
|
|
24672
25044
|
}
|
|
24673
25045
|
|
|
24674
25046
|
// src/tools/grep/constants.ts
|
|
@@ -24691,13 +25063,13 @@ function getOpenCodeBundledRg() {
|
|
|
24691
25063
|
const isWindows2 = process.platform === "win32";
|
|
24692
25064
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
24693
25065
|
const candidates = [
|
|
24694
|
-
|
|
24695
|
-
|
|
24696
|
-
|
|
24697
|
-
|
|
25066
|
+
join40(execDir, rgName),
|
|
25067
|
+
join40(execDir, "bin", rgName),
|
|
25068
|
+
join40(execDir, "..", "bin", rgName),
|
|
25069
|
+
join40(execDir, "..", "libexec", rgName)
|
|
24698
25070
|
];
|
|
24699
25071
|
for (const candidate of candidates) {
|
|
24700
|
-
if (
|
|
25072
|
+
if (existsSync33(candidate)) {
|
|
24701
25073
|
return candidate;
|
|
24702
25074
|
}
|
|
24703
25075
|
}
|
|
@@ -25100,22 +25472,22 @@ var glob = tool({
|
|
|
25100
25472
|
}
|
|
25101
25473
|
});
|
|
25102
25474
|
// src/tools/slashcommand/tools.ts
|
|
25103
|
-
import { existsSync as
|
|
25475
|
+
import { existsSync as existsSync34, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
|
|
25104
25476
|
import { homedir as homedir16 } from "os";
|
|
25105
|
-
import { join as
|
|
25477
|
+
import { join as join41, basename as basename3, dirname as dirname8 } from "path";
|
|
25106
25478
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
25107
|
-
if (!
|
|
25479
|
+
if (!existsSync34(commandsDir)) {
|
|
25108
25480
|
return [];
|
|
25109
25481
|
}
|
|
25110
|
-
const entries =
|
|
25482
|
+
const entries = readdirSync10(commandsDir, { withFileTypes: true });
|
|
25111
25483
|
const commands = [];
|
|
25112
25484
|
for (const entry of entries) {
|
|
25113
25485
|
if (!isMarkdownFile(entry))
|
|
25114
25486
|
continue;
|
|
25115
|
-
const commandPath =
|
|
25487
|
+
const commandPath = join41(commandsDir, entry.name);
|
|
25116
25488
|
const commandName = basename3(entry.name, ".md");
|
|
25117
25489
|
try {
|
|
25118
|
-
const content =
|
|
25490
|
+
const content = readFileSync21(commandPath, "utf-8");
|
|
25119
25491
|
const { data, body } = parseFrontmatter(content);
|
|
25120
25492
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
25121
25493
|
const metadata = {
|
|
@@ -25140,10 +25512,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
25140
25512
|
return commands;
|
|
25141
25513
|
}
|
|
25142
25514
|
function discoverCommandsSync() {
|
|
25143
|
-
const userCommandsDir =
|
|
25144
|
-
const projectCommandsDir =
|
|
25145
|
-
const opencodeGlobalDir =
|
|
25146
|
-
const opencodeProjectDir =
|
|
25515
|
+
const userCommandsDir = join41(homedir16(), ".claude", "commands");
|
|
25516
|
+
const projectCommandsDir = join41(process.cwd(), ".claude", "commands");
|
|
25517
|
+
const opencodeGlobalDir = join41(homedir16(), ".config", "opencode", "command");
|
|
25518
|
+
const opencodeProjectDir = join41(process.cwd(), ".opencode", "command");
|
|
25147
25519
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
25148
25520
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
25149
25521
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -25275,9 +25647,9 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
25275
25647
|
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
25276
25648
|
});
|
|
25277
25649
|
// src/tools/skill/tools.ts
|
|
25278
|
-
import { existsSync as
|
|
25650
|
+
import { existsSync as existsSync35, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
|
|
25279
25651
|
import { homedir as homedir17 } from "os";
|
|
25280
|
-
import { join as
|
|
25652
|
+
import { join as join42, basename as basename4 } from "path";
|
|
25281
25653
|
function parseSkillFrontmatter(data) {
|
|
25282
25654
|
return {
|
|
25283
25655
|
name: typeof data.name === "string" ? data.name : "",
|
|
@@ -25288,22 +25660,22 @@ function parseSkillFrontmatter(data) {
|
|
|
25288
25660
|
};
|
|
25289
25661
|
}
|
|
25290
25662
|
function discoverSkillsFromDir(skillsDir, scope) {
|
|
25291
|
-
if (!
|
|
25663
|
+
if (!existsSync35(skillsDir)) {
|
|
25292
25664
|
return [];
|
|
25293
25665
|
}
|
|
25294
|
-
const entries =
|
|
25666
|
+
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
25295
25667
|
const skills = [];
|
|
25296
25668
|
for (const entry of entries) {
|
|
25297
25669
|
if (entry.name.startsWith("."))
|
|
25298
25670
|
continue;
|
|
25299
|
-
const skillPath =
|
|
25671
|
+
const skillPath = join42(skillsDir, entry.name);
|
|
25300
25672
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25301
25673
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25302
|
-
const skillMdPath =
|
|
25303
|
-
if (!
|
|
25674
|
+
const skillMdPath = join42(resolvedPath, "SKILL.md");
|
|
25675
|
+
if (!existsSync35(skillMdPath))
|
|
25304
25676
|
continue;
|
|
25305
25677
|
try {
|
|
25306
|
-
const content =
|
|
25678
|
+
const content = readFileSync22(skillMdPath, "utf-8");
|
|
25307
25679
|
const { data } = parseFrontmatter(content);
|
|
25308
25680
|
skills.push({
|
|
25309
25681
|
name: data.name || entry.name,
|
|
@@ -25318,8 +25690,8 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
25318
25690
|
return skills;
|
|
25319
25691
|
}
|
|
25320
25692
|
function discoverSkillsSync() {
|
|
25321
|
-
const userSkillsDir =
|
|
25322
|
-
const projectSkillsDir =
|
|
25693
|
+
const userSkillsDir = join42(homedir17(), ".claude", "skills");
|
|
25694
|
+
const projectSkillsDir = join42(process.cwd(), ".claude", "skills");
|
|
25323
25695
|
const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
|
|
25324
25696
|
const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
|
|
25325
25697
|
return [...projectSkills, ...userSkills];
|
|
@@ -25329,12 +25701,12 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
|
|
|
25329
25701
|
`);
|
|
25330
25702
|
async function parseSkillMd(skillPath) {
|
|
25331
25703
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25332
|
-
const skillMdPath =
|
|
25333
|
-
if (!
|
|
25704
|
+
const skillMdPath = join42(resolvedPath, "SKILL.md");
|
|
25705
|
+
if (!existsSync35(skillMdPath)) {
|
|
25334
25706
|
return null;
|
|
25335
25707
|
}
|
|
25336
25708
|
try {
|
|
25337
|
-
let content =
|
|
25709
|
+
let content = readFileSync22(skillMdPath, "utf-8");
|
|
25338
25710
|
content = await resolveCommandsInText(content);
|
|
25339
25711
|
const { data, body } = parseFrontmatter(content);
|
|
25340
25712
|
const frontmatter2 = parseSkillFrontmatter(data);
|
|
@@ -25345,12 +25717,12 @@ async function parseSkillMd(skillPath) {
|
|
|
25345
25717
|
allowedTools: frontmatter2["allowed-tools"],
|
|
25346
25718
|
metadata: frontmatter2.metadata
|
|
25347
25719
|
};
|
|
25348
|
-
const referencesDir =
|
|
25349
|
-
const scriptsDir =
|
|
25350
|
-
const assetsDir =
|
|
25351
|
-
const references =
|
|
25352
|
-
const scripts =
|
|
25353
|
-
const assets =
|
|
25720
|
+
const referencesDir = join42(resolvedPath, "references");
|
|
25721
|
+
const scriptsDir = join42(resolvedPath, "scripts");
|
|
25722
|
+
const assetsDir = join42(resolvedPath, "assets");
|
|
25723
|
+
const references = existsSync35(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
|
|
25724
|
+
const scripts = existsSync35(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
|
|
25725
|
+
const assets = existsSync35(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
|
|
25354
25726
|
return {
|
|
25355
25727
|
name: metadata.name,
|
|
25356
25728
|
path: resolvedPath,
|
|
@@ -25366,15 +25738,15 @@ async function parseSkillMd(skillPath) {
|
|
|
25366
25738
|
}
|
|
25367
25739
|
}
|
|
25368
25740
|
async function discoverSkillsFromDirAsync(skillsDir) {
|
|
25369
|
-
if (!
|
|
25741
|
+
if (!existsSync35(skillsDir)) {
|
|
25370
25742
|
return [];
|
|
25371
25743
|
}
|
|
25372
|
-
const entries =
|
|
25744
|
+
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
25373
25745
|
const skills = [];
|
|
25374
25746
|
for (const entry of entries) {
|
|
25375
25747
|
if (entry.name.startsWith("."))
|
|
25376
25748
|
continue;
|
|
25377
|
-
const skillPath =
|
|
25749
|
+
const skillPath = join42(skillsDir, entry.name);
|
|
25378
25750
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25379
25751
|
const skillInfo = await parseSkillMd(skillPath);
|
|
25380
25752
|
if (skillInfo) {
|
|
@@ -25385,8 +25757,8 @@ async function discoverSkillsFromDirAsync(skillsDir) {
|
|
|
25385
25757
|
return skills;
|
|
25386
25758
|
}
|
|
25387
25759
|
async function discoverSkills() {
|
|
25388
|
-
const userSkillsDir =
|
|
25389
|
-
const projectSkillsDir =
|
|
25760
|
+
const userSkillsDir = join42(homedir17(), ".claude", "skills");
|
|
25761
|
+
const projectSkillsDir = join42(process.cwd(), ".claude", "skills");
|
|
25390
25762
|
const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
|
|
25391
25763
|
const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
|
|
25392
25764
|
return [...projectSkills, ...userSkills];
|
|
@@ -25415,9 +25787,9 @@ async function loadSkillWithReferences(skill, includeRefs) {
|
|
|
25415
25787
|
const referencesLoaded = [];
|
|
25416
25788
|
if (includeRefs && skill.references.length > 0) {
|
|
25417
25789
|
for (const ref of skill.references) {
|
|
25418
|
-
const refPath =
|
|
25790
|
+
const refPath = join42(skill.path, "references", ref);
|
|
25419
25791
|
try {
|
|
25420
|
-
let content =
|
|
25792
|
+
let content = readFileSync22(refPath, "utf-8");
|
|
25421
25793
|
content = await resolveCommandsInText(content);
|
|
25422
25794
|
referencesLoaded.push({ path: ref, content });
|
|
25423
25795
|
} catch {}
|
|
@@ -25689,12 +26061,15 @@ Arguments:
|
|
|
25689
26061
|
- timeout: Max wait time in ms when blocking (default: 60000, max: 600000)
|
|
25690
26062
|
|
|
25691
26063
|
The system automatically notifies when background tasks complete. You typically don't need block=true.`;
|
|
25692
|
-
var BACKGROUND_CANCEL_DESCRIPTION = `Cancel
|
|
26064
|
+
var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s).
|
|
25693
26065
|
|
|
25694
26066
|
Only works for tasks with status "running". Aborts the background session and marks the task as cancelled.
|
|
25695
26067
|
|
|
25696
26068
|
Arguments:
|
|
25697
|
-
- taskId:
|
|
26069
|
+
- taskId: Task ID to cancel (optional if all=true)
|
|
26070
|
+
- all: Set to true to cancel ALL running background tasks at once (default: false)
|
|
26071
|
+
|
|
26072
|
+
**Cleanup Before Answer**: When you have gathered sufficient information and are ready to provide your final answer to the user, use \`all=true\` to cancel ALL running background tasks first, then deliver your response. This conserves resources and ensures clean workflow completion.`;
|
|
25698
26073
|
|
|
25699
26074
|
// src/tools/background-task/tools.ts
|
|
25700
26075
|
function formatDuration(start, end) {
|
|
@@ -25909,10 +26284,35 @@ function createBackgroundCancel(manager, client2) {
|
|
|
25909
26284
|
return tool({
|
|
25910
26285
|
description: BACKGROUND_CANCEL_DESCRIPTION,
|
|
25911
26286
|
args: {
|
|
25912
|
-
taskId: tool.schema.string().describe("Task ID to cancel")
|
|
26287
|
+
taskId: tool.schema.string().optional().describe("Task ID to cancel (required if all=false)"),
|
|
26288
|
+
all: tool.schema.boolean().optional().describe("Cancel all running background tasks (default: false)")
|
|
25913
26289
|
},
|
|
25914
|
-
async execute(args) {
|
|
26290
|
+
async execute(args, toolContext) {
|
|
25915
26291
|
try {
|
|
26292
|
+
const cancelAll = args.all === true;
|
|
26293
|
+
if (!cancelAll && !args.taskId) {
|
|
26294
|
+
return `\u274C Invalid arguments: Either provide a taskId or set all=true to cancel all running tasks.`;
|
|
26295
|
+
}
|
|
26296
|
+
if (cancelAll) {
|
|
26297
|
+
const tasks = manager.getTasksByParentSession(toolContext.sessionID);
|
|
26298
|
+
const runningTasks = tasks.filter((t) => t.status === "running");
|
|
26299
|
+
if (runningTasks.length === 0) {
|
|
26300
|
+
return `\u2705 No running background tasks to cancel.`;
|
|
26301
|
+
}
|
|
26302
|
+
const results = [];
|
|
26303
|
+
for (const task2 of runningTasks) {
|
|
26304
|
+
client2.session.abort({
|
|
26305
|
+
path: { id: task2.sessionID }
|
|
26306
|
+
}).catch(() => {});
|
|
26307
|
+
task2.status = "cancelled";
|
|
26308
|
+
task2.completedAt = new Date;
|
|
26309
|
+
results.push(`- ${task2.id}: ${task2.description}`);
|
|
26310
|
+
}
|
|
26311
|
+
return `\u2705 Cancelled ${runningTasks.length} background task(s):
|
|
26312
|
+
|
|
26313
|
+
${results.join(`
|
|
26314
|
+
`)}`;
|
|
26315
|
+
}
|
|
25916
26316
|
const task = manager.getTask(args.taskId);
|
|
25917
26317
|
if (!task) {
|
|
25918
26318
|
return `\u274C Task not found: ${args.taskId}`;
|
|
@@ -26213,17 +26613,17 @@ var builtinTools = {
|
|
|
26213
26613
|
skill
|
|
26214
26614
|
};
|
|
26215
26615
|
// src/features/background-agent/manager.ts
|
|
26216
|
-
import { existsSync as
|
|
26217
|
-
import { join as
|
|
26218
|
-
function
|
|
26219
|
-
if (!
|
|
26616
|
+
import { existsSync as existsSync36, readdirSync as readdirSync12 } from "fs";
|
|
26617
|
+
import { join as join43 } from "path";
|
|
26618
|
+
function getMessageDir4(sessionID) {
|
|
26619
|
+
if (!existsSync36(MESSAGE_STORAGE))
|
|
26220
26620
|
return null;
|
|
26221
|
-
const directPath =
|
|
26222
|
-
if (
|
|
26621
|
+
const directPath = join43(MESSAGE_STORAGE, sessionID);
|
|
26622
|
+
if (existsSync36(directPath))
|
|
26223
26623
|
return directPath;
|
|
26224
|
-
for (const dir of
|
|
26225
|
-
const sessionPath =
|
|
26226
|
-
if (
|
|
26624
|
+
for (const dir of readdirSync12(MESSAGE_STORAGE)) {
|
|
26625
|
+
const sessionPath = join43(MESSAGE_STORAGE, dir, sessionID);
|
|
26626
|
+
if (existsSync36(sessionPath))
|
|
26227
26627
|
return sessionPath;
|
|
26228
26628
|
}
|
|
26229
26629
|
return null;
|
|
@@ -26447,7 +26847,7 @@ class BackgroundManager {
|
|
|
26447
26847
|
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
26448
26848
|
setTimeout(async () => {
|
|
26449
26849
|
try {
|
|
26450
|
-
const messageDir =
|
|
26850
|
+
const messageDir = getMessageDir4(task.parentSessionID);
|
|
26451
26851
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
26452
26852
|
await this.client.session.prompt({
|
|
26453
26853
|
path: { id: task.parentSessionID },
|
|
@@ -26643,7 +27043,8 @@ var HookNameSchema = exports_external.enum([
|
|
|
26643
27043
|
"keyword-detector",
|
|
26644
27044
|
"agent-usage-reminder",
|
|
26645
27045
|
"non-interactive-env",
|
|
26646
|
-
"interactive-bash-session"
|
|
27046
|
+
"interactive-bash-session",
|
|
27047
|
+
"empty-message-sanitizer"
|
|
26647
27048
|
]);
|
|
26648
27049
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
26649
27050
|
model: exports_external.string().optional(),
|
|
@@ -26811,13 +27212,14 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
26811
27212
|
const agentUsageReminder = isHookEnabled("agent-usage-reminder") ? createAgentUsageReminderHook(ctx) : null;
|
|
26812
27213
|
const nonInteractiveEnv = isHookEnabled("non-interactive-env") ? createNonInteractiveEnvHook(ctx) : null;
|
|
26813
27214
|
const interactiveBashSession = isHookEnabled("interactive-bash-session") ? createInteractiveBashSessionHook(ctx) : null;
|
|
27215
|
+
const emptyMessageSanitizer = isHookEnabled("empty-message-sanitizer") ? createEmptyMessageSanitizerHook() : null;
|
|
26814
27216
|
updateTerminalTitle({ sessionId: "main" });
|
|
26815
27217
|
const backgroundManager = new BackgroundManager(ctx);
|
|
26816
27218
|
const backgroundNotificationHook = isHookEnabled("background-notification") ? createBackgroundNotificationHook(backgroundManager) : null;
|
|
26817
27219
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
|
26818
27220
|
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
|
26819
27221
|
const lookAt = createLookAt(ctx);
|
|
26820
|
-
const googleAuthHooks = pluginConfig.google_auth ? await createGoogleAntigravityAuthPlugin(ctx) : null;
|
|
27222
|
+
const googleAuthHooks = pluginConfig.google_auth !== false ? await createGoogleAntigravityAuthPlugin(ctx) : null;
|
|
26821
27223
|
const tmuxAvailable = await getTmuxPath();
|
|
26822
27224
|
return {
|
|
26823
27225
|
...googleAuthHooks ? { auth: googleAuthHooks.auth } : {},
|
|
@@ -26832,6 +27234,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
26832
27234
|
await claudeCodeHooks["chat.message"]?.(input, output);
|
|
26833
27235
|
await keywordDetector?.["chat.message"]?.(input, output);
|
|
26834
27236
|
},
|
|
27237
|
+
"experimental.chat.messages.transform": async (input, output) => {
|
|
27238
|
+
await emptyMessageSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
|
|
27239
|
+
},
|
|
26835
27240
|
config: async (config3) => {
|
|
26836
27241
|
const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory);
|
|
26837
27242
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|