@vibe-forge/core 0.3.0 → 0.5.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.
@@ -0,0 +1,179 @@
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
+ import { resolve } from 'node:path'
3
+ import process from 'node:process'
4
+
5
+ import { execCommand, execShellCommand, linkPreparedNodeModules, pathExists } from './utils'
6
+
7
+ interface CategoryWorkspaceInput {
8
+ workspaceFolder?: string
9
+ category: string
10
+ baseCommit: string
11
+ setupCommand: string
12
+ timeoutSec: number
13
+ }
14
+
15
+ interface CaseWorkspaceInput {
16
+ workspaceFolder?: string
17
+ category: string
18
+ title: string
19
+ runId: string
20
+ baseCommit: string
21
+ setupCommand: string
22
+ timeoutSec: number
23
+ }
24
+
25
+ export interface CategoryWorkspaceState {
26
+ workspacePath: string
27
+ gitRoot: string
28
+ }
29
+
30
+ export interface CaseWorkspaceState extends CategoryWorkspaceState {
31
+ caseWorkspacePath: string
32
+ }
33
+
34
+ const categoryWorkspaceInflight = new Map<string, Promise<CategoryWorkspaceState>>()
35
+
36
+ const resolveWorktreeRoot = (workspaceFolder = process.cwd()) =>
37
+ resolve(workspaceFolder, '.ai/worktress/benchmark')
38
+
39
+ const findGitRoot = async (workspaceFolder: string) => {
40
+ const result = await execCommand({
41
+ command: 'git',
42
+ args: ['rev-parse', '--show-toplevel'],
43
+ cwd: workspaceFolder
44
+ })
45
+ if (result.exitCode !== 0) {
46
+ throw new Error(result.stderr || 'Failed to resolve git root')
47
+ }
48
+ return result.stdout.trim()
49
+ }
50
+
51
+ const detachWorktree = async (gitRoot: string, worktreePath: string) => {
52
+ const result = await execCommand({
53
+ command: 'git',
54
+ args: ['worktree', 'remove', '--force', worktreePath],
55
+ cwd: gitRoot
56
+ })
57
+ const output = `${result.stdout}\n${result.stderr}`
58
+ if (result.exitCode !== 0 && !output.includes('is not a working tree')) {
59
+ throw new Error(output.trim() || `Failed to remove worktree: ${worktreePath}`)
60
+ }
61
+ }
62
+
63
+ export const ensureCategoryWorkspace = async (input: CategoryWorkspaceInput): Promise<CategoryWorkspaceState> => {
64
+ const workspaceFolder = input.workspaceFolder ?? process.cwd()
65
+ const lockKey = `${workspaceFolder}:${input.category}`
66
+ const inflight = categoryWorkspaceInflight.get(lockKey)
67
+ if (inflight != null) {
68
+ return inflight
69
+ }
70
+
71
+ const promise = (async () => {
72
+ const gitRoot = await findGitRoot(workspaceFolder)
73
+ const worktreeRoot = resolveWorktreeRoot(workspaceFolder)
74
+ const workspacePath = resolve(worktreeRoot, input.category)
75
+ const statePath = resolve(workspacePath, '.benchmark-state.json')
76
+
77
+ await mkdir(worktreeRoot, { recursive: true })
78
+
79
+ const currentState = await pathExists(statePath)
80
+ ? JSON.parse(await readFile(statePath, 'utf-8')) as {
81
+ baseCommit?: string
82
+ setupCommand?: string
83
+ }
84
+ : null
85
+
86
+ const needsRecreate = !await pathExists(workspacePath) ||
87
+ currentState?.baseCommit !== input.baseCommit ||
88
+ currentState?.setupCommand !== input.setupCommand
89
+
90
+ if (needsRecreate) {
91
+ if (await pathExists(workspacePath)) {
92
+ await detachWorktree(gitRoot, workspacePath)
93
+ await rm(workspacePath, { force: true, recursive: true })
94
+ }
95
+ const addResult = await execCommand({
96
+ command: 'git',
97
+ args: ['worktree', 'add', '--force', '--detach', workspacePath, input.baseCommit],
98
+ cwd: gitRoot
99
+ })
100
+ if (addResult.exitCode !== 0) {
101
+ throw new Error(addResult.stderr || `Failed to create worktree for ${input.category}`)
102
+ }
103
+ if (input.setupCommand.trim() !== '') {
104
+ const setupResult = await execShellCommand({
105
+ command: input.setupCommand,
106
+ cwd: workspacePath,
107
+ timeoutMs: input.timeoutSec * 1000
108
+ })
109
+ if (setupResult.exitCode !== 0) {
110
+ throw new Error(setupResult.stderr || setupResult.stdout || 'Failed to prepare category workspace')
111
+ }
112
+ }
113
+ await writeFile(statePath, `${JSON.stringify({
114
+ baseCommit: input.baseCommit,
115
+ setupCommand: input.setupCommand
116
+ }, null, 2)}\n`, 'utf-8')
117
+ }
118
+
119
+ return {
120
+ workspacePath,
121
+ gitRoot
122
+ }
123
+ })()
124
+
125
+ categoryWorkspaceInflight.set(lockKey, promise)
126
+
127
+ try {
128
+ return await promise
129
+ } finally {
130
+ categoryWorkspaceInflight.delete(lockKey)
131
+ }
132
+ }
133
+
134
+ export const createCaseWorkspace = async (input: CaseWorkspaceInput): Promise<CaseWorkspaceState> => {
135
+ const categoryWorkspace = await ensureCategoryWorkspace(input)
136
+ const worktreeRoot = resolveWorktreeRoot(input.workspaceFolder ?? process.cwd())
137
+ const caseRoot = resolve(worktreeRoot, '.cases', input.category)
138
+ const caseWorkspacePath = resolve(caseRoot, `${input.title}-${input.runId}`)
139
+
140
+ await mkdir(caseRoot, { recursive: true })
141
+ if (await pathExists(caseWorkspacePath)) {
142
+ await detachWorktree(categoryWorkspace.gitRoot, caseWorkspacePath)
143
+ await rm(caseWorkspacePath, { force: true, recursive: true })
144
+ }
145
+
146
+ const addResult = await execCommand({
147
+ command: 'git',
148
+ args: ['worktree', 'add', '--force', '--detach', caseWorkspacePath, input.baseCommit],
149
+ cwd: categoryWorkspace.gitRoot
150
+ })
151
+ if (addResult.exitCode !== 0) {
152
+ throw new Error(addResult.stderr || `Failed to create case workspace for ${input.category}/${input.title}`)
153
+ }
154
+
155
+ await linkPreparedNodeModules(categoryWorkspace.workspacePath, caseWorkspacePath)
156
+
157
+ if (input.setupCommand.trim() !== '') {
158
+ const setupResult = await execShellCommand({
159
+ command: input.setupCommand,
160
+ cwd: caseWorkspacePath,
161
+ timeoutMs: input.timeoutSec * 1000
162
+ })
163
+ if (setupResult.exitCode !== 0) {
164
+ throw new Error(setupResult.stderr || setupResult.stdout || 'Failed to prepare case workspace')
165
+ }
166
+ }
167
+
168
+ return {
169
+ ...categoryWorkspace,
170
+ caseWorkspacePath
171
+ }
172
+ }
173
+
174
+ export const disposeCaseWorkspace = async (state: CaseWorkspaceState) => {
175
+ await detachWorktree(state.gitRoot, state.caseWorkspacePath)
176
+ if (await pathExists(state.caseWorkspacePath)) {
177
+ await rm(state.caseWorkspacePath, { force: true, recursive: true })
178
+ }
179
+ }
@@ -134,6 +134,13 @@ const updateConfigSection = (config: Config, section: string, value: unknown): C
134
134
  )
