@agentuity/opencode 0.1.15
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/AGENTS.md +40 -0
- package/README.md +113 -0
- package/dist/agents/builder.d.ts +4 -0
- package/dist/agents/builder.d.ts.map +1 -0
- package/dist/agents/builder.js +298 -0
- package/dist/agents/builder.js.map +1 -0
- package/dist/agents/expert.d.ts +4 -0
- package/dist/agents/expert.d.ts.map +1 -0
- package/dist/agents/expert.js +773 -0
- package/dist/agents/expert.js.map +1 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +40 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/lead.d.ts +4 -0
- package/dist/agents/lead.d.ts.map +1 -0
- package/dist/agents/lead.js +463 -0
- package/dist/agents/lead.js.map +1 -0
- package/dist/agents/memory.d.ts +4 -0
- package/dist/agents/memory.d.ts.map +1 -0
- package/dist/agents/memory.js +317 -0
- package/dist/agents/memory.js.map +1 -0
- package/dist/agents/reviewer.d.ts +4 -0
- package/dist/agents/reviewer.d.ts.map +1 -0
- package/dist/agents/reviewer.js +321 -0
- package/dist/agents/reviewer.js.map +1 -0
- package/dist/agents/scout.d.ts +4 -0
- package/dist/agents/scout.d.ts.map +1 -0
- package/dist/agents/scout.js +280 -0
- package/dist/agents/scout.js.map +1 -0
- package/dist/agents/types.d.ts +29 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +14 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +98 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/mcps/context7.d.ts +3 -0
- package/dist/mcps/context7.d.ts.map +1 -0
- package/dist/mcps/context7.js +7 -0
- package/dist/mcps/context7.js.map +1 -0
- package/dist/mcps/grep-app.d.ts +3 -0
- package/dist/mcps/grep-app.d.ts.map +1 -0
- package/dist/mcps/grep-app.js +7 -0
- package/dist/mcps/grep-app.js.map +1 -0
- package/dist/mcps/index.d.ts +8 -0
- package/dist/mcps/index.d.ts.map +1 -0
- package/dist/mcps/index.js +25 -0
- package/dist/mcps/index.js.map +1 -0
- package/dist/plugin/hooks/keyword.d.ts +6 -0
- package/dist/plugin/hooks/keyword.d.ts.map +1 -0
- package/dist/plugin/hooks/keyword.js +110 -0
- package/dist/plugin/hooks/keyword.js.map +1 -0
- package/dist/plugin/hooks/params.d.ts +20 -0
- package/dist/plugin/hooks/params.d.ts.map +1 -0
- package/dist/plugin/hooks/params.js +157 -0
- package/dist/plugin/hooks/params.js.map +1 -0
- package/dist/plugin/hooks/session.d.ts +6 -0
- package/dist/plugin/hooks/session.d.ts.map +1 -0
- package/dist/plugin/hooks/session.js +20 -0
- package/dist/plugin/hooks/session.js.map +1 -0
- package/dist/plugin/hooks/tools.d.ts +7 -0
- package/dist/plugin/hooks/tools.d.ts.map +1 -0
- package/dist/plugin/hooks/tools.js +111 -0
- package/dist/plugin/hooks/tools.js.map +1 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +2 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/plugin.d.ts +3 -0
- package/dist/plugin/plugin.d.ts.map +1 -0
- package/dist/plugin/plugin.js +249 -0
- package/dist/plugin/plugin.js.map +1 -0
- package/dist/services/auth.d.ts +14 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +54 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -0
- package/dist/tools/delegate.d.ts +35 -0
- package/dist/tools/delegate.d.ts.map +1 -0
- package/dist/tools/delegate.js +51 -0
- package/dist/tools/delegate.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
- package/src/agents/builder.ts +300 -0
- package/src/agents/expert.ts +775 -0
- package/src/agents/index.ts +49 -0
- package/src/agents/lead.ts +466 -0
- package/src/agents/memory.ts +320 -0
- package/src/agents/reviewer.ts +323 -0
- package/src/agents/scout.ts +283 -0
- package/src/agents/types.ts +30 -0
- package/src/config/index.ts +1 -0
- package/src/config/loader.ts +127 -0
- package/src/index.ts +24 -0
- package/src/mcps/context7.ts +8 -0
- package/src/mcps/grep-app.ts +8 -0
- package/src/mcps/index.ts +34 -0
- package/src/plugin/hooks/keyword.ts +126 -0
- package/src/plugin/hooks/params.ts +188 -0
- package/src/plugin/hooks/session.ts +27 -0
- package/src/plugin/hooks/tools.ts +127 -0
- package/src/plugin/index.ts +1 -0
- package/src/plugin/plugin.ts +280 -0
- package/src/services/auth.ts +88 -0
- package/src/services/index.ts +1 -0
- package/src/tools/delegate.ts +62 -0
- package/src/tools/index.ts +1 -0
- package/src/types.ts +131 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { PluginContext, CoderConfig } from '../../types';
|
|
2
|
+
|
|
3
|
+
export interface ParamsHooks {
|
|
4
|
+
onParams: (input: unknown, output: unknown) => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Dynamic mode triggers based on user message content.
|
|
9
|
+
*
|
|
10
|
+
* Trigger phrases are intentionally specific to avoid false positives.
|
|
11
|
+
* Single common words are avoided; multi-word phrases are preferred.
|
|
12
|
+
*/
|
|
13
|
+
const DYNAMIC_MODES = {
|
|
14
|
+
/**
|
|
15
|
+
* Creative mode: Higher temperature for brainstorming and idea generation
|
|
16
|
+
* Advertised keyword: "brainstorm"
|
|
17
|
+
*/
|
|
18
|
+
creative: {
|
|
19
|
+
triggers: [
|
|
20
|
+
'brainstorm',
|
|
21
|
+
'be creative',
|
|
22
|
+
'get creative',
|
|
23
|
+
'give me ideas',
|
|
24
|
+
'explore options',
|
|
25
|
+
'explore alternatives',
|
|
26
|
+
'think outside the box',
|
|
27
|
+
],
|
|
28
|
+
settings: {
|
|
29
|
+
temperature: 0.8,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Deep thinking mode: Maximum reasoning for complex analysis
|
|
35
|
+
* Advertised keyword: "think hard"
|
|
36
|
+
*/
|
|
37
|
+
deepThink: {
|
|
38
|
+
triggers: [
|
|
39
|
+
'think hard',
|
|
40
|
+
'think deeply',
|
|
41
|
+
'reason through this',
|
|
42
|
+
'analyze carefully',
|
|
43
|
+
'think this through',
|
|
44
|
+
'give this extra thought',
|
|
45
|
+
],
|
|
46
|
+
settings: {
|
|
47
|
+
// For Anthropic models, this enables max thinking budget
|
|
48
|
+
// These get passed through as provider options
|
|
49
|
+
thinking: {
|
|
50
|
+
type: 'enabled',
|
|
51
|
+
budgetTokens: 32000,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Thorough mode: More iterations for comprehensive work
|
|
58
|
+
* Advertised keyword: "dig deep" or "go deep"
|
|
59
|
+
*/
|
|
60
|
+
thorough: {
|
|
61
|
+
triggers: [
|
|
62
|
+
'dig deep',
|
|
63
|
+
'go deep',
|
|
64
|
+
'deep dive',
|
|
65
|
+
'take your time',
|
|
66
|
+
'be thorough',
|
|
67
|
+
'be meticulous',
|
|
68
|
+
],
|
|
69
|
+
settings: {
|
|
70
|
+
maxSteps: 50,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if message content triggers any dynamic mode
|
|
77
|
+
*/
|
|
78
|
+
function detectMode(
|
|
79
|
+
messageContent: string
|
|
80
|
+
): { mode: string; settings: Record<string, unknown> } | null {
|
|
81
|
+
const lower = messageContent.toLowerCase();
|
|
82
|
+
|
|
83
|
+
for (const [modeName, config] of Object.entries(DYNAMIC_MODES)) {
|
|
84
|
+
for (const trigger of config.triggers) {
|
|
85
|
+
if (lower.includes(trigger)) {
|
|
86
|
+
return {
|
|
87
|
+
mode: modeName,
|
|
88
|
+
settings: config.settings as Record<string, unknown>,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createParamsHooks(ctx: PluginContext, _config: CoderConfig): ParamsHooks {
|
|
98
|
+
return {
|
|
99
|
+
async onParams(input: unknown, output: unknown): Promise<void> {
|
|
100
|
+
// Input contains: sessionID, agent, model, provider, message
|
|
101
|
+
const inputObj = input as {
|
|
102
|
+
sessionID?: string;
|
|
103
|
+
agent?: string;
|
|
104
|
+
message?: { content?: string };
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Output contains: temperature, topP, topK, options
|
|
108
|
+
const outputObj = output as {
|
|
109
|
+
temperature?: number;
|
|
110
|
+
topP?: number;
|
|
111
|
+
topK?: number;
|
|
112
|
+
options?: Record<string, unknown>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Get message content for mode detection
|
|
116
|
+
const messageContent = inputObj.message?.content || '';
|
|
117
|
+
if (!messageContent) return;
|
|
118
|
+
|
|
119
|
+
// Check for dynamic mode triggers
|
|
120
|
+
const detected = detectMode(messageContent);
|
|
121
|
+
if (!detected) return;
|
|
122
|
+
|
|
123
|
+
// Apply detected mode settings
|
|
124
|
+
ctx.client.app.log({
|
|
125
|
+
body: {
|
|
126
|
+
service: 'agentuity-coder',
|
|
127
|
+
level: 'info',
|
|
128
|
+
message: `Dynamic mode activated: ${detected.mode}`,
|
|
129
|
+
extra: { mode: detected.mode, settings: detected.settings },
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Show toast to user
|
|
134
|
+
const modeMessages: Record<string, string> = {
|
|
135
|
+
creative: '🎨 Creative Mode activated - higher creativity enabled',
|
|
136
|
+
deepThink: '🧠 Deep Think Mode activated - extended reasoning enabled',
|
|
137
|
+
thorough: '🔍 Thorough Mode activated - more iterations enabled',
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
ctx.client.tui?.showToast?.({
|
|
142
|
+
body: { message: modeMessages[detected.mode] || `${detected.mode} mode activated` },
|
|
143
|
+
});
|
|
144
|
+
} catch {
|
|
145
|
+
// Toast may not be available in all contexts (e.g., headless)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Apply temperature if specified
|
|
149
|
+
if (
|
|
150
|
+
'temperature' in detected.settings &&
|
|
151
|
+
typeof detected.settings.temperature === 'number'
|
|
152
|
+
) {
|
|
153
|
+
outputObj.temperature = detected.settings.temperature;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Apply maxSteps if specified
|
|
157
|
+
if ('maxSteps' in detected.settings && typeof detected.settings.maxSteps === 'number') {
|
|
158
|
+
outputObj.options = {
|
|
159
|
+
...outputObj.options,
|
|
160
|
+
maxSteps: detected.settings.maxSteps,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Apply provider-specific options (like thinking budget)
|
|
165
|
+
if ('thinking' in detected.settings) {
|
|
166
|
+
outputObj.options = {
|
|
167
|
+
...outputObj.options,
|
|
168
|
+
thinking: detected.settings.thinking,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Advertised magic words for users:
|
|
177
|
+
*
|
|
178
|
+
* - "brainstorm" - Activates creative mode (temperature → 0.8)
|
|
179
|
+
* - "think hard" - Activates deep thinking mode (max reasoning budget)
|
|
180
|
+
* - "dig deep" / "go deep" - Activates thorough mode (maxSteps → 50)
|
|
181
|
+
*
|
|
182
|
+
* These can also be triggered by specific phrases like:
|
|
183
|
+
* - "be creative", "give me ideas", "explore options", "explore alternatives"
|
|
184
|
+
* - "think deeply", "analyze carefully", "reason through this"
|
|
185
|
+
* - "deep dive", "take your time", "be thorough", "be meticulous"
|
|
186
|
+
*
|
|
187
|
+
* Note: Triggers use multi-word phrases to avoid false positives from common words.
|
|
188
|
+
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PluginContext, CoderConfig } from '../../types';
|
|
2
|
+
|
|
3
|
+
export interface SessionHooks {
|
|
4
|
+
onMessage: (input: unknown, output: unknown) => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createSessionHooks(_ctx: PluginContext, _config: CoderConfig): SessionHooks {
|
|
8
|
+
const initializedSessions = new Set<string>();
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
async onMessage(input: unknown, _output: unknown): Promise<void> {
|
|
12
|
+
const sessionId = extractSessionId(input);
|
|
13
|
+
if (!sessionId) return;
|
|
14
|
+
|
|
15
|
+
if (!initializedSessions.has(sessionId)) {
|
|
16
|
+
initializedSessions.add(sessionId);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function extractSessionId(input: unknown): string | undefined {
|
|
23
|
+
if (typeof input === 'object' && input !== null && 'sessionID' in input) {
|
|
24
|
+
return (input as { sessionID: string }).sessionID;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { PluginContext, CoderConfig } from '../../types';
|
|
2
|
+
import { checkAuth } from '../../services/auth';
|
|
3
|
+
|
|
4
|
+
export interface ToolHooks {
|
|
5
|
+
before: (input: unknown, output: unknown) => Promise<void>;
|
|
6
|
+
after: (input: unknown, output: unknown) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const CLOUD_TOOL_PREFIXES = [
|
|
10
|
+
'agentuity.kv',
|
|
11
|
+
'agentuity.storage',
|
|
12
|
+
'agentuity.vector',
|
|
13
|
+
'agentuity.sandbox',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/** Cloud service detection for bash commands */
|
|
17
|
+
const CLOUD_SERVICES: Record<string, { name: string; emoji: string }> = {
|
|
18
|
+
'agentuity cloud kv': { name: 'KV Storage', emoji: '🗄️' },
|
|
19
|
+
'agentuity cloud storage': { name: 'Object Storage', emoji: '📦' },
|
|
20
|
+
'agentuity cloud vector': { name: 'Vector Search', emoji: '🔍' },
|
|
21
|
+
'agentuity cloud sandbox': { name: 'Sandbox', emoji: '🏖️' },
|
|
22
|
+
'agentuity cloud db': { name: 'Postgres', emoji: '🐘' },
|
|
23
|
+
'agentuity cloud ssh': { name: 'SSH', emoji: '🔐' },
|
|
24
|
+
'agentuity cloud scp': { name: 'File Transfer', emoji: '📤' },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function createToolHooks(ctx: PluginContext, config: CoderConfig): ToolHooks {
|
|
28
|
+
const blockedCommands = config.blockedCommands ?? [];
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
async before(input: unknown, output: unknown): Promise<void> {
|
|
32
|
+
const toolName = extractToolName(input);
|
|
33
|
+
if (!toolName) return;
|
|
34
|
+
|
|
35
|
+
// Check MCP cloud tools
|
|
36
|
+
if (isCloudTool(toolName)) {
|
|
37
|
+
const authResult = await checkAuth();
|
|
38
|
+
if (!authResult.ok) {
|
|
39
|
+
const out = output as { error?: string };
|
|
40
|
+
out.error = authResult.error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Detect bash commands using agentuity CLI
|
|
45
|
+
if (toolName === 'bash') {
|
|
46
|
+
const command = extractBashCommand(input);
|
|
47
|
+
if (command?.includes('agentuity')) {
|
|
48
|
+
// Security: Block sensitive commands
|
|
49
|
+
const blockedPattern = isBlockedCommand(command, blockedCommands);
|
|
50
|
+
if (blockedPattern) {
|
|
51
|
+
const out = output as { blocked?: boolean; error?: string };
|
|
52
|
+
out.blocked = true;
|
|
53
|
+
out.error = `🚫 Blocked: "${blockedPattern}" commands are not allowed for security reasons.`;
|
|
54
|
+
|
|
55
|
+
ctx.client.app.log({
|
|
56
|
+
body: {
|
|
57
|
+
service: 'agentuity-coder',
|
|
58
|
+
level: 'warn',
|
|
59
|
+
message: `Blocked command pattern: ${blockedPattern}`,
|
|
60
|
+
extra: { command },
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Show toast for cloud service usage
|
|
67
|
+
const service = detectCloudService(command);
|
|
68
|
+
if (service) {
|
|
69
|
+
try {
|
|
70
|
+
ctx.client.tui?.showToast?.({
|
|
71
|
+
body: { message: `${service.emoji} Agentuity ${service.name}` },
|
|
72
|
+
});
|
|
73
|
+
} catch {
|
|
74
|
+
// Toast may not be available
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async after(_input: unknown, _output: unknown): Promise<void> {},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function extractToolName(input: unknown): string | undefined {
|
|
86
|
+
if (typeof input === 'object' && input !== null && 'tool' in input) {
|
|
87
|
+
return (input as { tool: string }).tool;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function extractBashCommand(input: unknown): string | undefined {
|
|
93
|
+
if (typeof input !== 'object' || input === null) return undefined;
|
|
94
|
+
const inp = input as Record<string, unknown>;
|
|
95
|
+
|
|
96
|
+
// Try different possible arg structures
|
|
97
|
+
if (typeof inp.command === 'string') return inp.command;
|
|
98
|
+
if (typeof inp.args === 'object' && inp.args !== null) {
|
|
99
|
+
const args = inp.args as Record<string, unknown>;
|
|
100
|
+
if (typeof args.command === 'string') return args.command;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function detectCloudService(command: string): { name: string; emoji: string } | null {
|
|
107
|
+
for (const [pattern, service] of Object.entries(CLOUD_SERVICES)) {
|
|
108
|
+
if (command.includes(pattern)) {
|
|
109
|
+
return service;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isCloudTool(toolName: string): boolean {
|
|
116
|
+
return CLOUD_TOOL_PREFIXES.some((prefix) => toolName.startsWith(prefix));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Check if a command matches any blocked pattern, returns the matched pattern or null */
|
|
120
|
+
function isBlockedCommand(command: string, blockedPatterns: string[]): string | null {
|
|
121
|
+
for (const pattern of blockedPatterns) {
|
|
122
|
+
if (command.includes(pattern)) {
|
|
123
|
+
return pattern;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createCoderPlugin } from './plugin';
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { PluginContext, PluginHooks, AgentConfig, CommandDefinition } from '../types';
|
|
2
|
+
import { agents } from '../agents';
|
|
3
|
+
import { loadCoderConfig, getDefaultConfig, mergeConfig } from '../config';
|
|
4
|
+
import { createSessionHooks } from './hooks/session';
|
|
5
|
+
import { createToolHooks } from './hooks/tools';
|
|
6
|
+
import { createKeywordHooks } from './hooks/keyword';
|
|
7
|
+
import { createParamsHooks } from './hooks/params';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { AgentRole } from '../types';
|
|
10
|
+
|
|
11
|
+
// Agent display names for @mentions
|
|
12
|
+
const AGENT_MENTIONS: Record<AgentRole, string> = {
|
|
13
|
+
lead: '@Agentuity Coder Lead',
|
|
14
|
+
scout: '@Agentuity Coder Scout',
|
|
15
|
+
builder: '@Agentuity Coder Builder',
|
|
16
|
+
reviewer: '@Agentuity Coder Reviewer',
|
|
17
|
+
memory: '@Agentuity Coder Memory',
|
|
18
|
+
expert: '@Agentuity Coder Expert',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export async function createCoderPlugin(ctx: PluginContext): Promise<PluginHooks> {
|
|
22
|
+
ctx.client.app.log({
|
|
23
|
+
body: {
|
|
24
|
+
service: 'agentuity-coder',
|
|
25
|
+
level: 'info',
|
|
26
|
+
message: 'Agentuity Coder plugin initializing',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const userConfig = await loadCoderConfig();
|
|
31
|
+
const coderConfig = mergeConfig(getDefaultConfig(), userConfig);
|
|
32
|
+
|
|
33
|
+
const sessionHooks = createSessionHooks(ctx, coderConfig);
|
|
34
|
+
const toolHooks = createToolHooks(ctx, coderConfig);
|
|
35
|
+
const keywordHooks = createKeywordHooks(ctx, coderConfig);
|
|
36
|
+
const paramsHooks = createParamsHooks(ctx, coderConfig);
|
|
37
|
+
|
|
38
|
+
const configHandler = createConfigHandler(coderConfig);
|
|
39
|
+
|
|
40
|
+
// Get the tool helper from Open Code context if available
|
|
41
|
+
const toolHelper = (ctx as { tool?: unknown }).tool as
|
|
42
|
+
| ((schema: (s: typeof z) => unknown) => unknown)
|
|
43
|
+
| undefined;
|
|
44
|
+
|
|
45
|
+
const tools = toolHelper ? createTools(toolHelper) : undefined;
|
|
46
|
+
|
|
47
|
+
// Show startup toast (fire and forget, don't block)
|
|
48
|
+
try {
|
|
49
|
+
ctx.client.tui?.showToast?.({ body: { message: '🚀 Agentuity Coder ready' } });
|
|
50
|
+
} catch {
|
|
51
|
+
// Toast may not be available
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
...(tools ? { tool: tools } : {}),
|
|
56
|
+
config: configHandler,
|
|
57
|
+
'chat.message': async (input: unknown, output: unknown) => {
|
|
58
|
+
await keywordHooks.onMessage(input, output);
|
|
59
|
+
await sessionHooks.onMessage(input, output);
|
|
60
|
+
},
|
|
61
|
+
'chat.params': paramsHooks.onParams,
|
|
62
|
+
'tool.execute.before': toolHooks.before,
|
|
63
|
+
'tool.execute.after': toolHooks.after,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createConfigHandler(
|
|
68
|
+
coderConfig: ReturnType<typeof getDefaultConfig>
|
|
69
|
+
): (config: Record<string, unknown>) => Promise<void> {
|
|
70
|
+
return async (config: Record<string, unknown>) => {
|
|
71
|
+
const agentConfigs = createAgentConfigs(coderConfig);
|
|
72
|
+
const commands = createCommands();
|
|
73
|
+
|
|
74
|
+
config.agent = {
|
|
75
|
+
...(config.agent as Record<string, AgentConfig> | undefined),
|
|
76
|
+
...agentConfigs,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
config.command = {
|
|
80
|
+
...(config.command as Record<string, CommandDefinition> | undefined),
|
|
81
|
+
...commands,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createAgentConfigs(
|
|
87
|
+
config: ReturnType<typeof getDefaultConfig>
|
|
88
|
+
): Record<string, AgentConfig> {
|
|
89
|
+
const result: Record<string, AgentConfig> = {};
|
|
90
|
+
|
|
91
|
+
for (const agent of Object.values(agents)) {
|
|
92
|
+
const modelConfig = config.agents?.[agent.role];
|
|
93
|
+
|
|
94
|
+
// Convert tools.exclude to Open Code format (tool: false)
|
|
95
|
+
const tools: Record<string, boolean> = {};
|
|
96
|
+
if (agent.tools?.exclude) {
|
|
97
|
+
for (const tool of agent.tools.exclude) {
|
|
98
|
+
tools[tool] = false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
result[agent.displayName] = {
|
|
103
|
+
description: agent.description,
|
|
104
|
+
model: modelConfig?.model ?? agent.defaultModel,
|
|
105
|
+
prompt: agent.systemPrompt,
|
|
106
|
+
mode: agent.mode ?? 'subagent',
|
|
107
|
+
...(Object.keys(tools).length > 0 ? { tools } : {}),
|
|
108
|
+
// Pass through thinking/reasoning settings
|
|
109
|
+
...(agent.variant ? { variant: agent.variant } : {}),
|
|
110
|
+
...(agent.temperature !== undefined ? { temperature: agent.temperature } : {}),
|
|
111
|
+
...(agent.maxSteps !== undefined ? { maxSteps: agent.maxSteps } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createCommands(): Record<string, CommandDefinition> {
|
|
119
|
+
return {
|
|
120
|
+
'agentuity-coder': {
|
|
121
|
+
name: 'agentuity-coder',
|
|
122
|
+
description:
|
|
123
|
+
'Run a task with the Agentuity Coder agent team (use @Agentuity Coder Lead, @Agentuity Coder Scout, etc.)',
|
|
124
|
+
template: `<coder-mode>
|
|
125
|
+
You are the Agentuity Coder Lead agent orchestrating the Agentuity Coder team.
|
|
126
|
+
|
|
127
|
+
## Your Team (use @mentions to invoke)
|
|
128
|
+
- **@Agentuity Coder Scout**: Explore codebase, find patterns, research docs (read-only)
|
|
129
|
+
- **@Agentuity Coder Builder**: Implement features, write code, run tests
|
|
130
|
+
- **@Agentuity Coder Reviewer**: Review changes, catch issues, apply fixes
|
|
131
|
+
- **@Agentuity Coder Memory**: Store context, remember decisions
|
|
132
|
+
- **@Agentuity Coder Expert**: Agentuity CLI and cloud services specialist
|
|
133
|
+
|
|
134
|
+
## Task
|
|
135
|
+
$ARGUMENTS
|
|
136
|
+
|
|
137
|
+
## Guidelines
|
|
138
|
+
1. Use @Agentuity Coder Scout first to understand context
|
|
139
|
+
2. Delegate implementation to @Agentuity Coder Builder
|
|
140
|
+
3. Have @Agentuity Coder Reviewer check the work
|
|
141
|
+
4. Use @Agentuity Coder Expert for Agentuity CLI questions
|
|
142
|
+
5. Only use cloud services when genuinely helpful
|
|
143
|
+
6. **When done, tell @Agentuity Coder Memory to memorialize the session**
|
|
144
|
+
</coder-mode>`,
|
|
145
|
+
agent: 'Agentuity Coder Lead',
|
|
146
|
+
argumentHint: '"task description"',
|
|
147
|
+
},
|
|
148
|
+
'agentuity-memory-save': {
|
|
149
|
+
name: 'agentuity-memory-save',
|
|
150
|
+
description: 'Save the current session to memory for future recall',
|
|
151
|
+
template: `Memorialize this session. Summarize what was accomplished in this conversation:
|
|
152
|
+
- Problem/task that was addressed
|
|
153
|
+
- Key decisions and their rationale
|
|
154
|
+
- Patterns and approaches used
|
|
155
|
+
- Solutions implemented
|
|
156
|
+
- Open questions or follow-ups
|
|
157
|
+
|
|
158
|
+
Save to vector storage using the coder-sessions namespace so the team can recall this work in future sessions.
|
|
159
|
+
|
|
160
|
+
$ARGUMENTS`,
|
|
161
|
+
agent: 'Agentuity Coder Memory',
|
|
162
|
+
argumentHint: '(optional additional context)',
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
166
|
+
// Agentuity Cloud Service Commands
|
|
167
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
'agentuity-cloud': {
|
|
170
|
+
name: 'agentuity-cloud',
|
|
171
|
+
description: '☁️ Agentuity cloud services (KV, Storage, Vector, Sandbox, DB, SSH, etc.)',
|
|
172
|
+
template: `You are the Agentuity Coder Expert helping with Agentuity cloud services.
|
|
173
|
+
|
|
174
|
+
Use the \`agentuity\` CLI to execute the user's request.
|
|
175
|
+
|
|
176
|
+
## Available Services
|
|
177
|
+
| Service | CLI | Purpose |
|
|
178
|
+
|---------|-----|---------|
|
|
179
|
+
| KV | \`agentuity cloud kv\` | Key-value storage (namespaces, keys) |
|
|
180
|
+
| Storage | \`agentuity cloud storage\` | Object/file storage (buckets) |
|
|
181
|
+
| Vector | \`agentuity cloud vector\` | Embeddings & semantic search |
|
|
182
|
+
| Sandbox | \`agentuity cloud sandbox\` | Isolated execution environments |
|
|
183
|
+
| Database | \`agentuity cloud db\` | Postgres databases |
|
|
184
|
+
| SSH | \`agentuity cloud ssh\` | SSH into deployments/sandboxes |
|
|
185
|
+
| Deployments | \`agentuity cloud deployment\` | Manage deployments |
|
|
186
|
+
| Agents | \`agentuity cloud agent\` | Cloud agent management |
|
|
187
|
+
| Sessions | \`agentuity cloud session\` | Agent session data |
|
|
188
|
+
| Threads | \`agentuity cloud thread\` | Conversation threads |
|
|
189
|
+
|
|
190
|
+
## Guidelines
|
|
191
|
+
1. First check auth: \`agentuity auth whoami\`
|
|
192
|
+
2. Prefer \`--json\` for programmatic output
|
|
193
|
+
3. List/inspect before creating new resources
|
|
194
|
+
4. Explain what commands you're running
|
|
195
|
+
|
|
196
|
+
## User Request
|
|
197
|
+
$ARGUMENTS`,
|
|
198
|
+
agent: 'Agentuity Coder Expert',
|
|
199
|
+
subtask: true,
|
|
200
|
+
argumentHint: '"list kv namespaces" or "upload file.txt to storage"',
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
'agentuity-sandbox': {
|
|
204
|
+
name: 'agentuity-sandbox',
|
|
205
|
+
description: '🏖️ Agentuity sandboxes (isolated execution environments)',
|
|
206
|
+
template: `You are the Agentuity Coder Expert helping with Agentuity sandboxes.
|
|
207
|
+
|
|
208
|
+
Use the \`agentuity cloud sandbox\` CLI commands to help the user.
|
|
209
|
+
|
|
210
|
+
## Common Commands
|
|
211
|
+
\`\`\`bash
|
|
212
|
+
agentuity cloud sandbox runtime list --json # List available runtimes (bun:1, python:3.14, etc.)
|
|
213
|
+
agentuity cloud sandbox run [--memory 1Gi] [--cpu 1000m] \\
|
|
214
|
+
[--runtime <name>] [--runtimeId <id>] \\
|
|
215
|
+
[--name <name>] [--description <text>] \\
|
|
216
|
+
-- <command> # One-shot execution
|
|
217
|
+
agentuity cloud sandbox create --json [--memory 1Gi] [--cpu 1000m] \\
|
|
218
|
+
[--network] [--runtime <name>] [--runtimeId <id>] \\
|
|
219
|
+
[--name <name>] [--description <text>] # Create persistent sandbox
|
|
220
|
+
agentuity cloud sandbox list --json # List sandboxes (includes telemetry)
|
|
221
|
+
agentuity cloud sandbox exec <id> -- <command> # Run in existing sandbox
|
|
222
|
+
agentuity cloud sandbox files <id> [path] --json # List files
|
|
223
|
+
agentuity cloud sandbox cp ./local <id>:/home/agentuity # Copy files to sandbox
|
|
224
|
+
agentuity cloud sandbox delete <id> --json # Delete sandbox
|
|
225
|
+
agentuity cloud sandbox snapshot create <id> \\
|
|
226
|
+
[--name <name>] [--description <text>] [--tag <tag>] # Save sandbox state
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
## Guidelines
|
|
230
|
+
1. First check auth: \`agentuity auth whoami\`
|
|
231
|
+
2. Use \`--json\` for programmatic output
|
|
232
|
+
3. Explain what commands you're running
|
|
233
|
+
4. Default working directory inside sandboxes: \`/home/agentuity\`
|
|
234
|
+
5. Use \`runtime list\` to find runtimes, then pass \`--runtime\` or \`--runtimeId\` on \`run\`/\`create\`
|
|
235
|
+
6. Use \`--name\` and \`--description\` for better tracking
|
|
236
|
+
7. Snapshot \`--tag\` defaults to \`latest\`, max 128 chars, must match \`^[a-zA-Z0-9][a-zA-Z0-9._-]*$\`
|
|
237
|
+
8. Telemetry fields from \`list\`/\`get\`: \`cpuTimeMs\`, \`memoryByteSec\`, \`networkEgressBytes\`, \`networkEnabled\`, \`mode\`
|
|
238
|
+
|
|
239
|
+
## User Request
|
|
240
|
+
$ARGUMENTS`,
|
|
241
|
+
agent: 'Agentuity Coder Expert',
|
|
242
|
+
subtask: true,
|
|
243
|
+
argumentHint: '"run bun test" or "create a sandbox with 2Gi memory"',
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function createTools(tool: (schema: (s: typeof z) => unknown) => unknown): Record<string, unknown> {
|
|
249
|
+
const coderDelegate = tool((s) => ({
|
|
250
|
+
description: `Delegate a task to a specialized Agentuity Coder agent.
|
|
251
|
+
|
|
252
|
+
Use this to:
|
|
253
|
+
- Scout: Explore codebase, find patterns, research documentation
|
|
254
|
+
- Builder: Implement features, write code, run tests
|
|
255
|
+
- Reviewer: Review changes, catch issues, apply fixes
|
|
256
|
+
- Memory: Store context, remember decisions across sessions
|
|
257
|
+
- Expert: Get help with Agentuity CLI and cloud services`,
|
|
258
|
+
args: s.object({
|
|
259
|
+
agent: s
|
|
260
|
+
.enum(['scout', 'builder', 'reviewer', 'memory', 'expert'])
|
|
261
|
+
.describe('Which agent to delegate to'),
|
|
262
|
+
task: s.string().describe('Clear description of the task'),
|
|
263
|
+
context: s.string().optional().describe('Additional context from previous tasks'),
|
|
264
|
+
}),
|
|
265
|
+
execute: async (args: { agent: AgentRole; task: string; context?: string }) => {
|
|
266
|
+
const mention = AGENT_MENTIONS[args.agent];
|
|
267
|
+
let prompt = `${mention}\n\n## Task\n${args.task}`;
|
|
268
|
+
if (args.context) {
|
|
269
|
+
prompt = `${mention}\n\n## Context\n${args.context}\n\n## Task\n${args.task}`;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
output: `To delegate this task, use the Task tool with this prompt:\n\n${prompt}\n\nThe ${args.agent} agent will handle this task.`,
|
|
273
|
+
};
|
|
274
|
+
},
|
|
275
|
+
}));
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
coder_delegate: coderDelegate,
|
|
279
|
+
};
|
|
280
|
+
}
|