opencodekit 0.21.4 → 0.21.5
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/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +55 -36
- package/dist/template/.opencode/agent/build.md +13 -3
- package/dist/template/.opencode/agent/explore.md +14 -0
- package/dist/template/.opencode/agent/general.md +13 -2
- package/dist/template/.opencode/agent/painter.md +9 -0
- package/dist/template/.opencode/agent/plan.md +26 -4
- package/dist/template/.opencode/agent/review.md +10 -0
- package/dist/template/.opencode/agent/scout.md +16 -1
- package/dist/template/.opencode/agent/vision.md +23 -0
- package/dist/template/.opencode/command/design.md +27 -8
- package/dist/template/.opencode/command/plan.md +22 -0
- package/dist/template/.opencode/command/ship.md +31 -5
- package/dist/template/.opencode/command/status.md +14 -5
- package/dist/template/.opencode/command/ui-review.md +38 -18
- package/dist/template/.opencode/command/ui-slop-check.md +30 -7
- package/dist/template/.opencode/command/verify.md +3 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/plugin/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +162 -168
- package/dist/template/.opencode/plugin/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +16 -16
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-chat-language-model.ts +807 -805
- package/dist/template/.opencode/plugin/sdk/copilot/chat/openai-compatible-prepare-tools.ts +77 -77
- package/dist/template/.opencode/plugin/sdk/copilot/copilot-provider.ts +75 -80
- package/dist/template/.opencode/skill/playwright/SKILL.md +51 -2
- package/dist/template/.opencode/skill/portless/SKILL.md +109 -0
- package/dist/template/.opencode/skill/terse-output-mode/SKILL.md +95 -0
- package/dist/template/.opencode/skill/think-in-code/SKILL.md +136 -0
- package/dist/template/.opencode/skill/ux-quality-gates/SKILL.md +137 -0
- package/package.json +1 -1
|
@@ -40,11 +40,12 @@ skill({ name: "beads" });
|
|
|
40
40
|
|
|
41
41
|
## Available Tools
|
|
42
42
|
|
|
43
|
-
| Tool | Use When
|
|
44
|
-
| --------------- |
|
|
45
|
-
| `br` | Task status and stats
|
|
46
|
-
| `git` | Git state and history
|
|
47
|
-
| `find_sessions` | Recent sessions
|
|
43
|
+
| Tool | Use When |
|
|
44
|
+
| --------------- | ----------------------------------------------- |
|
|
45
|
+
| `br` | Task status and stats |
|
|
46
|
+
| `git` | Git state and history |
|
|
47
|
+
| `find_sessions` | Recent sessions |
|
|
48
|
+
| `portless` | Optional read-only local URL state if installed |
|
|
48
49
|
|
|
49
50
|
## Phase 1: Gather State (Parallel)
|
|
50
51
|
|
|
@@ -62,6 +63,14 @@ git branch --show-current
|
|
|
62
63
|
git log --oneline -5
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
Optionally include Portless local URL state only if the binary is already installed:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
if command -v portless >/dev/null 2>&1; then portless list; fi
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Do not install Portless, start/stop proxies, trust CAs, sync hosts, prune/clean state, or expose LAN services from `/status`.
|
|
73
|
+
|
|
65
74
|
```typescript
|
|
66
75
|
find_sessions({ query: "<project-name or recent-bead-keywords>", limit: 5 });
|
|
67
76
|
```
|
|
@@ -13,6 +13,7 @@ model: proxypal/gemini-3-pro-preview
|
|
|
13
13
|
skill({ name: "visual-analysis" }); // Analysis framework
|
|
14
14
|
skill({ name: "accessibility-audit" }); // WCAG checklists
|
|
15
15
|
skill({ name: "frontend-design" }); // Anti-patterns, design quality
|
|
16
|
+
skill({ name: "ux-quality-gates" }); // IA, heuristics, forms, recovery, state coverage
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Input
|
|
@@ -33,31 +34,50 @@ Use the `visual-analysis` skill to perform deep analysis:
|
|
|
33
34
|
- Visual properties (colors, typography, spacing, layout)
|
|
34
35
|
- Design patterns and potential issues
|
|
35
36
|
|
|
36
|
-
### 2.
|
|
37
|
+
### 2. Usability Heuristic Pass
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
Apply `ux-quality-gates` before visual scoring. Check:
|
|
40
|
+
|
|
41
|
+
- System status is visible for async or multi-step work
|
|
42
|
+
- Labels and navigation use user vocabulary, not implementation terms
|
|
43
|
+
- Users can cancel, undo, go back, retry, or recover from failure
|
|
44
|
+
- Controls, names, and layouts are consistent across the surface
|
|
45
|
+
- Dangerous or invalid actions are prevented before they happen
|
|
46
|
+
- Options and relationships are visible without relying on memory
|
|
47
|
+
- High-volume workflows expose efficient paths where appropriate
|
|
48
|
+
- Empty/loading/error/success states are present and useful
|
|
49
|
+
|
|
50
|
+
Any failure that blocks task completion or recovery is **Critical**, even if the screen looks polished.
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
| **Color** | Palette cohesion, contrast, semantic usage, no AI slop |
|
|
44
|
-
| **Layout & Spacing** | Visual hierarchy, consistency, alignment, white space |
|
|
45
|
-
| **Interactive States** | Hover, focus, active, disabled, loading coverage |
|
|
46
|
-
| **Accessibility** | WCAG AA compliance (use `accessibility-audit` skill checklist) |
|
|
47
|
-
| **Visual Polish** | Consistency, attention to detail, motion, shadows, icons |
|
|
52
|
+
### 3. Score Categories
|
|
53
|
+
|
|
54
|
+
Rate each 1-10 with brief justification:
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
| Category | What to Evaluate |
|
|
57
|
+
| --------------------------------- | -------------------------------------------------------------- |
|
|
58
|
+
| **Information Architecture** | User vocabulary, scope clarity, relationships, navigation |
|
|
59
|
+
| **Task Flow & Recovery** | Primary action, cancellation, undo/retry, error recovery |
|
|
60
|
+
| **Forms & Data Interaction** | Labels, helper text, validation, selection, bulk actions |
|
|
61
|
+
| **Typography** | Hierarchy, readability, weight contrast, intentional choices |
|
|
62
|
+
| **Color** | Palette cohesion, contrast, semantic usage, no AI slop |
|
|
63
|
+
| **Layout & Spacing** | Visual hierarchy, consistency, alignment, white space |
|
|
64
|
+
| **Interactive States** | Hover, focus, active, disabled, loading/error/success coverage |
|
|
65
|
+
| **Accessibility & Semantic HTML** | WCAG AA compliance, native semantics, keyboard/focus behavior |
|
|
66
|
+
| **Component Consistency** | Shared token DNA: radius, height, border, shadow, states |
|
|
67
|
+
| **Visual Polish** | Consistency, attention to detail, motion, shadows, icons |
|
|
68
|
+
|
|
69
|
+
### 4. Conditional Reviews
|
|
50
70
|
|
|
51
71
|
**If `--responsive`**: Check at 375px, 768px, 1280px, 1536px+. Flag touch targets, horizontal scroll, text sizing.
|
|
52
72
|
|
|
53
73
|
**If `--dark-mode`**: Check contrast on dark backgrounds, adapted colors (not just inverted), shadow adjustments, focus visibility.
|
|
54
74
|
|
|
55
|
-
###
|
|
75
|
+
### 5. Report Findings
|
|
56
76
|
|
|
57
77
|
Group by severity:
|
|
58
78
|
|
|
59
|
-
- **Critical (Must Fix)**: Accessibility failures, broken interactions
|
|
60
|
-
- **Warning (Should Fix)**: AI slop patterns, inconsistent spacing, missing states
|
|
79
|
+
- **Critical (Must Fix)**: Accessibility failures, broken interactions, dead-end errors, unsafe destructive actions
|
|
80
|
+
- **Warning (Should Fix)**: AI slop patterns, inconsistent spacing, missing states, confusing IA/naming
|
|
61
81
|
- **Info (Nice to Have)**: Polish opportunities
|
|
62
82
|
|
|
63
83
|
For each finding: location, impact, and recommended fix.
|
|
@@ -85,7 +105,7 @@ observation({
|
|
|
85
105
|
|
|
86
106
|
## Related Commands
|
|
87
107
|
|
|
88
|
-
| Need
|
|
89
|
-
|
|
|
90
|
-
| Design from scratch
|
|
91
|
-
| Ship implementation
|
|
108
|
+
| Need | Command |
|
|
109
|
+
| ------------------- | --------- |
|
|
110
|
+
| Design from scratch | `/design` |
|
|
111
|
+
| Ship implementation | `/ship` |
|
|
@@ -15,16 +15,17 @@ Run a focused anti-slop audit against changed UI files using the frontend-design
|
|
|
15
15
|
skill({ name: "frontend-design" }); // Anti-pattern taxonomy + design references
|
|
16
16
|
skill({ name: "visual-analysis" }); // Structured visual/code analysis workflow
|
|
17
17
|
skill({ name: "accessibility-audit" }); // Keyboard/focus/contrast checks
|
|
18
|
+
skill({ name: "ux-quality-gates" }); // UX correctness gates beyond visual slop
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
## Parse Arguments
|
|
21
22
|
|
|
22
|
-
| Argument | Default | Description
|
|
23
|
-
| --------------- | ------- |
|
|
24
|
-
| `[path\|auto]` | `auto` | Specific file/dir to audit, or auto-detect changed UI files
|
|
25
|
-
| `--staged` | false | Audit staged changes only (`git diff --cached`)
|
|
26
|
-
| `--since=<ref>` | `HEAD` | Compare against ref (`main`, `HEAD~1`, commit SHA)
|
|
27
|
-
| `--full-report` | false | Include all categories even when no issues found
|
|
23
|
+
| Argument | Default | Description |
|
|
24
|
+
| --------------- | ------- | ----------------------------------------------------------- |
|
|
25
|
+
| `[path\|auto]` | `auto` | Specific file/dir to audit, or auto-detect changed UI files |
|
|
26
|
+
| `--staged` | false | Audit staged changes only (`git diff --cached`) |
|
|
27
|
+
| `--since=<ref>` | `HEAD` | Compare against ref (`main`, `HEAD~1`, commit SHA) |
|
|
28
|
+
| `--full-report` | false | Include all categories even when no issues found |
|
|
28
29
|
|
|
29
30
|
## Phase 1: Resolve Target Files
|
|
30
31
|
|
|
@@ -96,12 +97,34 @@ Evaluate each target file (or rendered screenshot if provided) against these che
|
|
|
96
97
|
- Error copy includes what happened + why + how to fix
|
|
97
98
|
- Empty states include guidance + next action
|
|
98
99
|
- Terminology is consistent (avoid mixed synonyms for same action)
|
|
100
|
+
- User-facing labels avoid implementation terms, database names, and internal acronyms
|
|
99
101
|
|
|
100
|
-
### F)
|
|
102
|
+
### F) UX Quality Gates
|
|
103
|
+
|
|
104
|
+
- One dominant filled primary action per view/section
|
|
105
|
+
- Destructive actions require explicit confirm or undo, with specific entity/count in copy
|
|
106
|
+
- No placeholder-as-label form fields
|
|
107
|
+
- Form errors are associated with controls (`aria-describedby`, `aria-invalid`, `role="alert"`)
|
|
108
|
+
- Submit/loading states prevent double-submit without layout shift
|
|
109
|
+
- Empty, loading, error, and success states exist where async/data flows exist
|
|
110
|
+
- Error toasts/banners persist long enough and include retry/undo/support where applicable
|
|
111
|
+
- Data-heavy UI distinguishes empty state from filtered no-results state
|
|
112
|
+
- Bulk actions show selected count and confirm destructive scope
|
|
113
|
+
|
|
114
|
+
### G) Accessibility Safety Nets
|
|
101
115
|
|
|
102
116
|
- Keyboard-visible focus treatment (`:focus-visible`)
|
|
103
117
|
- Contrast baseline expectations (WCAG AA)
|
|
104
118
|
- Touch targets reasonable (44x44 context where applicable)
|
|
119
|
+
- Native semantic elements are used before ARIA patches (`button`, `a`, `form`, landmarks)
|
|
120
|
+
- Heading structure has one logical `h1` per page/screen context
|
|
121
|
+
|
|
122
|
+
### H) Component Family Consistency
|
|
123
|
+
|
|
124
|
+
- Buttons and inputs in the same form share height, radius, border, and focus treatment
|
|
125
|
+
- Focus, error, disabled, and loading states use the same token logic across components
|
|
126
|
+
- No one-off radius/shadow/border width unless documented as a system-level exception
|
|
127
|
+
- Semantic color roles are consistent: success, warning, destructive, info, primary
|
|
105
128
|
|
|
106
129
|
## Phase 3: Severity and Scoring
|
|
107
130
|
|
|
@@ -13,6 +13,7 @@ Check implementation against PRD before shipping.
|
|
|
13
13
|
```typescript
|
|
14
14
|
skill({ name: "beads" });
|
|
15
15
|
skill({ name: "verification-before-completion" });
|
|
16
|
+
// If local web/browser verification needs stable URLs: skill({ name: "portless" });
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Parse Arguments
|
|
@@ -105,6 +106,8 @@ Follow the [Verification Protocol](../skill/verification-before-completion/refer
|
|
|
105
106
|
1. **Parallel**: typecheck + lint (simultaneously)
|
|
106
107
|
2. **Sequential** (after parallel passes): test, then build (ship only)
|
|
107
108
|
|
|
109
|
+
For browser/manual local-web requirements, load the [portless](../skill/portless/SKILL.md) skill when stable named URLs would improve verification. Use only read-only Portless commands unless the user explicitly approves local networking changes; a reachable Portless URL supplements, but never replaces, typecheck/lint/test/build evidence.
|
|
110
|
+
|
|
108
111
|
Report results with mode column:
|
|
109
112
|
|
|
110
113
|
```text
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,178 +1,172 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
type LanguageModelV3Prompt,
|
|
3
|
+
type SharedV3ProviderOptions,
|
|
4
|
+
UnsupportedFunctionalityError,
|
|
5
5
|
} from "@ai-sdk/provider";
|
|
6
6
|
import { convertToBase64 } from "@ai-sdk/provider-utils";
|
|
7
7
|
import type { OpenAICompatibleChatPrompt } from "./openai-compatible-api-types.js";
|
|
8
8
|
|
|
9
|
-
function getOpenAIMetadata(message: {
|
|
10
|
-
|
|
11
|
-
}) {
|
|
12
|
-
return message?.providerOptions?.copilot ?? {};
|
|
9
|
+
function getOpenAIMetadata(message: { providerOptions?: SharedV3ProviderOptions }) {
|
|
10
|
+
return message?.providerOptions?.copilot ?? {};
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
export function convertToOpenAICompatibleChatMessages(
|
|
16
|
-
|
|
14
|
+
prompt: LanguageModelV3Prompt,
|
|
17
15
|
): OpenAICompatibleChatPrompt {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return messages;
|
|
16
|
+
const messages: OpenAICompatibleChatPrompt = [];
|
|
17
|
+
for (const { role, content, ...message } of prompt) {
|
|
18
|
+
const metadata = getOpenAIMetadata({ ...message });
|
|
19
|
+
switch (role) {
|
|
20
|
+
case "system": {
|
|
21
|
+
messages.push({
|
|
22
|
+
role: "system",
|
|
23
|
+
content: content,
|
|
24
|
+
...metadata,
|
|
25
|
+
});
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
case "user": {
|
|
30
|
+
if (content.length === 1 && content[0].type === "text") {
|
|
31
|
+
messages.push({
|
|
32
|
+
role: "user",
|
|
33
|
+
content: content[0].text,
|
|
34
|
+
...getOpenAIMetadata(content[0]),
|
|
35
|
+
});
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
messages.push({
|
|
40
|
+
role: "user",
|
|
41
|
+
content: content.map((part) => {
|
|
42
|
+
const partMetadata = getOpenAIMetadata(part);
|
|
43
|
+
switch (part.type) {
|
|
44
|
+
case "text": {
|
|
45
|
+
return { type: "text" as const, text: part.text, ...partMetadata };
|
|
46
|
+
}
|
|
47
|
+
case "file": {
|
|
48
|
+
if (part.mediaType.startsWith("image/")) {
|
|
49
|
+
const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
type: "image_url" as const,
|
|
53
|
+
image_url: {
|
|
54
|
+
url:
|
|
55
|
+
part.data instanceof URL
|
|
56
|
+
? part.data.toString()
|
|
57
|
+
: `data:${mediaType};base64,${convertToBase64(part.data)}`,
|
|
58
|
+
},
|
|
59
|
+
...partMetadata,
|
|
60
|
+
};
|
|
61
|
+
} else {
|
|
62
|
+
throw new UnsupportedFunctionalityError({
|
|
63
|
+
functionality: `file part media type ${part.mediaType}`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
...metadata,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case "assistant": {
|
|
76
|
+
let text = "";
|
|
77
|
+
let reasoningText: string | undefined;
|
|
78
|
+
let reasoningOpaque: string | undefined;
|
|
79
|
+
const toolCalls: Array<{
|
|
80
|
+
id: string;
|
|
81
|
+
type: "function";
|
|
82
|
+
function: { name: string; arguments: string };
|
|
83
|
+
}> = [];
|
|
84
|
+
|
|
85
|
+
for (const part of content) {
|
|
86
|
+
const partMetadata = getOpenAIMetadata(part);
|
|
87
|
+
// Check for reasoningOpaque on any part (may be attached to text/tool-call)
|
|
88
|
+
const partOpaque = (part.providerOptions as { copilot?: { reasoningOpaque?: string } })
|
|
89
|
+
?.copilot?.reasoningOpaque;
|
|
90
|
+
if (partOpaque && !reasoningOpaque) {
|
|
91
|
+
reasoningOpaque = partOpaque;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
switch (part.type) {
|
|
95
|
+
case "text": {
|
|
96
|
+
text += part.text;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "reasoning": {
|
|
100
|
+
if (part.text) reasoningText = part.text;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "tool-call": {
|
|
104
|
+
toolCalls.push({
|
|
105
|
+
id: part.toolCallId,
|
|
106
|
+
type: "function",
|
|
107
|
+
function: {
|
|
108
|
+
name: part.toolName,
|
|
109
|
+
arguments: JSON.stringify(part.input),
|
|
110
|
+
},
|
|
111
|
+
...partMetadata,
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
messages.push({
|
|
119
|
+
role: "assistant",
|
|
120
|
+
content: text || null,
|
|
121
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
122
|
+
reasoning_text: reasoningOpaque ? reasoningText : undefined,
|
|
123
|
+
reasoning_opaque: reasoningOpaque,
|
|
124
|
+
...metadata,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case "tool": {
|
|
131
|
+
for (const toolResponse of content) {
|
|
132
|
+
if (toolResponse.type === "tool-approval-response") {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const output = toolResponse.output;
|
|
136
|
+
|
|
137
|
+
let contentValue: string;
|
|
138
|
+
switch (output.type) {
|
|
139
|
+
case "text":
|
|
140
|
+
case "error-text":
|
|
141
|
+
contentValue = output.value;
|
|
142
|
+
break;
|
|
143
|
+
case "execution-denied":
|
|
144
|
+
contentValue = output.reason ?? "Tool execution denied.";
|
|
145
|
+
break;
|
|
146
|
+
case "content":
|
|
147
|
+
case "json":
|
|
148
|
+
case "error-json":
|
|
149
|
+
contentValue = JSON.stringify(output.value);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const toolResponseMetadata = getOpenAIMetadata(toolResponse);
|
|
154
|
+
messages.push({
|
|
155
|
+
role: "tool",
|
|
156
|
+
tool_call_id: toolResponse.toolCallId,
|
|
157
|
+
content: contentValue,
|
|
158
|
+
...toolResponseMetadata,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
default: {
|
|
165
|
+
const _exhaustiveCheck: never = role;
|
|
166
|
+
throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return messages;
|
|
178
172
|
}
|
package/dist/template/.opencode/plugin/sdk/copilot/chat/map-openai-compatible-finish-reason.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LanguageModelV3FinishReason } from "@ai-sdk/provider";
|
|
2
2
|
|
|
3
3
|
export function mapOpenAICompatibleFinishReason(
|
|
4
|
-
|
|
5
|
-
):
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
4
|
+
finishReason: string | null | undefined,
|
|
5
|
+
): LanguageModelV3FinishReason["unified"] {
|
|
6
|
+
switch (finishReason) {
|
|
7
|
+
case "stop":
|
|
8
|
+
return "stop";
|
|
9
|
+
case "length":
|
|
10
|
+
return "length";
|
|
11
|
+
case "content_filter":
|
|
12
|
+
return "content-filter";
|
|
13
|
+
case "function_call":
|
|
14
|
+
case "tool_calls":
|
|
15
|
+
return "tool-calls";
|
|
16
|
+
default:
|
|
17
|
+
return "other";
|
|
18
|
+
}
|
|
19
19
|
}
|