the claude code sourcemaps leaked march 31
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 204 lines 7.7 kB view raw
1import { feature } from 'bun:bundle' 2import { z } from 'zod/v4' 3import { getKairosActive, getUserMsgOptIn } from '../../bootstrap/state.js' 4import { getFeatureValue_CACHED_WITH_REFRESH } from '../../services/analytics/growthbook.js' 5import { logEvent } from '../../services/analytics/index.js' 6import type { ValidationResult } from '../../Tool.js' 7import { buildTool, type ToolDef } from '../../Tool.js' 8import { isEnvTruthy } from '../../utils/envUtils.js' 9import { lazySchema } from '../../utils/lazySchema.js' 10import { plural } from '../../utils/stringUtils.js' 11import { resolveAttachments, validateAttachmentPaths } from './attachments.js' 12import { 13 BRIEF_TOOL_NAME, 14 BRIEF_TOOL_PROMPT, 15 DESCRIPTION, 16 LEGACY_BRIEF_TOOL_NAME, 17} from './prompt.js' 18import { renderToolResultMessage, renderToolUseMessage } from './UI.js' 19 20const inputSchema = lazySchema(() => 21 z.strictObject({ 22 message: z 23 .string() 24 .describe('The message for the user. Supports markdown formatting.'), 25 attachments: z 26 .array(z.string()) 27 .optional() 28 .describe( 29 'Optional file paths (absolute or relative to cwd) to attach. Use for photos, screenshots, diffs, logs, or any file the user should see alongside your message.', 30 ), 31 status: z 32 .enum(['normal', 'proactive']) 33 .describe( 34 "Use 'proactive' when you're surfacing something the user hasn't asked for and needs to see now — task completion while they're away, a blocker you hit, an unsolicited status update. Use 'normal' when replying to something the user just said.", 35 ), 36 }), 37) 38type InputSchema = ReturnType<typeof inputSchema> 39 40// attachments MUST remain optional — resumed sessions replay pre-attachment 41// outputs verbatim and a required field would crash the UI renderer on resume. 42const outputSchema = lazySchema(() => 43 z.object({ 44 message: z.string().describe('The message'), 45 attachments: z 46 .array( 47 z.object({ 48 path: z.string(), 49 size: z.number(), 50 isImage: z.boolean(), 51 file_uuid: z.string().optional(), 52 }), 53 ) 54 .optional() 55 .describe('Resolved attachment metadata'), 56 sentAt: z 57 .string() 58 .optional() 59 .describe( 60 'ISO timestamp captured at tool execution on the emitting process. Optional — resumed sessions replay pre-sentAt outputs verbatim.', 61 ), 62 }), 63) 64type OutputSchema = ReturnType<typeof outputSchema> 65export type Output = z.infer<OutputSchema> 66 67const KAIROS_BRIEF_REFRESH_MS = 5 * 60 * 1000 68 69/** 70 * Entitlement check — is the user ALLOWED to use Brief? Combines build-time 71 * flags with runtime GB gate + assistant-mode passthrough. No opt-in check 72 * here — this decides whether opt-in should be HONORED, not whether the user 73 * has opted in. 74 * 75 * Build-time OR-gated on KAIROS || KAIROS_BRIEF (same pattern as 76 * PROACTIVE || KAIROS): assistant mode depends on Brief, so KAIROS alone 77 * must bundle it. KAIROS_BRIEF lets Brief ship independently. 78 * 79 * Use this to decide whether `--brief` / `defaultView: 'chat'` / `--tools` 80 * listing should be honored. Use `isBriefEnabled()` to decide whether the 81 * tool is actually active in the current session. 82 * 83 * CLAUDE_CODE_BRIEF env var force-grants entitlement for dev/testing — 84 * bypasses the GB gate so you can test without being enrolled. Still 85 * requires an opt-in action to activate (--brief, defaultView, etc.), but 86 * the env var alone also sets userMsgOptIn via maybeActivateBrief(). 87 */ 88export function isBriefEntitled(): boolean { 89 // Positive ternary — see docs/feature-gating.md. Negative early-return 90 // would not eliminate the GB gate string from external builds. 91 return feature('KAIROS') || feature('KAIROS_BRIEF') 92 ? getKairosActive() || 93 isEnvTruthy(process.env.CLAUDE_CODE_BRIEF) || 94 getFeatureValue_CACHED_WITH_REFRESH( 95 'tengu_kairos_brief', 96 false, 97 KAIROS_BRIEF_REFRESH_MS, 98 ) 99 : false 100} 101 102/** 103 * Unified activation gate for the Brief tool. Governs model-facing behavior 104 * as a unit: tool availability, system prompt section (getBriefSection), 105 * tool-deferral bypass (isDeferredTool), and todo-nag suppression. 106 * 107 * Activation requires explicit opt-in (userMsgOptIn) set by one of: 108 * - `--brief` CLI flag (maybeActivateBrief in main.tsx) 109 * - `defaultView: 'chat'` in settings (main.tsx init) 110 * - `/brief` slash command (brief.ts) 111 * - `/config` defaultView picker (Config.tsx) 112 * - SendUserMessage in `--tools` / SDK `tools` option (main.tsx) 113 * - CLAUDE_CODE_BRIEF env var (maybeActivateBrief — dev/testing bypass) 114 * Assistant mode (kairosActive) bypasses opt-in since its system prompt 115 * hard-codes "you MUST use SendUserMessage" (systemPrompt.md:14). 116 * 117 * The GB gate is re-checked here as a kill-switch AND — flipping 118 * tengu_kairos_brief off mid-session disables the tool on the next 5-min 119 * refresh even for opted-in sessions. No opt-in → always false regardless 120 * of GB (this is the fix for "brief defaults on for enrolled ants"). 121 * 122 * Called from Tool.isEnabled() (lazy, post-init), never at module scope. 123 * getKairosActive() and getUserMsgOptIn() are set in main.tsx before any 124 * caller reaches here. 125 */ 126export function isBriefEnabled(): boolean { 127 // Top-level feature() guard is load-bearing for DCE: Bun can constant-fold 128 // the ternary to `false` in external builds and then dead-code the BriefTool 129 // object. Composing isBriefEntitled() alone (which has its own guard) is 130 // semantically equivalent but defeats constant-folding across the boundary. 131 return feature('KAIROS') || feature('KAIROS_BRIEF') 132 ? (getKairosActive() || getUserMsgOptIn()) && isBriefEntitled() 133 : false 134} 135 136export const BriefTool = buildTool({ 137 name: BRIEF_TOOL_NAME, 138 aliases: [LEGACY_BRIEF_TOOL_NAME], 139 searchHint: 140 'send a message to the user — your primary visible output channel', 141 maxResultSizeChars: 100_000, 142 userFacingName() { 143 return '' 144 }, 145 get inputSchema(): InputSchema { 146 return inputSchema() 147 }, 148 get outputSchema(): OutputSchema { 149 return outputSchema() 150 }, 151 isEnabled() { 152 return isBriefEnabled() 153 }, 154 isConcurrencySafe() { 155 return true 156 }, 157 isReadOnly() { 158 return true 159 }, 160 toAutoClassifierInput(input) { 161 return input.message 162 }, 163 async validateInput({ attachments }, _context): Promise<ValidationResult> { 164 if (!attachments || attachments.length === 0) { 165 return { result: true } 166 } 167 return validateAttachmentPaths(attachments) 168 }, 169 async description() { 170 return DESCRIPTION 171 }, 172 async prompt() { 173 return BRIEF_TOOL_PROMPT 174 }, 175 mapToolResultToToolResultBlockParam(output, toolUseID) { 176 const n = output.attachments?.length ?? 0 177 const suffix = n === 0 ? '' : ` (${n} ${plural(n, 'attachment')} included)` 178 return { 179 tool_use_id: toolUseID, 180 type: 'tool_result', 181 content: `Message delivered to user.${suffix}`, 182 } 183 }, 184 renderToolUseMessage, 185 renderToolResultMessage, 186 async call({ message, attachments, status }, context) { 187 const sentAt = new Date().toISOString() 188 logEvent('tengu_brief_send', { 189 proactive: status === 'proactive', 190 attachment_count: attachments?.length ?? 0, 191 }) 192 if (!attachments || attachments.length === 0) { 193 return { data: { message, sentAt } } 194 } 195 const appState = context.getAppState() 196 const resolved = await resolveAttachments(attachments, { 197 replBridgeEnabled: appState.replBridgeEnabled, 198 signal: context.abortController.signal, 199 }) 200 return { 201 data: { message, attachments: resolved, sentAt }, 202 } 203 }, 204} satisfies ToolDef<InputSchema, Output>)