File size: 4,715 Bytes
064bfd6 | 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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | import axios from 'axios'
import { z } from 'zod/v4'
import { getOauthConfig } from '../../constants/oauth.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import { getOrganizationUUID } from '../../services/oauth/client.js'
import { isPolicyAllowed } from '../../services/policyLimits/index.js'
import type { ToolUseContext } from '../../Tool.js'
import { buildTool, type ToolDef } from '../../Tool.js'
import {
checkAndRefreshOAuthTokenIfNeeded,
getClaudeAIOAuthTokens,
} from '../../utils/auth.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { DESCRIPTION, PROMPT, REMOTE_TRIGGER_TOOL_NAME } from './prompt.js'
import { renderToolResultMessage, renderToolUseMessage } from './UI.js'
const inputSchema = lazySchema(() =>
z.strictObject({
action: z.enum(['list', 'get', 'create', 'update', 'run']),
trigger_id: z
.string()
.regex(/^[\w-]+$/)
.optional()
.describe('Required for get, update, and run'),
body: z
.record(z.string(), z.unknown())
.optional()
.describe('JSON body for create and update'),
}),
)
type InputSchema = ReturnType<typeof inputSchema>
export type Input = z.infer<InputSchema>
const outputSchema = lazySchema(() =>
z.object({
status: z.number(),
json: z.string(),
}),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
const TRIGGERS_BETA = 'ccr-triggers-2026-01-30'
export const RemoteTriggerTool = buildTool({
name: REMOTE_TRIGGER_TOOL_NAME,
searchHint: 'manage scheduled remote agent triggers',
maxResultSizeChars: 100_000,
shouldDefer: true,
get inputSchema(): InputSchema {
return inputSchema()
},
get outputSchema(): OutputSchema {
return outputSchema()
},
isEnabled() {
return (
getFeatureValue_CACHED_MAY_BE_STALE('tengu_surreal_dali', false) &&
isPolicyAllowed('allow_remote_sessions')
)
},
isConcurrencySafe() {
return true
},
isReadOnly(input: Input) {
return input.action === 'list' || input.action === 'get'
},
toAutoClassifierInput(input: Input) {
return `RemoteTrigger ${input.action}${input.trigger_id ? ` ${input.trigger_id}` : ''}`
},
async description() {
return DESCRIPTION
},
async prompt() {
return PROMPT
},
async call(input: Input, context: ToolUseContext) {
await checkAndRefreshOAuthTokenIfNeeded()
const accessToken = getClaudeAIOAuthTokens()?.accessToken
if (!accessToken) {
throw new Error(
'Not authenticated with a claude.ai account. Run /login and try again.',
)
}
const orgUUID = await getOrganizationUUID()
if (!orgUUID) {
throw new Error('Unable to resolve organization UUID.')
}
const base = `${getOauthConfig().BASE_API_URL}/v1/code/triggers`
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
'anthropic-beta': TRIGGERS_BETA,
'x-organization-uuid': orgUUID,
}
const { action, trigger_id, body } = input
let method: 'GET' | 'POST'
let url: string
let data: unknown
switch (action) {
case 'list':
method = 'GET'
url = base
break
case 'get':
if (!trigger_id) throw new Error('get requires trigger_id')
method = 'GET'
url = `${base}/${trigger_id}`
break
case 'create':
if (!body) throw new Error('create requires body')
method = 'POST'
url = base
data = body
break
case 'update':
if (!trigger_id) throw new Error('update requires trigger_id')
if (!body) throw new Error('update requires body')
method = 'POST'
url = `${base}/${trigger_id}`
data = body
break
case 'run':
if (!trigger_id) throw new Error('run requires trigger_id')
method = 'POST'
url = `${base}/${trigger_id}/run`
data = {}
break
}
const res = await axios.request({
method,
url,
headers,
data,
timeout: 20_000,
signal: context.abortController.signal,
validateStatus: () => true,
})
return {
data: {
status: res.status,
json: jsonStringify(res.data),
},
}
},
mapToolResultToToolResultBlockParam(output, toolUseID) {
return {
tool_use_id: toolUseID,
type: 'tool_result',
content: `HTTP ${output.status}\n${output.json}`,
}
},
renderToolUseMessage,
renderToolResultMessage,
} satisfies ToolDef<InputSchema, Output>)
|