@vibe-forge/core 0.7.5 → 0.8.0
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 +4 -46
- package/src/env.ts +5 -25
- package/src/index.ts +0 -5
- package/src/types.ts +12 -72
- package/src/ws.ts +3 -12
- package/src/adapter/index.ts +0 -6
- package/src/adapter/loader.ts +0 -11
- package/src/adapter/type.ts +0 -117
- package/src/config/load.ts +0 -122
- package/src/config/types.ts +0 -289
- package/src/config.ts +0 -2
- package/src/controllers/benchmark/discover.ts +0 -89
- package/src/controllers/benchmark/index.ts +0 -24
- package/src/controllers/benchmark/result-store.ts +0 -46
- package/src/controllers/benchmark/runner.ts +0 -415
- package/src/controllers/benchmark/schema.ts +0 -60
- package/src/controllers/benchmark/types.ts +0 -80
- package/src/controllers/benchmark/utils.ts +0 -144
- package/src/controllers/benchmark/workspace.ts +0 -179
- package/src/controllers/config/index.ts +0 -214
- package/src/controllers/system/assets/completed.mp3 +0 -0
- package/src/controllers/system/assets/mcp.png +0 -0
- package/src/controllers/system/index.ts +0 -102
- package/src/controllers/task/generate-adapter-query-options.ts +0 -25
- package/src/controllers/task/index.ts +0 -2
- package/src/controllers/task/prepare.ts +0 -74
- package/src/controllers/task/run.ts +0 -231
- package/src/controllers/task/schema.ts +0 -131
- package/src/controllers/task/type.ts +0 -6
- package/src/hooks/bridge.ts +0 -368
- package/src/hooks/call.ts +0 -74
- package/src/hooks/index.ts +0 -41
- package/src/hooks/loader.ts +0 -79
- package/src/hooks/native.ts +0 -116
- package/src/hooks/runtime.ts +0 -139
- package/src/hooks/type.ts +0 -145
- package/src/utils/cache.ts +0 -58
- package/src/utils/create-logger.ts +0 -89
- package/src/utils/definition-loader.ts +0 -530
- package/src/utils/filter.ts +0 -26
- package/src/utils/string-transform.ts +0 -37
- package/src/utils/uuid.ts +0 -6
- package/src/utils/workspace-assets.ts +0 -919
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import type { AdapterCtx, AdapterOutputEvent, AdapterQueryOptions } from '#~/adapter/index.js'
|
|
2
|
-
import { loadAdapter } from '#~/adapter/index.js'
|
|
3
|
-
import type { ModelServiceConfig } from '#~/config.js'
|
|
4
|
-
import { createAdapterHookBridge } from '#~/hooks/bridge.js'
|
|
5
|
-
import { callHook } from '#~/hooks/call.js'
|
|
6
|
-
import { buildAdapterAssetPlan } from '#~/utils/workspace-assets.js'
|
|
7
|
-
import type { TaskDetail } from '#~/types.js'
|
|
8
|
-
|
|
9
|
-
import { prepare } from './prepare'
|
|
10
|
-
import type { RunTaskOptions } from './type'
|
|
11
|
-
|
|
12
|
-
const normalizeNonEmptyString = (value: unknown) => (
|
|
13
|
-
typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
const pickFirstNonEmptyString = (values: unknown[]) =>
|
|
17
|
-
values
|
|
18
|
-
.map(normalizeNonEmptyString)
|
|
19
|
-
.find((value): value is string => value != null)
|
|
20
|
-
|
|
21
|
-
const resolveQueryModel = (params: {
|
|
22
|
-
config: AdapterCtx['configs'][0]
|
|
23
|
-
userConfig: AdapterCtx['configs'][1]
|
|
24
|
-
inputModel?: string
|
|
25
|
-
}) => {
|
|
26
|
-
const inputModel = normalizeNonEmptyString(params.inputModel)
|
|
27
|
-
// User explicitly provided a model → pass through as-is.
|
|
28
|
-
// The adapter decides CCR vs native based on whether it contains ",".
|
|
29
|
-
if (inputModel != null) return inputModel
|
|
30
|
-
|
|
31
|
-
// No explicit model → auto-resolve from modelServices config.
|
|
32
|
-
// Produces "service,model" format when services are configured,
|
|
33
|
-
// which signals the adapter to route through CCR.
|
|
34
|
-
const mergedModelServices = {
|
|
35
|
-
...(params.config?.modelServices ?? {}),
|
|
36
|
-
...(params.userConfig?.modelServices ?? {})
|
|
37
|
-
}
|
|
38
|
-
const mergedDefaultModel = pickFirstNonEmptyString(
|
|
39
|
-
[
|
|
40
|
-
params.userConfig?.defaultModel,
|
|
41
|
-
params.config?.defaultModel
|
|
42
|
-
]
|
|
43
|
-
)
|
|
44
|
-
const mergedDefaultModelService = pickFirstNonEmptyString(
|
|
45
|
-
[
|
|
46
|
-
params.userConfig?.defaultModelService,
|
|
47
|
-
params.config?.defaultModelService
|
|
48
|
-
]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
const serviceEntries = Object.entries(mergedModelServices)
|
|
52
|
-
const modelToService = new Map<string, string>()
|
|
53
|
-
const availableModels: string[] = []
|
|
54
|
-
for (const [serviceKey, serviceValue] of serviceEntries) {
|
|
55
|
-
const service = (serviceValue != null && typeof serviceValue === 'object')
|
|
56
|
-
? serviceValue as ModelServiceConfig
|
|
57
|
-
: undefined
|
|
58
|
-
const models = Array.isArray(service?.models)
|
|
59
|
-
? service?.models.filter(item => typeof item === 'string' && item.trim() !== '')
|
|
60
|
-
: []
|
|
61
|
-
for (const model of models) {
|
|
62
|
-
if (!modelToService.has(model)) modelToService.set(model, serviceKey)
|
|
63
|
-
availableModels.push(model)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (availableModels.length === 0) return undefined
|
|
68
|
-
|
|
69
|
-
const resolveDefaultModel = () => {
|
|
70
|
-
if (mergedDefaultModel && modelToService.has(mergedDefaultModel)) return mergedDefaultModel
|
|
71
|
-
if (mergedDefaultModelService && mergedModelServices[mergedDefaultModelService]) {
|
|
72
|
-
const service = mergedModelServices[mergedDefaultModelService] as ModelServiceConfig | undefined
|
|
73
|
-
const models = Array.isArray(service?.models)
|
|
74
|
-
? service?.models.filter((item: unknown) => typeof item === 'string' && (item as string).trim() !== '')
|
|
75
|
-
: []
|
|
76
|
-
if (models.length > 0) return models[0]
|
|
77
|
-
}
|
|
78
|
-
return availableModels[0]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const resolvedModel = resolveDefaultModel()
|
|
82
|
-
if (!resolvedModel) return undefined
|
|
83
|
-
|
|
84
|
-
const resolvedService = modelToService.get(resolvedModel) ??
|
|
85
|
-
mergedDefaultModelService ??
|
|
86
|
-
serviceEntries[0]?.[0]
|
|
87
|
-
|
|
88
|
-
return resolvedService ? `${resolvedService},${resolvedModel}` : resolvedModel
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
declare module '@vibe-forge/core' {
|
|
92
|
-
interface Cache {
|
|
93
|
-
base: Omit<AdapterCtx, 'logger' | 'cache'>
|
|
94
|
-
detail: TaskDetail
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const run = async (
|
|
99
|
-
options: RunTaskOptions,
|
|
100
|
-
adapterOptions: AdapterQueryOptions
|
|
101
|
-
) => {
|
|
102
|
-
const [ctx] = await prepare(options, adapterOptions)
|
|
103
|
-
const {
|
|
104
|
-
configs: [config, userConfig]
|
|
105
|
-
} = ctx
|
|
106
|
-
|
|
107
|
-
const { logger, cache, ...base } = ctx
|
|
108
|
-
|
|
109
|
-
await cache.set('base', base)
|
|
110
|
-
|
|
111
|
-
const adapters = {
|
|
112
|
-
...config?.adapters,
|
|
113
|
-
...userConfig?.adapters
|
|
114
|
-
}
|
|
115
|
-
// dprint-ignore
|
|
116
|
-
const adapterType =
|
|
117
|
-
// 0. adapter from options
|
|
118
|
-
options.adapter ??
|
|
119
|
-
// 1. config default adapter
|
|
120
|
-
config?.defaultAdapter ??
|
|
121
|
-
// 2. user config default adapter
|
|
122
|
-
userConfig?.defaultAdapter ??
|
|
123
|
-
// 3. first adapter in config
|
|
124
|
-
(() => {
|
|
125
|
-
const adapterNames = Object.keys(adapters)
|
|
126
|
-
if (adapterNames.length === 0) {
|
|
127
|
-
throw new Error('No adapter found in config, please set adapters in config file')
|
|
128
|
-
}
|
|
129
|
-
return adapterNames[0]
|
|
130
|
-
})()
|
|
131
|
-
|
|
132
|
-
const adapter = await loadAdapter(adapterType)
|
|
133
|
-
await adapter.init?.(ctx)
|
|
134
|
-
|
|
135
|
-
const resolvedModel = resolveQueryModel({
|
|
136
|
-
config,
|
|
137
|
-
userConfig,
|
|
138
|
-
inputModel: adapterOptions.model
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
const originalOnEvent = adapterOptions.onEvent
|
|
142
|
-
const supportedAssetPlanAdapters = new Set(['claude-code', 'codex', 'opencode'])
|
|
143
|
-
const assetPlan = ctx.assets == null || !supportedAssetPlanAdapters.has(adapterType)
|
|
144
|
-
? undefined
|
|
145
|
-
: buildAdapterAssetPlan({
|
|
146
|
-
adapter: adapterType as 'claude-code' | 'codex' | 'opencode',
|
|
147
|
-
bundle: ctx.assets,
|
|
148
|
-
options: {
|
|
149
|
-
mcpServers: adapterOptions.mcpServers,
|
|
150
|
-
skills: adapterOptions.skills,
|
|
151
|
-
promptAssetIds: adapterOptions.promptAssetIds
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
const nativeBridgeDisabledEvents = adapterType === 'codex' && ctx.env.__VF_PROJECT_AI_CODEX_NATIVE_HOOKS_AVAILABLE__ === '1'
|
|
155
|
-
? ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop']
|
|
156
|
-
: adapterType === 'claude-code' && ctx.env.__VF_PROJECT_AI_CLAUDE_NATIVE_HOOKS_AVAILABLE__ === '1'
|
|
157
|
-
? ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop']
|
|
158
|
-
: adapterType === 'opencode' && ctx.env.__VF_PROJECT_AI_OPENCODE_NATIVE_HOOKS_AVAILABLE__ === '1'
|
|
159
|
-
? ['SessionStart', 'PreToolUse', 'PostToolUse', 'Stop']
|
|
160
|
-
: []
|
|
161
|
-
const hookBridge = createAdapterHookBridge({
|
|
162
|
-
ctx,
|
|
163
|
-
adapter: adapterType,
|
|
164
|
-
runtime: adapterOptions.runtime,
|
|
165
|
-
sessionId: adapterOptions.sessionId,
|
|
166
|
-
type: adapterOptions.type,
|
|
167
|
-
model: resolvedModel,
|
|
168
|
-
disabledEvents: nativeBridgeDisabledEvents
|
|
169
|
-
})
|
|
170
|
-
const wrappedOnEvent = (event: AdapterOutputEvent) => {
|
|
171
|
-
hookBridge.handleOutput(event)
|
|
172
|
-
|
|
173
|
-
if (event.type === 'init') {
|
|
174
|
-
originalOnEvent({
|
|
175
|
-
...event,
|
|
176
|
-
data: {
|
|
177
|
-
...event.data,
|
|
178
|
-
adapter: adapterType,
|
|
179
|
-
assetDiagnostics: assetPlan?.diagnostics ?? event.data.assetDiagnostics
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (event.type === 'exit') {
|
|
186
|
-
const { data } = event
|
|
187
|
-
|
|
188
|
-
void callHook('TaskStop', {
|
|
189
|
-
adapter: adapterType,
|
|
190
|
-
cwd: ctx.cwd,
|
|
191
|
-
sessionId: adapterOptions.sessionId,
|
|
192
|
-
|
|
193
|
-
options,
|
|
194
|
-
adapterOptions,
|
|
195
|
-
|
|
196
|
-
exitCode: data.exitCode,
|
|
197
|
-
stderr: data.stderr
|
|
198
|
-
}, ctx.env)
|
|
199
|
-
.catch((e) => {
|
|
200
|
-
logger.error('[Hook] TaskStop failed', e)
|
|
201
|
-
})
|
|
202
|
-
}
|
|
203
|
-
originalOnEvent(event)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const taskStartOutput = await callHook('TaskStart', {
|
|
207
|
-
adapter: adapterType,
|
|
208
|
-
cwd: ctx.cwd,
|
|
209
|
-
sessionId: adapterOptions.sessionId,
|
|
210
|
-
|
|
211
|
-
options,
|
|
212
|
-
adapterOptions
|
|
213
|
-
}, ctx.env)
|
|
214
|
-
if (taskStartOutput?.continue === false) {
|
|
215
|
-
throw new Error(taskStartOutput.stopReason ?? 'TaskStart hook blocked task startup')
|
|
216
|
-
}
|
|
217
|
-
await hookBridge.start()
|
|
218
|
-
const description = await hookBridge.prepareInitialPrompt(adapterOptions.description)
|
|
219
|
-
const session = await adapter.query(
|
|
220
|
-
ctx,
|
|
221
|
-
{
|
|
222
|
-
...adapterOptions,
|
|
223
|
-
assetPlan,
|
|
224
|
-
description,
|
|
225
|
-
model: resolvedModel,
|
|
226
|
-
onEvent: wrappedOnEvent
|
|
227
|
-
}
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
return { session: hookBridge.wrapSession(session), ctx }
|
|
231
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import z from 'zod'
|
|
2
|
-
|
|
3
|
-
export const TaskOptions = z.object({
|
|
4
|
-
type: z
|
|
5
|
-
.union([
|
|
6
|
-
z.literal('entity'),
|
|
7
|
-
// 基础库 API 能力开发
|
|
8
|
-
z.literal('spec')
|
|
9
|
-
])
|
|
10
|
-
.describe('任务模式'),
|
|
11
|
-
name: z.string().describe('垂类知识库基准目标'),
|
|
12
|
-
specParams: z.record(z.string()).describe('SPEC 执行时的参数').optional(),
|
|
13
|
-
runtime: z
|
|
14
|
-
.object({
|
|
15
|
-
type: z
|
|
16
|
-
.string()
|
|
17
|
-
.describe('选择特定的 AI CLI 类型'),
|
|
18
|
-
noDefaultSystemPrompt: z
|
|
19
|
-
.boolean()
|
|
20
|
-
.describe('是否禁用默认的系统提示')
|
|
21
|
-
.optional()
|
|
22
|
-
}),
|
|
23
|
-
description: z
|
|
24
|
-
.string()
|
|
25
|
-
.describe('本次任务的描述,介绍关于本次任务需要进行的工作')
|
|
26
|
-
.optional(),
|
|
27
|
-
sessionId: z
|
|
28
|
-
.string()
|
|
29
|
-
.describe(
|
|
30
|
-
'复用上次会话的历史消息作为本次任务的上下文。\n' +
|
|
31
|
-
'- 通常如果某个任务在执行的时候出现了非预期的错误,那么你可以通过传入相同的 sessionID 来继续这个会话\n' +
|
|
32
|
-
'- 如果有一个步骤需要间隔执行,比如说先执行任务 A 的 A-1,然后完成后执行 B 的 B-1,等 B-1 这个完成后回到 A-1 继续执行 A-2 时也可以使用'
|
|
33
|
-
)
|
|
34
|
-
.optional(),
|
|
35
|
-
frontendTimeout: z
|
|
36
|
-
.number()
|
|
37
|
-
.describe(
|
|
38
|
-
'前台等待时间的上限,单位秒,默认值为 8 分钟。' +
|
|
39
|
-
'在超过这个时间后该任务会被转化为一个后台任务,并直接返回当前的输出以及 sessionId,你可以通过 sessionId 来继续这个任务,或者查询任务的状态'
|
|
40
|
-
)
|
|
41
|
-
.default(8 * 60)
|
|
42
|
-
.optional(),
|
|
43
|
-
defaultModel: z.string().describe('默认的模型').optional()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
export type TaskOptions = z.infer<typeof TaskOptions>
|
|
47
|
-
|
|
48
|
-
export const MCPRunTasksOptions = z.object({
|
|
49
|
-
tasks: z
|
|
50
|
-
.array(TaskOptions)
|
|
51
|
-
.describe(
|
|
52
|
-
'子任务列表。传递多个子任务时会并发执行多个,可用于优化整体任务效率。'
|
|
53
|
-
),
|
|
54
|
-
|
|
55
|
-
bashDefaultTimeoutMs: z.number().optional(),
|
|
56
|
-
bashMaxTimeoutMs: z.number().optional(),
|
|
57
|
-
maxMcpOutputTokens: z.number().optional(),
|
|
58
|
-
mcpTimeout: z.number().optional(),
|
|
59
|
-
mcpToolTimeout: z.number().optional()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
export type MCPRunTasksOptions = z.infer<typeof MCPRunTasksOptions>
|
|
63
|
-
|
|
64
|
-
export const Options = z
|
|
65
|
-
.object({
|
|
66
|
-
taskId: z
|
|
67
|
-
.string()
|
|
68
|
-
.describe(
|
|
69
|
-
'唯一 id,会用于关联多个任务相关信息。\n' +
|
|
70
|
-
'如果是第一次执行,则不需要传入,会在返回值中自动生成一个,在下次创建或复用的时候必须传入该值以供记录相关上下文。\n' +
|
|
71
|
-
'用户指定了 taskId,则以用户指定的 taskId 为准,在创建任务时则必须要指定对应的 taskId 参数。'
|
|
72
|
-
)
|
|
73
|
-
.optional()
|
|
74
|
-
})
|
|
75
|
-
.extend(MCPRunTasksOptions.shape)
|
|
76
|
-
|
|
77
|
-
export type Options = z.infer<typeof Options>
|
|
78
|
-
|
|
79
|
-
export const Entity = z.object({
|
|
80
|
-
prompt: z
|
|
81
|
-
.string()
|
|
82
|
-
.describe('实体的描述,简单介绍一下当前实体的作用。')
|
|
83
|
-
.optional(),
|
|
84
|
-
promptPath: z
|
|
85
|
-
.string()
|
|
86
|
-
.describe(
|
|
87
|
-
'实体的描述文件路径,文件内容为实体的描述。默认为当前目录下的 AGENTS.md 文件。'
|
|
88
|
-
)
|
|
89
|
-
.optional(),
|
|
90
|
-
rules: z
|
|
91
|
-
.array(
|
|
92
|
-
z.union([
|
|
93
|
-
z.string(),
|
|
94
|
-
z.discriminatedUnion('type', [
|
|
95
|
-
z.object({
|
|
96
|
-
type: z.literal('local').optional(),
|
|
97
|
-
path: z.string(),
|
|
98
|
-
desc: z.string().optional()
|
|
99
|
-
}),
|
|
100
|
-
z.object({
|
|
101
|
-
type: z.literal('remote'),
|
|
102
|
-
tags: z.array(z.string()).describe('关键 tag').optional(),
|
|
103
|
-
desc: z.string().describe('知识库的描述').optional()
|
|
104
|
-
})
|
|
105
|
-
])
|
|
106
|
-
])
|
|
107
|
-
)
|
|
108
|
-
.optional()
|
|
109
|
-
.describe('垂类 agent 的规则集合'),
|
|
110
|
-
skills: z
|
|
111
|
-
.object({
|
|
112
|
-
type: z.union([z.literal('include'), z.literal('exclude')]),
|
|
113
|
-
list: z.array(z.string()).describe('技能列表')
|
|
114
|
-
})
|
|
115
|
-
.optional(),
|
|
116
|
-
mcpServers: z
|
|
117
|
-
.object({
|
|
118
|
-
include: z.array(z.string()).describe('包含的服务名称列表'),
|
|
119
|
-
exclude: z.array(z.string()).describe('排除的服务名称列表')
|
|
120
|
-
})
|
|
121
|
-
.optional(),
|
|
122
|
-
tools: z
|
|
123
|
-
.object({
|
|
124
|
-
include: z.array(z.string()).describe('包含的工具名称列表'),
|
|
125
|
-
exclude: z.array(z.string()).describe('排除的工具名称列表')
|
|
126
|
-
})
|
|
127
|
-
.optional(),
|
|
128
|
-
defaultModel: z.string().optional()
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
export type Entity = z.infer<typeof Entity>
|