135
135
  return nextConfig
136
136
  }
137
+ case 'channels': {
138
+ updateField(
139
+ 'channels',
140
+ mergeMaskedValues(sectionValue, config.channels) as Config['channels']
141
+ )
142
+ return nextConfig
143
+ }
137
144
  case 'adapters': {
138
145
  updateField('adapters', mergeMaskedValues(sectionValue, config.adapters) as Config['adapters'])
139
146
  return nextConfig
@@ -8,7 +8,7 @@ export async function generateAdapterQueryOptions(
8
8
  type: 'spec' | 'entity' | undefined,
9
9
  name?: string,
10
10
  cwd: string = process.cwd()
11
- ): Promise<Partial<AdapterQueryOptions>> {
11
+ ) {
12
12
  const loader = new DefinitionLoader(cwd)
13
13
  const options: Partial<AdapterQueryOptions> = {}
14
14
  const systemPromptParts: string[] = []
@@ -82,5 +82,15 @@ export async function generateAdapterQueryOptions(
82
82
  )
83
83
 
84
84
  options.systemPrompt = systemPromptParts.join('\n\n')
85
- return options
85
+ return [
86
+ {
87
+ rules,
88
+ targetSkills,
89
+ entities,
90
+ skills,
91
+ specs,
92
+ targetBody
93
+ },
94
+ options
95
+ ] as const
86
96
  }
@@ -1,7 +1,7 @@
1
1
  import process from 'node:process'
2
2
 
3
- import type { AdapterCtx, AdapterQueryOptions } from '@vibe-forge/core'
4
3
  import { loadConfig } from '@vibe-forge/core'
4
+ import type { AdapterCtx, AdapterQueryOptions } from '@vibe-forge/core/adapter'
5
5
  import { getCache, setCache } from '@vibe-forge/core/utils/cache'
6
6
  import { createLogger } from '@vibe-forge/core/utils/create-logger'
7
7
  import { uuid } from '@vibe-forge/core/utils/uuid'
@@ -1,10 +1,91 @@
1
- import type { AdapterCtx, AdapterOutputEvent, AdapterQueryOptions, TaskDetail } from '@vibe-forge/core'
2
- import { loadAdapter } from '@vibe-forge/core'
3
- import { setCache } from '@vibe-forge/core/utils/cache'
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 type { TaskDetail } from '#~/types.js'
5
+ import { callHook } from '#~/utils/api.js'
4
6
 
5
7
  import { prepare } from './prepare'
6
8
  import type { RunTaskOptions } from './type'
7
9
 
10
+ const normalizeNonEmptyString = (value: unknown) => (
11
+ typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined
12
+ )
13
+
14
+ const pickFirstNonEmptyString = (values: unknown[]) =>
15
+ values
16
+ .map(normalizeNonEmptyString)
17
+ .find((value): value is string => value != null)
18
+
19
+ const resolveQueryModel = (params: {
20
+ config: AdapterCtx['configs'][0]
21
+ userConfig: AdapterCtx['configs'][1]
22
+ inputModel?: string
23
+ }) => {
24
+ const inputModel = normalizeNonEmptyString(params.inputModel)
25
+ // User explicitly provided a model → pass through as-is.
26
+ // The adapter decides CCR vs native based on whether it contains ",".
27
+ if (inputModel != null) return inputModel
28
+
29
+ // No explicit model → auto-resolve from modelServices config.
30
+ // Produces "service,model" format when services are configured,
31
+ // which signals the adapter to route through CCR.
32
+ const mergedModelServices = {
33
+ ...(params.config?.modelServices ?? {}),
34
+ ...(params.userConfig?.modelServices ?? {})
35
+ }
36
+ const mergedDefaultModel = pickFirstNonEmptyString(
37
+ [
38
+ params.userConfig?.defaultModel,
39
+ params.config?.defaultModel
40
+ ]
41
+ )
42
+ const mergedDefaultModelService = pickFirstNonEmptyString(
43
+ [
44
+ params.userConfig?.defaultModelService,
45
+ params.config?.defaultModelService
46
+ ]
47
+ )
48
+
49
+ const serviceEntries = Object.entries(mergedModelServices)
50
+ const modelToService = new Map<string, string>()
51
+ const availableModels: string[] = []
52
+ for (const [serviceKey, serviceValue] of serviceEntries) {
53
+ const service = (serviceValue != null && typeof serviceValue === 'object')
54
+ ? serviceValue as ModelServiceConfig
55
+ : undefined
56
+ const models = Array.isArray(service?.models)
57
+ ? service?.models.filter(item => typeof item === 'string' && item.trim() !== '')
58
+ : []
59
+ for (const model of models) {
60
+ if (!modelToService.has(model)) modelToService.set(model, serviceKey)
61
+ availableModels.push(model)
62
+ }
63
+ }
64
+
65
+ if (availableModels.length === 0) return undefined
66
+
67
+ const resolveDefaultModel = () => {
68
+ if (mergedDefaultModel && modelToService.has(mergedDefaultModel)) return mergedDefaultModel
69
+ if (mergedDefaultModelService && mergedModelServices[mergedDefaultModelService]) {
70
+ const service = mergedModelServices[mergedDefaultModelService] as ModelServiceConfig | undefined
71
+ const models = Array.isArray(service?.models)
72
+ ? service?.models.filter((item: unknown) => typeof item === 'string' && (item as string).trim() !== '')
73
+ : []
74
+ if (models.length > 0) return models[0]
75
+ }
76
+ return availableModels[0]
77
+ }
78
+
79
+ const resolvedModel = resolveDefaultModel()
80
+ if (!resolvedModel) return undefined
81
+
82
+ const resolvedService = modelToService.get(resolvedModel) ??
83
+ mergedDefaultModelService ??
84
+ serviceEntries[0]?.[0]
85
+
86
+ return resolvedService ? `${resolvedService},${resolvedModel}` : resolvedModel
87
+ }
88
+
8
89
  declare module '@vibe-forge/core' {
9
90
  interface Cache {
10
91
  base: Omit<AdapterCtx, 'logger' | 'cache'>
@@ -25,12 +106,6 @@ export const run = async (
25
106
 
26
107
  await cache.set('base', base)
27
108
 
28
- const startTime = Date.now()
29
- logger.info('[Framework] Process start', {
30
- ...base,
31
- adapterOptions,
32
- startDateTime: new Date(startTime).toLocaleString()
33
- })
34
109
  const adapters = {
35
110
  ...config?.adapters,
36
111
  ...userConfig?.adapters
@@ -52,47 +127,52 @@ export const run = async (
52
127
  return adapterNames[0]
53
128
  })()
54
129
 
55
- const detail: TaskDetail = {
56
- ctxId: ctx.ctxId,
57
- sessionId: adapterOptions.sessionId,
58
- status: 'running',
59
- startTime,
60
- description: adapterOptions.description,
61
- adapter: adapterType,
62
- model: adapterOptions.model
63
- }
64
-
65
- const saveDetail = async (d: TaskDetail) => {
66
- // Save to caches/ctxId/detail.json (ignoring sessionId)
67
- await setCache(ctx.cwd, ctx.ctxId, undefined, 'detail', d)
68
- }
69
-
70
- await saveDetail(detail)
71
-
72
130
  const originalOnEvent = adapterOptions.onEvent
73
131
  const wrappedOnEvent = (event: AdapterOutputEvent) => {
74
132
  if (event.type === 'exit') {
75
- detail.status = event.data.exitCode === 0 ? 'completed' : 'failed'
76
- detail.endTime = Date.now()
77
- detail.exitCode = event.data.exitCode ?? undefined
78
- void saveDetail(detail).catch(console.error)
133
+ const { data } = event
134
+
135
+ void callHook('TaskStop', {
136
+ adapter: adapterType,
137
+ cwd: ctx.cwd,
138
+ sessionId: adapterOptions.sessionId,
139
+
140
+ options,
141
+ adapterOptions,
142
+
143
+ exitCode: data.exitCode,
144
+ stderr: data.stderr
145
+ }, ctx.env)
146
+ .catch((e) => {
147
+ logger.error('[Hook] TaskStop failed', e)
148
+ })
79
149
  }
80
150
  originalOnEvent(event)
81
151
  }
82
152
 
83
153
  const adapter = await loadAdapter(adapterType)
154
+ const resolvedModel = resolveQueryModel({
155
+ config,
156
+ userConfig,
157
+ inputModel: adapterOptions.model
158
+ })
159
+
160
+ await callHook('TaskStart', {
161
+ adapter: adapterType,
162
+ cwd: ctx.cwd,
163
+ sessionId: adapterOptions.sessionId,
164
+
165
+ options,
166
+ adapterOptions
167
+ }, ctx.env)
84
168
  const session = await adapter.query(
85
169
  ctx,
86
170
  {
87
171
  ...adapterOptions,
172
+ model: resolvedModel,
88
173
  onEvent: wrappedOnEvent
89
174
  }
90
175
  )
91
176
 
92
- if (session.pid) {
93
- detail.pid = session.pid
94
- await saveDetail(detail)
95
- }
96
-
97
177
  return { session, ctx }
98
178
  }
package/src/env.ts CHANGED
@@ -8,8 +8,9 @@ export interface ServerEnv {
8
8
  __VF_PROJECT_AI_SERVER_LOG_DIR__: string
9
9
  __VF_PROJECT_AI_SERVER_LOG_LEVEL__: 'debug' | 'info' | 'warn' | 'error'
10
10
  __VF_PROJECT_AI_SERVER_ALLOW_CORS__: boolean
11
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_PATH__?: string
12
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_ARGS__?: string
11
+ __VF_PROJECT_AI_CLIENT_MODE__?: 'dev' | 'static'
12
+ __VF_PROJECT_AI_CLIENT_BASE__?: string
13
+ __VF_PROJECT_AI_CLIENT_DIST_PATH__?: string
13
14
  }
14
15
 
15
16
  export function loadEnv(): ServerEnv {
@@ -21,9 +22,9 @@ export function loadEnv(): ServerEnv {
21
22
  __VF_PROJECT_AI_SERVER_LOG_DIR__ = '.logs',
22
23
  __VF_PROJECT_AI_SERVER_LOG_LEVEL__ = 'info',
23
24
  __VF_PROJECT_AI_SERVER_ALLOW_CORS__,
24
-
25
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_PATH__,
26
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_ARGS__
25
+ __VF_PROJECT_AI_CLIENT_MODE__ = 'static',
26
+ __VF_PROJECT_AI_CLIENT_BASE__,
27
+ __VF_PROJECT_AI_CLIENT_DIST_PATH__
27
28
  } = processEnv || {}
28
29
  return {
29
30
  __VF_PROJECT_AI_SERVER_HOST__,
@@ -36,8 +37,8 @@ export function loadEnv(): ServerEnv {
36
37
  __VF_PROJECT_AI_SERVER_ALLOW_CORS__: __VF_PROJECT_AI_SERVER_ALLOW_CORS__ != null
37
38
  ? __VF_PROJECT_AI_SERVER_ALLOW_CORS__ === 'true'
38
39
  : true,
39
-
40
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_PATH__,
41
- __VF_PROJECT_AI_ADAPTER_CLAUDE_CODE_CLI_ARGS__
40
+ __VF_PROJECT_AI_CLIENT_MODE__: __VF_PROJECT_AI_CLIENT_MODE__ as ServerEnv['__VF_PROJECT_AI_CLIENT_MODE__'],
41
+ __VF_PROJECT_AI_CLIENT_BASE__,
42
+ __VF_PROJECT_AI_CLIENT_DIST_PATH__
42
43
  }
43
44
  }
package/src/hooks/type.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ToolInput, ToolOutput } from '../tools'
2
+
1
3
  /**
2
4
  * https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input
3
5
  */
@@ -5,96 +7,8 @@ export interface HookInputCore {
5
7
  cwd: string
6
8
  sessionId: string
7
9
  hookEventName: keyof HookInputs
8
- transcriptPath: string
9
- }
10
-
11
- /**
12
- * https://docs.anthropic.com/en/docs/claude-code/settings#tools-available-to-claude
13
- */
14
- export interface ToolInputs {
15
- mcp__TmarAITools__notify: {
16
- title: string
17
- description: string
18
- sound?: boolean
19
- }
20
- 'mcp__TmarAITools__run-tasks': {
21
- taskId: string
22
- agents: number[]
23
- }
24
- Read: {
25
- filePath: string
26
- }
27
- LS: {
28
- path: string
29
- }
30
- Edit: {
31
- filePath: string
32
- newString: string
33
- oldString: string
34
- }
35
- Write: {
36
- filePath: string
37
- content: string
38
- }
39
- Bash: {
40
- command: string
41
- description: string
42
- }
43
- }
44
-
45
- export interface ToolOutputs {
46
- mcp__TmarAITools__notify: {}
47
- 'mcp__TmarAITools__run-tasks': {}
48
- Read: {
49
- type: 'text' | (string & {})
50
- file: {
51
- filePath: string
52
- content: string
53
- numLines: number
54
- startLine: number
55
- totalLines: number
56
- }
57
- }
58
- LS: string
59
- Edit: {
60
- filePath: string
61
- newString: string
62
- oldString: string
63
- originalFile: string
64
- }
65
- Write: {
66
- filePath: string
67
- content: string
68
- }
69
- Bash: {
70
- stdout: string
71
- stderr: string
72
- interrupted: boolean
73
- isImage: boolean
74
- }
75
10
  }
76
11
 
77
- // dprint-ignore
78
- export type ToolInput = keyof ToolInputs extends infer Keys
79
- ? Keys extends infer Key extends keyof ToolInputs
80
- ? {
81
- toolName: Key
82
- toolInput: ToolInputs[Key]
83
- }
84
- : never
85
- : never
86
-
87
- // dprint-ignore
88
- export type ToolOutput = keyof ToolOutputs extends infer Keys
89
- ? Keys extends infer Key extends keyof ToolOutputs
90
- ? {
91
- toolName: Key
92
- toolInput: ToolInputs[Key]
93
- toolResponse?: ToolOutputs[Key]
94
- }
95
- : never
96
- : never
97
-
98
12
  export interface HookInputs {
99
13
  /**
100
14
  * https://docs.anthropic.com/en/docs/claude-code/hooks#pretooluse-input
@@ -113,6 +27,34 @@ export interface HookInputs {
113
27
  SessionEnd: HookInputCore & {
114
28
  reason: string
115
29
  }
30
+
31
+ StartTasks: HookInputCore & {
32
+ tasks: Array<{
33
+ description: string
34
+ type: 'default' | 'spec' | 'entity'
35
+ name?: string
36
+ adapter?: string
37
+ background?: boolean
38
+ }>
39
+ }
40
+ GenerateSystemPrompt: HookInputCore & {
41
+ type?: 'spec' | 'entity'
42
+ name?: string
43
+ data?: unknown
44
+ }
45
+ TaskStart: HookInputCore & {
46
+ adapter?: string
47
+ options: unknown
48
+ adapterOptions: unknown
49
+ }
50
+ TaskStop: HookInputCore & {
51
+ exitCode?: number
52
+ stderr?: string
53
+ adapter?: string
54
+
55
+ options: unknown
56
+ adapterOptions: unknown
57
+ }
116
58
  }
117
59
 
118
60
  export type HookInput = HookInputs[keyof HookInputs]
@@ -168,6 +110,11 @@ export interface HookOutputs {
168
110
  SessionEnd: HookOutputCore
169
111
  SubagentStop: HookOutputCore
170
112
  PreCompact: HookOutputCore
113
+
114
+ StartTasks: HookOutputCore
115
+ GenerateSystemPrompt: HookOutputCore
116
+ TaskStart: HookOutputCore
117
+ TaskStop: HookOutputCore
171
118
  }
172
119
 
173
120
  export type HookOutput = HookOutputs[keyof HookOutputs]
package/src/index.ts CHANGED
@@ -1,9 +1,10 @@
1
- export * from './adapter'
2
1
  export * from './config'
2
+ export * from './controllers/benchmark'
3
3
  export * from './controllers/config'
4
4
  export * from './controllers/system'
5
5
  export * from './env'
6
6
  export * from './hooks'
7
7
  export * from './schema'
8
+ export * from './tools'
8
9
  export * from './types'
9
10
  export * from './ws'
package/src/tools.ts ADDED
@@ -0,0 +1,46 @@
1
+ export interface StopTaskToolInput {
2
+ task_id?: string
3
+ }
4
+
5
+ export interface StartTasksToolInput {
6
+ tasks: Array<{
7
+ description?: string
8
+ type?: 'default' | 'spec' | 'entity'
9
+ name?: string
10
+ adapter?: string
11
+ permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions'
12
+ background?: boolean
13
+ }>
14
+ }
15
+
16
+ export interface GetTaskInfoToolInput {
17
+ taskId: string
18
+ }
19
+
20
+ export interface ListTasksToolInput {}
21
+
22
+ export interface ToolInputs {
23
+ StartTasks: StartTasksToolInput
24
+ GetTaskInfo: GetTaskInfoToolInput
25
+ ListTasks: ListTasksToolInput
26
+ StopTask: StopTaskToolInput
27
+ }
28
+
29
+ export interface ToolOutputs {}
30
+
31
+ export type ToolName = keyof ToolInputs
32
+
33
+ export type ToolInput = keyof ToolInputs extends infer Keys ? Keys extends infer Key extends keyof ToolInputs ? {
34
+ toolName: Key
35
+ toolInput: ToolInputs[Key]
36
+ }
37
+ : never
38
+ : never
39
+
40
+ export type ToolOutput = keyof ToolOutputs extends infer Keys ? Keys extends infer Key extends keyof ToolOutputs ? {
41
+ toolName: Key
42
+ toolInput: ToolInputs[Key]
43
+ toolResponse?: ToolOutputs[Key]
44
+ }
45
+ : never
46
+ : never
package/src/types.ts CHANGED
@@ -25,6 +25,7 @@ export interface Session {
25
25
 
26
26
  export type ChatMessageContent =
27
27
  | { type: 'text'; text: string }
28
+ | { type: 'image'; url: string; name?: string; size?: number; mimeType?: string }
28
29
  | { type: 'tool_use'; id: string; name: string; input: any }
29
30
  | { type: 'tool_result'; tool_use_id: string; content: any; is_error?: boolean }
30
31
 
@@ -60,7 +61,7 @@ export interface TaskDetail {
60
61
  startTime: number
61
62
  endTime?: number
62
63
  description?: string
63
- adapter: string
64
+ adapterType?: string
64
65
  model?: string
65
66
  exitCode?: number
66
67
  }