mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
- PushNotificationTool、SendUserFileTool 添加 isEnabled() 使用 isBridgeEnabled() - BriefTool 的 isEnabled() 从 isBriefEnabled() 改为 isBridgeEnabled() - ExecuteTool 添加 isEnabled() 兜底检查,不可用时返回友好错误 - useReplBridge bridge 首次连接时插入 system 消息通知模型新工具可用 - 移除 toolSearch 中 firstParty base URL 白名单检测,默认启用 tool search Co-Authored-By: glm-5.1[1m] <zai-org@claude-code-best.win>
150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
import { feature } from 'bun:bundle'
|
|
import { z } from 'zod/v4'
|
|
import type { ToolResultBlockParam } from 'src/Tool.js'
|
|
import { buildTool } from 'src/Tool.js'
|
|
import { lazySchema } from 'src/utils/lazySchema.js'
|
|
import { logForDebugging } from 'src/utils/debug.js'
|
|
import { isBridgeEnabled } from 'src/bridge/bridgeEnabled.js'
|
|
|
|
const PUSH_NOTIFICATION_TOOL_NAME = 'PushNotification'
|
|
|
|
const inputSchema = lazySchema(() =>
|
|
z.strictObject({
|
|
title: z.string().describe('Title of the push notification.'),
|
|
body: z.string().describe('Body text of the push notification.'),
|
|
priority: z
|
|
.enum(['normal', 'high'])
|
|
.optional()
|
|
.describe(
|
|
'Notification priority. Use "high" for blockers or permission prompts.',
|
|
),
|
|
}),
|
|
)
|
|
type InputSchema = ReturnType<typeof inputSchema>
|
|
type PushInput = z.infer<InputSchema>
|
|
|
|
type PushOutput = { sent: boolean }
|
|
|
|
export const PushNotificationTool = buildTool({
|
|
name: PUSH_NOTIFICATION_TOOL_NAME,
|
|
searchHint: 'push notification mobile alert notify user',
|
|
maxResultSizeChars: 1_000,
|
|
strict: true,
|
|
|
|
get inputSchema(): InputSchema {
|
|
return inputSchema()
|
|
},
|
|
|
|
async description() {
|
|
return "Send a push notification to the user's mobile device"
|
|
},
|
|
async prompt() {
|
|
return `Send a push notification to the user's mobile device via Remote Control.
|
|
|
|
Use this when:
|
|
- A long-running task completes and the user may not be watching
|
|
- A permission prompt is waiting and you need user input
|
|
- Something urgent requires the user's attention
|
|
|
|
Requires Remote Control to be configured. Respects user notification settings (taskCompleteNotifEnabled, inputNeededNotifEnabled, agentPushNotifEnabled).`
|
|
},
|
|
|
|
isEnabled() {
|
|
return isBridgeEnabled()
|
|
},
|
|
isConcurrencySafe() {
|
|
return true
|
|
},
|
|
isReadOnly() {
|
|
return true
|
|
},
|
|
|
|
userFacingName() {
|
|
return 'Notify'
|
|
},
|
|
|
|
renderToolUseMessage(input: Partial<PushInput>) {
|
|
return `Push: ${input.title ?? '...'}`
|
|
},
|
|
|
|
mapToolResultToToolResultBlockParam(
|
|
content: PushOutput,
|
|
toolUseID: string,
|
|
): ToolResultBlockParam {
|
|
return {
|
|
tool_use_id: toolUseID,
|
|
type: 'tool_result',
|
|
content: content.sent
|
|
? 'Notification sent.'
|
|
: 'Failed to send notification.',
|
|
}
|
|
},
|
|
|
|
async call(input: PushInput, context) {
|
|
const appState = context.getAppState()
|
|
|
|
// Try bridge delivery first (for remote/mobile viewers)
|
|
if (appState.replBridgeEnabled) {
|
|
if (feature('BRIDGE_MODE')) {
|
|
try {
|
|
const { getBridgeAccessToken, getBridgeBaseUrl } = await import(
|
|
'src/bridge/bridgeConfig.js'
|
|
)
|
|
const { getSessionId } = await import('src/bootstrap/state.js')
|
|
const token = getBridgeAccessToken()
|
|
const sessionId = getSessionId()
|
|
if (token && sessionId) {
|
|
const baseUrl = getBridgeBaseUrl()
|
|
const axios = (await import('axios')).default
|
|
const response = await axios.post(
|
|
`${baseUrl}/v1/sessions/${sessionId}/events`,
|
|
{
|
|
events: [
|
|
{
|
|
type: 'push_notification',
|
|
title: input.title,
|
|
body: input.body,
|
|
priority: input.priority ?? 'normal',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
'Content-Type': 'application/json',
|
|
'anthropic-version': '2023-06-01',
|
|
},
|
|
timeout: 10_000,
|
|
validateStatus: (s: number) => s < 500,
|
|
},
|
|
)
|
|
if (response.status >= 200 && response.status < 300) {
|
|
logForDebugging(
|
|
`[PushNotification] delivered via bridge session=${sessionId}`,
|
|
)
|
|
return { data: { sent: true } }
|
|
}
|
|
logForDebugging(
|
|
`[PushNotification] bridge delivery failed: status=${response.status}`,
|
|
)
|
|
}
|
|
} catch (e) {
|
|
logForDebugging(`[PushNotification] bridge delivery error: ${e}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: no bridge available, push was not delivered to a remote device.
|
|
logForDebugging(
|
|
`[PushNotification] no bridge available, not delivered: ${input.title}`,
|
|
)
|
|
return {
|
|
data: {
|
|
sent: false,
|
|
error:
|
|
'No Remote Control bridge configured. Notification not delivered.',
|
|
},
|
|
}
|
|
},
|
|
})
|