@miphamai/cli 0.6.6 → 0.6.7
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/package.json +1 -1
- package/src/agent/sub-agent.ts +87 -14
- package/src/agent/types.ts +2 -0
- package/src/plugin/plugin-loader.ts +15 -4
package/package.json
CHANGED
package/src/agent/sub-agent.ts
CHANGED
|
@@ -75,24 +75,97 @@ export class SubAgent {
|
|
|
75
75
|
const resolvedModel = modelToUse === 'inherit' ? model : modelToUse
|
|
76
76
|
|
|
77
77
|
const chunks: string[] = []
|
|
78
|
+
const MAX_TOOL_TURNS = options.maxTurns || 5
|
|
79
|
+
|
|
80
|
+
let currentMessages = messages
|
|
81
|
+
let currentSystemPrompt = systemPrompt
|
|
78
82
|
|
|
79
83
|
try {
|
|
80
|
-
for
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
for (let turn = 0; turn < MAX_TOOL_TURNS; turn++) {
|
|
85
|
+
const toolUses: Array<{ id: string; name: string; input: Record<string, unknown> }> = []
|
|
86
|
+
let turnText = ''
|
|
87
|
+
|
|
88
|
+
for await (const chunk of provider.chat({
|
|
89
|
+
model: resolvedModel,
|
|
90
|
+
messages: currentMessages,
|
|
91
|
+
systemPrompt: currentSystemPrompt,
|
|
92
|
+
tools: toolDefs,
|
|
93
|
+
maxTokens: 4096,
|
|
94
|
+
})) {
|
|
95
|
+
if (chunk.type === 'text' && chunk.content) {
|
|
96
|
+
turnText += chunk.content
|
|
97
|
+
}
|
|
98
|
+
if (chunk.type === 'tool_use' && chunk.toolUse) {
|
|
99
|
+
toolUses.push({
|
|
100
|
+
id: chunk.toolUse.id,
|
|
101
|
+
name: chunk.toolUse.name,
|
|
102
|
+
input: chunk.toolUse.input,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
if (chunk.type === 'error') {
|
|
106
|
+
throw new Error(`Sub-agent execution failed: ${chunk.error}`)
|
|
107
|
+
}
|
|
108
|
+
if (chunk.type === 'stop') {
|
|
109
|
+
break
|
|
110
|
+
}
|
|
92
111
|
}
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
|
|
113
|
+
chunks.push(turnText)
|
|
114
|
+
|
|
115
|
+
// No tools used — we're done
|
|
116
|
+
if (toolUses.length === 0) break
|
|
117
|
+
|
|
118
|
+
// Execute tools and build follow-up messages
|
|
119
|
+
currentMessages = [
|
|
120
|
+
...currentMessages,
|
|
121
|
+
{
|
|
122
|
+
role: 'assistant' as const,
|
|
123
|
+
content: turnText || 'Executing tools...',
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
for (const tu of toolUses) {
|
|
128
|
+
const tool = this.toolRegistry.get(tu.name)
|
|
129
|
+
if (!tool) {
|
|
130
|
+
currentMessages.push({
|
|
131
|
+
role: 'user' as const,
|
|
132
|
+
content: `Error: Unknown tool "${tu.name}"`,
|
|
133
|
+
})
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const result = await tool.execute(tu.input, {
|
|
139
|
+
cwd: process.cwd(),
|
|
140
|
+
sessionId: 'sub-agent',
|
|
141
|
+
provider: '',
|
|
142
|
+
model: resolvedModel,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
currentMessages.push({
|
|
146
|
+
role: 'assistant' as const,
|
|
147
|
+
content: [{ type: 'tool_use' as const, id: tu.id, name: tu.name, input: tu.input }],
|
|
148
|
+
})
|
|
149
|
+
currentMessages.push({
|
|
150
|
+
role: 'user' as const,
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: 'tool_result' as const,
|
|
154
|
+
tool_use_id: tu.id,
|
|
155
|
+
content: result.success ? result.content : result.error || result.content,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
})
|
|
159
|
+
} catch (err) {
|
|
160
|
+
currentMessages.push({
|
|
161
|
+
role: 'user' as const,
|
|
162
|
+
content: `Tool "${tu.name}" execution error: ${String(err)}`,
|
|
163
|
+
})
|
|
164
|
+
}
|
|
95
165
|
}
|
|
166
|
+
|
|
167
|
+
// Don't send system prompt on subsequent turns
|
|
168
|
+
currentSystemPrompt = ''
|
|
96
169
|
}
|
|
97
170
|
} catch (err) {
|
|
98
171
|
if (err instanceof Error && err.message.startsWith('Sub-agent')) {
|
package/src/agent/types.ts
CHANGED
|
@@ -6,7 +6,8 @@ import type { SkillsLoader } from '../skills/loader'
|
|
|
6
6
|
import type { HookEngine } from '../core/hooks'
|
|
7
7
|
import type { McpClient } from '../mcp/client'
|
|
8
8
|
import { registerMcpServerTools } from '../mcp/registry'
|
|
9
|
-
import
|
|
9
|
+
import { executeHook } from '../core/hooks-executor'
|
|
10
|
+
import type { McpServerConfig, ToolDefinition, HookConfig, HookEvent } from '../shared/types'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Activate all enabled plugins — load their agents, skills, MCP servers, and hooks.
|
|
@@ -86,14 +87,24 @@ export function loadPlugins(
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// ── Hooks from plugin.json ──
|
|
90
|
+
// Plugin hooks are HookConfig objects (type + command/url), not HookDefinition
|
|
91
|
+
// (which requires a handler function). Convert them via executeHook wrapper.
|
|
89
92
|
try {
|
|
90
93
|
const manifestPath = join(plugin.path, 'plugin.json')
|
|
91
94
|
if (existsSync(manifestPath)) {
|
|
92
95
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
93
96
|
if (manifest.hooks && Array.isArray(manifest.hooks)) {
|
|
94
|
-
for (const
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
+
for (const hookCfg of manifest.hooks as HookConfig[]) {
|
|
98
|
+
if (hookCfg.type && hookCfg) {
|
|
99
|
+
const event = (hookCfg as unknown as Record<string, unknown>).event as
|
|
100
|
+
| HookEvent
|
|
101
|
+
| undefined
|
|
102
|
+
if (event) {
|
|
103
|
+
hookEngine.register({
|
|
104
|
+
event,
|
|
105
|
+
handler: async (ctx) => executeHook(hookCfg, ctx),
|
|
106
|
+
})
|
|
107
|
+
}
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
110
|
}
|