File size: 2,478 Bytes
fdc8b59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { z } from 'zod'
import { buildTool, type ToolDef } from '../../Tool.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import {
  formatElapsed,
  formatGoalStatus,
} from '../../utils/goal.js'
import {
  DESCRIPTION,
  GET_GOAL_TOOL_NAME,
  GET_GOAL_TOOL_PROMPT,
} from './prompt.js'
import { renderToolResultMessage, renderToolUseMessage } from './UI.js'

const inputSchema = lazySchema(() => z.strictObject({}))
type InputSchema = ReturnType<typeof inputSchema>

const goalInfoSchema = z.object({
  id: z.string(),
  objective: z.string(),
  status: z.string(),
  elapsed_ms: z.number(),
  elapsed_formatted: z.string(),
  continuation_count: z.number(),
})

const outputSchema = lazySchema(() =>
  z.object({
    goal: goalInfoSchema.nullable(),
    message: z.string(),
  }),
)
type OutputSchema = ReturnType<typeof outputSchema>

export type Output = z.infer<OutputSchema>

export const GoalGetTool = buildTool({
  name: GET_GOAL_TOOL_NAME,
  searchHint: 'get current thread goal status and budget',
  maxResultSizeChars: 4_000,
  userFacingName: () => 'Get Goal',
  get inputSchema(): InputSchema {
    return inputSchema()
  },
  get outputSchema(): OutputSchema {
    return outputSchema()
  },
  isReadOnly() {
    return true
  },
  isConcurrencySafe() {
    return true
  },
  toAutoClassifierInput() {
    return ''
  },
  async description() {
    return DESCRIPTION
  },
  async prompt() {
    return GET_GOAL_TOOL_PROMPT
  },
  mapToolResultToToolResultBlockParam(output, toolUseID) {
    return {
      tool_use_id: toolUseID,
      type: 'tool_result',
      content: jsonStringify(output),
    }
  },
  renderToolUseMessage,
  renderToolResultMessage,
  async call(_input, { getAppState }) {
    const goal = getAppState().goal

    if (!goal) {
      return {
        data: {
          goal: null,
          message: 'No active goal.',
        },
      }
    }

    const now = Date.now()
    const elapsed = now - goal.startedAt

    return {
      data: {
        goal: {
          id: goal.id,
          objective: goal.objective,
          status: formatGoalStatus(goal.status),
          elapsed_ms: elapsed,
          elapsed_formatted: formatElapsed(elapsed),
          continuation_count: goal.continuationCount,
        },
        message: `Current goal: ${goal.objective} (${formatGoalStatus(goal.status)})`,
      },
    }
  },
} satisfies ToolDef<InputSchema, Output>)