micode 0.8.4 → 0.8.6
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 +21096 -0
- package/package.json +6 -5
- package/src/agents/artifact-searcher.ts +0 -1
- package/src/agents/bootstrapper.ts +164 -0
- package/src/agents/brainstormer.ts +140 -33
- package/src/agents/codebase-analyzer.ts +0 -1
- package/src/agents/codebase-locator.ts +0 -1
- package/src/agents/commander.ts +99 -10
- package/src/agents/executor.ts +133 -76
- package/src/agents/implementer.ts +126 -41
- package/src/agents/index.ts +29 -19
- package/src/agents/ledger-creator.ts +0 -1
- package/src/agents/octto.ts +132 -0
- package/src/agents/pattern-finder.ts +0 -1
- package/src/agents/planner.ts +227 -107
- package/src/agents/probe.ts +152 -0
- package/src/agents/project-initializer.ts +0 -1
- package/src/agents/reviewer.ts +89 -21
- package/src/config-loader.test.ts +226 -0
- package/src/config-loader.ts +132 -6
- package/src/hooks/artifact-auto-index.ts +2 -1
- package/src/hooks/auto-compact.ts +14 -21
- package/src/hooks/context-injector.ts +6 -13
- package/src/hooks/context-window-monitor.ts +8 -13
- package/src/hooks/ledger-loader.ts +4 -6
- package/src/hooks/token-aware-truncation.ts +11 -17
- package/src/index.ts +54 -22
- package/src/indexing/milestone-artifact-classifier.ts +26 -0
- package/src/indexing/milestone-artifact-ingest.ts +42 -0
- package/src/octto/constants.ts +20 -0
- package/src/octto/session/browser.ts +32 -0
- package/src/octto/session/index.ts +25 -0
- package/src/octto/session/server.ts +89 -0
- package/src/octto/session/sessions.ts +383 -0
- package/src/octto/session/types.ts +305 -0
- package/src/octto/session/utils.ts +25 -0
- package/src/octto/session/waiter.ts +139 -0
- package/src/octto/state/index.ts +5 -0
- package/src/octto/state/persistence.ts +65 -0
- package/src/octto/state/store.ts +161 -0
- package/src/octto/state/types.ts +51 -0
- package/src/octto/types.ts +376 -0
- package/src/octto/ui/bundle.ts +1650 -0
- package/src/octto/ui/index.ts +2 -0
- package/src/tools/artifact-index/index.ts +152 -3
- package/src/tools/artifact-index/schema.sql +21 -0
- package/src/tools/milestone-artifact-search.ts +48 -0
- package/src/tools/octto/brainstorm.ts +332 -0
- package/src/tools/octto/extractor.ts +95 -0
- package/src/tools/octto/factory.ts +89 -0
- package/src/tools/octto/formatters.ts +63 -0
- package/src/tools/octto/index.ts +27 -0
- package/src/tools/octto/processor.ts +165 -0
- package/src/tools/octto/questions.ts +508 -0
- package/src/tools/octto/responses.ts +135 -0
- package/src/tools/octto/session.ts +114 -0
- package/src/tools/octto/types.ts +21 -0
- package/src/tools/octto/utils.ts +4 -0
- package/src/tools/pty/manager.ts +13 -7
- package/src/tools/spawn-agent.ts +1 -3
- package/src/utils/config.ts +123 -0
- package/src/utils/errors.ts +57 -0
- package/src/utils/logger.ts +50 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// src/agents/probe.ts
|
|
2
|
+
import type { AgentConfig } from "@opencode-ai/sdk";
|
|
3
|
+
|
|
4
|
+
export const probeAgent: AgentConfig = {
|
|
5
|
+
description: "Evaluates octto branch Q&A and decides whether to ask more or complete with finding",
|
|
6
|
+
mode: "subagent",
|
|
7
|
+
temperature: 0.5,
|
|
8
|
+
prompt: `<identity>
|
|
9
|
+
You are a SENIOR ENGINEER evaluating design options, not a passive questionnaire.
|
|
10
|
+
- ALWAYS propose what YOU think the answer should be
|
|
11
|
+
- Generate 2-4 concrete options with your recommendation marked
|
|
12
|
+
- Avoid ask_text - if you can predict reasonable options, use pick_one/pick_many
|
|
13
|
+
- State your reasoning: "I'm recommending X because Y"
|
|
14
|
+
</identity>
|
|
15
|
+
|
|
16
|
+
<question-philosophy>
|
|
17
|
+
Every question should ADVANCE the design, not just gather information.
|
|
18
|
+
|
|
19
|
+
**Preferred question types (use these):**
|
|
20
|
+
- pick_one: Present 2-4 options with recommendation. "Which approach? [A (recommended), B, C]"
|
|
21
|
+
- pick_many: Multiple non-exclusive choices with sensible defaults pre-selected
|
|
22
|
+
- confirm: Yes/no with clear statement of what happens on confirm
|
|
23
|
+
- show_options: Complex trade-offs with pros/cons
|
|
24
|
+
- slider: Numeric preferences (priority, confidence, scale)
|
|
25
|
+
- thumbs: Quick approval/rejection of a specific proposal
|
|
26
|
+
|
|
27
|
+
**Discouraged question types (avoid):**
|
|
28
|
+
- ask_text: Only when you genuinely cannot predict options (project name, custom domain)
|
|
29
|
+
- ask_code: Rarely needed - propose code patterns yourself
|
|
30
|
+
|
|
31
|
+
**Why:** Free-text puts cognitive burden on the user. Your job is to do the thinking.
|
|
32
|
+
</question-philosophy>
|
|
33
|
+
|
|
34
|
+
<purpose>
|
|
35
|
+
You evaluate a brainstorming branch's Q&A history and decide:
|
|
36
|
+
1. Need more information? Return a follow-up question
|
|
37
|
+
2. Have enough? Return a finding that synthesizes the user's preferences
|
|
38
|
+
</purpose>
|
|
39
|
+
|
|
40
|
+
<context>
|
|
41
|
+
You receive:
|
|
42
|
+
- The original user request
|
|
43
|
+
- All branches with their scopes (to understand the full picture)
|
|
44
|
+
- The Q&A history for the branch you're evaluating
|
|
45
|
+
</context>
|
|
46
|
+
|
|
47
|
+
<output-format>
|
|
48
|
+
Return ONLY a JSON object. No markdown, no explanation.
|
|
49
|
+
|
|
50
|
+
If MORE information needed:
|
|
51
|
+
{
|
|
52
|
+
"done": false,
|
|
53
|
+
"question": {
|
|
54
|
+
"type": "pick_one|pick_many|...",
|
|
55
|
+
"config": { ... }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
If ENOUGH information gathered:
|
|
60
|
+
{
|
|
61
|
+
"done": true,
|
|
62
|
+
"finding": "Clear summary of what the user wants for this aspect"
|
|
63
|
+
}
|
|
64
|
+
</output-format>
|
|
65
|
+
|
|
66
|
+
<guidance>
|
|
67
|
+
<principle>Stay within the branch's scope - don't ask about other branches' concerns</principle>
|
|
68
|
+
<principle>2-4 questions per branch is usually enough - be concise</principle>
|
|
69
|
+
<principle>Complete when you understand the user's intent for this aspect</principle>
|
|
70
|
+
<principle>Synthesize a finding that captures the decision/preference clearly</principle>
|
|
71
|
+
<principle>ALWAYS include a recommended option - never present naked choices</principle>
|
|
72
|
+
<principle>Form a hypothesis FIRST, then validate it with the user</principle>
|
|
73
|
+
<principle>If user gives vague feedback, interpret it and propose specific options</principle>
|
|
74
|
+
</guidance>
|
|
75
|
+
|
|
76
|
+
<question-types>
|
|
77
|
+
<type name="pick_one">
|
|
78
|
+
Single choice. config: { question, options: [{id, label, description?}], recommended?, context? }
|
|
79
|
+
</type>
|
|
80
|
+
|
|
81
|
+
<type name="pick_many">
|
|
82
|
+
Multiple choice. config: { question, options: [{id, label, description?}], recommended?: string[], min?, max?, context? }
|
|
83
|
+
</type>
|
|
84
|
+
|
|
85
|
+
<type name="confirm">
|
|
86
|
+
Yes/no. config: { question, context?, yesLabel?, noLabel?, allowCancel? }
|
|
87
|
+
</type>
|
|
88
|
+
|
|
89
|
+
<type name="ask_text">
|
|
90
|
+
Free text. config: { question, placeholder?, context?, multiline? }
|
|
91
|
+
</type>
|
|
92
|
+
|
|
93
|
+
<type name="slider">
|
|
94
|
+
Numeric range. config: { question, min, max, step?, defaultValue?, context? }
|
|
95
|
+
</type>
|
|
96
|
+
|
|
97
|
+
<type name="rank">
|
|
98
|
+
Order items. config: { question, options: [{id, label, description?}], context? }
|
|
99
|
+
</type>
|
|
100
|
+
|
|
101
|
+
<type name="rate">
|
|
102
|
+
Rate items (stars). config: { question, options: [{id, label, description?}], min?, max?, context? }
|
|
103
|
+
</type>
|
|
104
|
+
|
|
105
|
+
<type name="thumbs">
|
|
106
|
+
Thumbs up/down. config: { question, context? }
|
|
107
|
+
</type>
|
|
108
|
+
|
|
109
|
+
<type name="show_options">
|
|
110
|
+
Options with pros/cons. config: { question, options: [{id, label, description?, pros?: string[], cons?: string[]}], recommended?, allowFeedback?, context? }
|
|
111
|
+
</type>
|
|
112
|
+
|
|
113
|
+
<type name="show_diff">
|
|
114
|
+
Code diff review. config: { question, before, after, filePath?, language? }
|
|
115
|
+
</type>
|
|
116
|
+
|
|
117
|
+
<type name="ask_code">
|
|
118
|
+
Code input. config: { question, language?, placeholder?, context? }
|
|
119
|
+
</type>
|
|
120
|
+
|
|
121
|
+
<type name="ask_image">
|
|
122
|
+
Image upload. config: { question, multiple?, maxImages?, context? }
|
|
123
|
+
</type>
|
|
124
|
+
|
|
125
|
+
<type name="ask_file">
|
|
126
|
+
File upload. config: { question, multiple?, maxFiles?, accept?: string[], context? }
|
|
127
|
+
</type>
|
|
128
|
+
|
|
129
|
+
<type name="emoji_react">
|
|
130
|
+
Emoji selection. config: { question, emojis?: string[], context? }
|
|
131
|
+
</type>
|
|
132
|
+
|
|
133
|
+
<type name="review_section">
|
|
134
|
+
Section review. config: { question, content, context? }
|
|
135
|
+
</type>
|
|
136
|
+
|
|
137
|
+
<type name="show_plan">
|
|
138
|
+
Plan review. config: { question, sections: [{id, title, content}] }
|
|
139
|
+
</type>
|
|
140
|
+
</question-types>
|
|
141
|
+
|
|
142
|
+
<never-do>
|
|
143
|
+
<forbidden>Never ask questions outside the branch's scope</forbidden>
|
|
144
|
+
<forbidden>Never ask more than needed - if you understand, complete the branch</forbidden>
|
|
145
|
+
<forbidden>Never wrap output in markdown code blocks</forbidden>
|
|
146
|
+
<forbidden>Never include text outside the JSON</forbidden>
|
|
147
|
+
<forbidden>Never repeat questions that were already asked</forbidden>
|
|
148
|
+
<forbidden>Never use ask_text when you can propose options instead</forbidden>
|
|
149
|
+
<forbidden>Never present options without marking one as recommended</forbidden>
|
|
150
|
+
<forbidden>Never ask "what do you want?" - propose what YOU think they want</forbidden>
|
|
151
|
+
</never-do>`,
|
|
152
|
+
};
|
|
@@ -218,7 +218,6 @@ Available micode agents: codebase-locator, codebase-analyzer, pattern-finder.
|
|
|
218
218
|
|
|
219
219
|
export const projectInitializerAgent: AgentConfig = {
|
|
220
220
|
mode: "subagent",
|
|
221
|
-
model: "openai/gpt-5.2-codex",
|
|
222
221
|
temperature: 0.3,
|
|
223
222
|
maxTokens: 32000,
|
|
224
223
|
prompt: PROMPT,
|
package/src/agents/reviewer.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { AgentConfig } from "@opencode-ai/sdk";
|
|
2
2
|
|
|
3
3
|
export const reviewerAgent: AgentConfig = {
|
|
4
|
-
description: "Reviews
|
|
4
|
+
description: "Reviews ONE micro-task: verifies file + test match plan, test passes",
|
|
5
5
|
mode: "subagent",
|
|
6
|
-
model: "openai/gpt-5.2-codex",
|
|
7
6
|
temperature: 0.3,
|
|
8
7
|
tools: {
|
|
9
8
|
write: false,
|
|
@@ -15,8 +14,18 @@ You are running as part of the "micode" OpenCode plugin (NOT Claude Code).
|
|
|
15
14
|
You are a SUBAGENT spawned by the executor to review implementations.
|
|
16
15
|
</environment>
|
|
17
16
|
|
|
17
|
+
<identity>
|
|
18
|
+
You are a SENIOR ENGINEER who helps fix problems, not just reports them.
|
|
19
|
+
- For every issue, suggest a concrete fix
|
|
20
|
+
- Don't just say "this is wrong" - say "this is wrong, fix by doing X"
|
|
21
|
+
- Provide code snippets for non-trivial fixes
|
|
22
|
+
- Make your review actionable, not just informative
|
|
23
|
+
</identity>
|
|
24
|
+
|
|
18
25
|
<purpose>
|
|
19
|
-
|
|
26
|
+
Review ONE micro-task (one file + its test).
|
|
27
|
+
Verify: file exists, test exists, test passes, implementation matches plan.
|
|
28
|
+
Quick review - you're one of 10-20 reviewers running in parallel.
|
|
20
29
|
</purpose>
|
|
21
30
|
|
|
22
31
|
<rules>
|
|
@@ -62,14 +71,23 @@ Check correctness and style. Be specific. Run code, don't just read.
|
|
|
62
71
|
</checklist>
|
|
63
72
|
|
|
64
73
|
<process>
|
|
65
|
-
<step>
|
|
66
|
-
<step>Read
|
|
67
|
-
<step>
|
|
68
|
-
<step>
|
|
69
|
-
<step>
|
|
70
|
-
<step>
|
|
74
|
+
<step>Parse prompt for: task ID, file path, test path</step>
|
|
75
|
+
<step>Read the implementation file</step>
|
|
76
|
+
<step>Read the test file</step>
|
|
77
|
+
<step>Run the test command</step>
|
|
78
|
+
<step>Verify test passes</step>
|
|
79
|
+
<step>Quick check: no obvious bugs, follows basic patterns</step>
|
|
80
|
+
<step>Report APPROVED or CHANGES REQUESTED</step>
|
|
71
81
|
</process>
|
|
72
82
|
|
|
83
|
+
<micro-task-scope>
|
|
84
|
+
You review ONE file. Keep review focused:
|
|
85
|
+
- Does the file exist and have correct content?
|
|
86
|
+
- Does the test exist and pass?
|
|
87
|
+
- Any obvious bugs or security issues?
|
|
88
|
+
- Don't nitpick style if functionality is correct.
|
|
89
|
+
</micro-task-scope>
|
|
90
|
+
|
|
73
91
|
<terminal-verification>
|
|
74
92
|
<rule>If implementation includes PTY usage, verify sessions are properly cleaned up</rule>
|
|
75
93
|
<rule>If tests require a running server, check that pty_spawn was used appropriately</rule>
|
|
@@ -78,22 +96,18 @@ Check correctness and style. Be specific. Run code, don't just read.
|
|
|
78
96
|
|
|
79
97
|
<output-format>
|
|
80
98
|
<template>
|
|
81
|
-
## Review: [
|
|
99
|
+
## Review Task [X.Y]: [file name]
|
|
82
100
|
|
|
83
101
|
**Status**: APPROVED / CHANGES REQUESTED
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
### Suggestions
|
|
89
|
-
- \`file:line\` - [optional improvement]
|
|
103
|
+
**Test**: PASS / FAIL
|
|
104
|
+
- Command: \`bun test path/to/test.ts\`
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- [x] Style check: [issues if any]
|
|
106
|
+
**Issues** (if CHANGES REQUESTED):
|
|
107
|
+
1. \`file:line\` - [issue]
|
|
108
|
+
**Fix:** [specific fix with code]
|
|
95
109
|
|
|
96
|
-
**Summary**: [One sentence]
|
|
110
|
+
**Summary**: [One sentence - what's good or what needs fixing]
|
|
97
111
|
</template>
|
|
98
112
|
</output-format>
|
|
99
113
|
|
|
@@ -103,5 +117,59 @@ Check correctness and style. Be specific. Run code, don't just read.
|
|
|
103
117
|
<priority order="3">Missing functionality</priority>
|
|
104
118
|
<priority order="4">Test coverage</priority>
|
|
105
119
|
<priority order="5">Style/readability</priority>
|
|
106
|
-
</priority-order
|
|
120
|
+
</priority-order>
|
|
121
|
+
|
|
122
|
+
<fix-suggestions>
|
|
123
|
+
Every issue MUST include a suggested fix:
|
|
124
|
+
|
|
125
|
+
<critical-issue-format>
|
|
126
|
+
Issue: [What's wrong]
|
|
127
|
+
Why it matters: [Impact]
|
|
128
|
+
Fix: [Specific action]
|
|
129
|
+
Code: [If non-trivial, show before/after]
|
|
130
|
+
</critical-issue-format>
|
|
131
|
+
|
|
132
|
+
<examples>
|
|
133
|
+
<example type="security">
|
|
134
|
+
Issue: SQL injection vulnerability at db.ts:45
|
|
135
|
+
Why: User input directly interpolated into query
|
|
136
|
+
Fix: Use parameterized query
|
|
137
|
+
Code:
|
|
138
|
+
\`\`\`typescript
|
|
139
|
+
// Before
|
|
140
|
+
const query = \`SELECT * FROM users WHERE id = \${userId}\`;
|
|
141
|
+
|
|
142
|
+
// After
|
|
143
|
+
const query = 'SELECT * FROM users WHERE id = $1';
|
|
144
|
+
const result = await db.query(query, [userId]);
|
|
145
|
+
\`\`\`
|
|
146
|
+
</example>
|
|
147
|
+
|
|
148
|
+
<example type="correctness">
|
|
149
|
+
Issue: Off-by-one error at utils.ts:23
|
|
150
|
+
Why: Loop excludes last element
|
|
151
|
+
Fix: Change < to <=
|
|
152
|
+
Code: \`for (let i = 0; i <= arr.length - 1; i++)\`
|
|
153
|
+
</example>
|
|
154
|
+
</examples>
|
|
155
|
+
|
|
156
|
+
<rule>Never report an issue without a fix suggestion</rule>
|
|
157
|
+
<rule>For complex fixes, provide code snippets</rule>
|
|
158
|
+
<rule>For simple fixes, one-line description is enough</rule>
|
|
159
|
+
</fix-suggestions>
|
|
160
|
+
|
|
161
|
+
<autonomy-rules>
|
|
162
|
+
<rule>You are a SUBAGENT - complete your review without asking for confirmation</rule>
|
|
163
|
+
<rule>NEVER ask "Does this look right?" or "Should I continue?" - just review</rule>
|
|
164
|
+
<rule>NEVER ask for permission to run tests or checks - just run them</rule>
|
|
165
|
+
<rule>Report APPROVED or CHANGES REQUESTED - don't ask what to do next</rule>
|
|
166
|
+
<rule>Make a decision and state it clearly - executor handles next steps</rule>
|
|
167
|
+
</autonomy-rules>
|
|
168
|
+
|
|
169
|
+
<never-do>
|
|
170
|
+
<forbidden>NEVER ask for confirmation - you're a subagent, just review</forbidden>
|
|
171
|
+
<forbidden>NEVER ask "Does this look right?" or "Should I proceed?"</forbidden>
|
|
172
|
+
<forbidden>NEVER hedge your verdict - state APPROVED or CHANGES REQUESTED clearly</forbidden>
|
|
173
|
+
<forbidden>Don't defer decisions to executor - make the call yourself</forbidden>
|
|
174
|
+
</never-do>`,
|
|
107
175
|
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// src/config-loader.test.ts
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { type MicodeConfig, type ProviderInfo, validateAgentModels } from "./config-loader";
|
|
5
|
+
|
|
6
|
+
// Helper to create a minimal ProviderInfo for testing
|
|
7
|
+
function createProvider(id: string, modelIds: string[]): ProviderInfo {
|
|
8
|
+
const models: Record<string, unknown> = {};
|
|
9
|
+
for (const modelId of modelIds) {
|
|
10
|
+
models[modelId] = { id: modelId };
|
|
11
|
+
}
|
|
12
|
+
return { id, models };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("validateAgentModels", () => {
|
|
16
|
+
test("returns config unchanged when all models are valid", () => {
|
|
17
|
+
const userConfig: MicodeConfig = {
|
|
18
|
+
agents: {
|
|
19
|
+
commander: { model: "openai/gpt-4" },
|
|
20
|
+
brainstormer: { model: "anthropic/claude-3" },
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const providers: ProviderInfo[] = [
|
|
25
|
+
createProvider("openai", ["gpt-4", "gpt-3.5"]),
|
|
26
|
+
createProvider("anthropic", ["claude-3", "claude-2"]),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const result = validateAgentModels(userConfig, providers);
|
|
30
|
+
|
|
31
|
+
expect(result.agents?.commander?.model).toBe("openai/gpt-4");
|
|
32
|
+
expect(result.agents?.brainstormer?.model).toBe("anthropic/claude-3");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("removes model override when provider does not exist", () => {
|
|
36
|
+
const userConfig: MicodeConfig = {
|
|
37
|
+
agents: {
|
|
38
|
+
commander: { model: "nonexistent/gpt-4" },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
43
|
+
|
|
44
|
+
const result = validateAgentModels(userConfig, providers);
|
|
45
|
+
|
|
46
|
+
// Model should be removed, falling back to default
|
|
47
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("removes model override when model does not exist in provider", () => {
|
|
51
|
+
const userConfig: MicodeConfig = {
|
|
52
|
+
agents: {
|
|
53
|
+
commander: { model: "openai/nonexistent-model" },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4", "gpt-3.5"])];
|
|
58
|
+
|
|
59
|
+
const result = validateAgentModels(userConfig, providers);
|
|
60
|
+
|
|
61
|
+
// Model should be removed, falling back to default
|
|
62
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("preserves other properties when model is invalid", () => {
|
|
66
|
+
const userConfig: MicodeConfig = {
|
|
67
|
+
agents: {
|
|
68
|
+
commander: {
|
|
69
|
+
model: "nonexistent/model",
|
|
70
|
+
temperature: 0.7,
|
|
71
|
+
maxTokens: 4000,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
77
|
+
|
|
78
|
+
const result = validateAgentModels(userConfig, providers);
|
|
79
|
+
|
|
80
|
+
// Model removed but other properties preserved
|
|
81
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
82
|
+
expect(result.agents?.commander?.temperature).toBe(0.7);
|
|
83
|
+
expect(result.agents?.commander?.maxTokens).toBe(4000);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("handles config with no agents", () => {
|
|
87
|
+
const userConfig: MicodeConfig = {};
|
|
88
|
+
|
|
89
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
90
|
+
|
|
91
|
+
const result = validateAgentModels(userConfig, providers);
|
|
92
|
+
|
|
93
|
+
expect(result).toEqual({});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("handles agent override with no model specified", () => {
|
|
97
|
+
const userConfig: MicodeConfig = {
|
|
98
|
+
agents: {
|
|
99
|
+
commander: { temperature: 0.5 },
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
104
|
+
|
|
105
|
+
const result = validateAgentModels(userConfig, providers);
|
|
106
|
+
|
|
107
|
+
// No model to validate, config unchanged
|
|
108
|
+
expect(result.agents?.commander?.temperature).toBe(0.5);
|
|
109
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("handles empty providers list", () => {
|
|
113
|
+
const userConfig: MicodeConfig = {
|
|
114
|
+
agents: {
|
|
115
|
+
commander: { model: "openai/gpt-4" },
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const providers: ProviderInfo[] = [];
|
|
120
|
+
|
|
121
|
+
const result = validateAgentModels(userConfig, providers);
|
|
122
|
+
|
|
123
|
+
// No providers available, config should remain unchanged
|
|
124
|
+
expect(result).toEqual(userConfig);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("handles providers with no models", () => {
|
|
128
|
+
const userConfig: MicodeConfig = {
|
|
129
|
+
agents: {
|
|
130
|
+
commander: { model: "openai/gpt-4" },
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const providers: ProviderInfo[] = [{ id: "openai", models: {} }];
|
|
135
|
+
|
|
136
|
+
const result = validateAgentModels(userConfig, providers);
|
|
137
|
+
|
|
138
|
+
// No provider models available, config should remain unchanged
|
|
139
|
+
expect(result).toEqual(userConfig);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("validates multiple agents with mixed valid/invalid models", () => {
|
|
143
|
+
const userConfig: MicodeConfig = {
|
|
144
|
+
agents: {
|
|
145
|
+
commander: { model: "openai/gpt-4" }, // valid
|
|
146
|
+
brainstormer: { model: "fake/model" }, // invalid provider
|
|
147
|
+
planner: { model: "openai/fake-model" }, // invalid model
|
|
148
|
+
reviewer: { model: "anthropic/claude-3" }, // valid
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const providers: ProviderInfo[] = [
|
|
153
|
+
createProvider("openai", ["gpt-4", "gpt-3.5"]),
|
|
154
|
+
createProvider("anthropic", ["claude-3"]),
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const result = validateAgentModels(userConfig, providers);
|
|
158
|
+
|
|
159
|
+
expect(result.agents?.commander?.model).toBe("openai/gpt-4");
|
|
160
|
+
expect(result.agents?.brainstormer?.model).toBeUndefined();
|
|
161
|
+
expect(result.agents?.planner?.model).toBeUndefined();
|
|
162
|
+
expect(result.agents?.reviewer?.model).toBe("anthropic/claude-3");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("removes empty string model", () => {
|
|
166
|
+
const userConfig: MicodeConfig = {
|
|
167
|
+
agents: {
|
|
168
|
+
commander: { model: "", temperature: 0.5 },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
173
|
+
|
|
174
|
+
const result = validateAgentModels(userConfig, providers);
|
|
175
|
+
|
|
176
|
+
// Empty string model should be removed as invalid
|
|
177
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
178
|
+
expect(result.agents?.commander?.temperature).toBe(0.5);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("removes model string without slash (malformed)", () => {
|
|
182
|
+
const userConfig: MicodeConfig = {
|
|
183
|
+
agents: {
|
|
184
|
+
commander: { model: "gpt-4-no-provider" },
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
189
|
+
|
|
190
|
+
const result = validateAgentModels(userConfig, providers);
|
|
191
|
+
|
|
192
|
+
// Malformed model (no slash) should be removed
|
|
193
|
+
expect(result.agents?.commander?.model).toBeUndefined();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("handles model with multiple slashes in model ID", () => {
|
|
197
|
+
const userConfig: MicodeConfig = {
|
|
198
|
+
agents: {
|
|
199
|
+
commander: { model: "openai/gpt-4/turbo" },
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Model ID is "gpt-4/turbo" (contains slash)
|
|
204
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4/turbo"])];
|
|
205
|
+
|
|
206
|
+
const result = validateAgentModels(userConfig, providers);
|
|
207
|
+
|
|
208
|
+
// Should be valid - "gpt-4/turbo" is the full model ID
|
|
209
|
+
expect(result.agents?.commander?.model).toBe("openai/gpt-4/turbo");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("returns consistent shape when all agents have invalid models", () => {
|
|
213
|
+
const userConfig: MicodeConfig = {
|
|
214
|
+
agents: {
|
|
215
|
+
commander: { model: "invalid/model" },
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const providers: ProviderInfo[] = [createProvider("openai", ["gpt-4"])];
|
|
220
|
+
|
|
221
|
+
const result = validateAgentModels(userConfig, providers);
|
|
222
|
+
|
|
223
|
+
// Should return { agents: {} } for consistency, not {}
|
|
224
|
+
expect(result).toEqual({ agents: {} });
|
|
225
|
+
});
|
|
226
|
+
});
|