Remove internal-only tooling from the external build (#352)
* Remove internal-only tooling without changing external runtime contracts This trims the lowest-risk internal-only surfaces first: deleted internal modules are replaced by build-time no-op stubs, the bundled stuck skill is removed, and the insights S3 upload path now stays local-only. The privacy verifier is expanded and the remaining bundled internal Slack/Artifactory strings are neutralized without broad repo-wide renames. Constraint: Keep the first PR deletion-heavy and avoid mass rewrites of USER_TYPE, tengu, or claude_code identifiers Rejected: One-shot DMCA cleanup branch | too much semantic risk for a first PR Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Treat full-repo typecheck as a baseline issue on this upstream snapshot; do not claim this commit introduced the existing non-Phase-A errors without isolating them first Tested: bun run build Tested: bun run smoke Tested: bun run verify:privacy Not-tested: Full repo typecheck (currently fails on widespread pre-existing upstream errors outside this change set) * Keep minimal source shims so CI can import Phase A cleanup paths The first PR removed internal-only source files entirely, but CI provider and context tests import those modules directly from source rather than through the build-time no-telemetry stubs. This restores tiny no-op source shims so tests and local source imports resolve while preserving the same external runtime behavior. Constraint: GitHub Actions runs source-level tests in addition to bundled build/privacy checks Rejected: Revert the entire deletion pass | unnecessary once the import contract is satisfied by small shims Confidence: high Scope-risk: narrow Reversibility: clean Directive: For later cleanup phases, treat build-time stubs and source-test imports as separate compatibility surfaces Tested: bun run build Tested: bun run smoke Tested: bun run verify:privacy Tested: bun run test:provider Tested: bun run test:provider-recommendation Not-tested: Full repo typecheck (still noisy on this upstream snapshot) --------- Co-authored-by: anandh8x <test@example.com>
This commit is contained in:
@@ -203,6 +203,60 @@ export async function submitTranscriptShare() { return { success: false }; }
|
||||
'services/internalLogging': `
|
||||
export async function logPermissionContextForAnts() {}
|
||||
export const getContainerId = async () => null;
|
||||
`,
|
||||
|
||||
// ─── Deleted Anthropic-internal modules ───────────────────────────────
|
||||
|
||||
'services/api/dumpPrompts': `
|
||||
export function createDumpPromptsFetch() { return undefined; }
|
||||
export function getDumpPromptsPath() { return ''; }
|
||||
export function getLastApiRequests() { return []; }
|
||||
export function clearApiRequestCache() {}
|
||||
export function clearDumpState() {}
|
||||
export function clearAllDumpState() {}
|
||||
export function addApiRequestToCache() {}
|
||||
`,
|
||||
|
||||
'utils/undercover': `
|
||||
export function isUndercover() { return false; }
|
||||
export function getUndercoverInstructions() { return ''; }
|
||||
export function shouldShowUndercoverAutoNotice() { return false; }
|
||||
`,
|
||||
|
||||
'types/generated/events_mono/claude_code/v1/claude_code_internal_event': `
|
||||
export const ClaudeCodeInternalEvent = {
|
||||
fromJSON: value => value,
|
||||
toJSON: value => value,
|
||||
create: value => value ?? {},
|
||||
fromPartial: value => value ?? {},
|
||||
};
|
||||
`,
|
||||
|
||||
'types/generated/events_mono/growthbook/v1/growthbook_experiment_event': `
|
||||
export const GrowthbookExperimentEvent = {
|
||||
fromJSON: value => value,
|
||||
toJSON: value => value,
|
||||
create: value => value ?? {},
|
||||
fromPartial: value => value ?? {},
|
||||
};
|
||||
`,
|
||||
|
||||
'types/generated/events_mono/common/v1/auth': `
|
||||
export const PublicApiAuth = {
|
||||
fromJSON: value => value,
|
||||
toJSON: value => value,
|
||||
create: value => value ?? {},
|
||||
fromPartial: value => value ?? {},
|
||||
};
|
||||
`,
|
||||
|
||||
'types/generated/google/protobuf/timestamp': `
|
||||
export const Timestamp = {
|
||||
fromJSON: value => value,
|
||||
toJSON: value => value,
|
||||
create: value => value ?? {},
|
||||
fromPartial: value => value ?? {},
|
||||
};
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ BANNED=(
|
||||
"/var/run/secrets/kubernetes"
|
||||
"/proc/self/mountinfo"
|
||||
"tengu_internal_record_permission_context"
|
||||
"anthropic-serve"
|
||||
"infra.ant.dev"
|
||||
"claude-code-feedback"
|
||||
"C07VBSHV7EV"
|
||||
)
|
||||
|
||||
echo "Checking $DIST for banned patterns..."
|
||||
|
||||
@@ -9,6 +9,10 @@ const BANNED_PATTERNS = [
|
||||
'/var/run/secrets/kubernetes',
|
||||
'/proc/self/mountinfo',
|
||||
'tengu_internal_record_permission_context',
|
||||
'anthropic-serve',
|
||||
'infra.ant.dev',
|
||||
'claude-code-feedback',
|
||||
'C07VBSHV7EV',
|
||||
] as const
|
||||
|
||||
if (!existsSync(DIST)) {
|
||||
|
||||
@@ -3072,33 +3072,6 @@ const usageReport: Command = {
|
||||
let reportUrl = `file://${htmlPath}`
|
||||
let uploadHint = ''
|
||||
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
// Try to upload to S3
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/[-:]/g, '')
|
||||
.replace('T', '_')
|
||||
.slice(0, 15)
|
||||
const username = process.env.SAFEUSER || process.env.USER || 'unknown'
|
||||
const filename = `${username}_insights_${timestamp}.html`
|
||||
const s3Path = `s3://anthropic-serve/atamkin/cc-user-reports/${filename}`
|
||||
const s3Url = `https://s3-frontend.infra.ant.dev/anthropic-serve/atamkin/cc-user-reports/${filename}`
|
||||
|
||||
reportUrl = s3Url
|
||||
try {
|
||||
execFileSync('ff', ['cp', htmlPath, s3Path], {
|
||||
timeout: 60000,
|
||||
stdio: 'pipe', // Suppress output
|
||||
})
|
||||
} catch {
|
||||
// Upload failed - fall back to local file and show upload command
|
||||
reportUrl = `file://${htmlPath}`
|
||||
uploadHint = `\nAutomatic upload failed. Are you on the boron namespace? Try \`use-bo\` and ensure you've run \`sso\`.
|
||||
To share, run: ff cp ${htmlPath} ${s3Path}
|
||||
Then access at: ${s3Url}`
|
||||
}
|
||||
}
|
||||
|
||||
// Build header with stats
|
||||
const sessionLabel =
|
||||
data.total_sessions_scanned &&
|
||||
|
||||
@@ -242,7 +242,7 @@ function getSimpleDoingTasksSection(): string {
|
||||
: []),
|
||||
...(process.env.USER_TYPE === 'ant'
|
||||
? [
|
||||
`If the user reports a bug, slowness, or unexpected behavior with Claude Code itself (as opposed to asking you to fix their own code), recommend the appropriate slash command: /issue for model-related problems (odd outputs, wrong tool choices, hallucinations, refusals), or /share to upload the full session transcript for product bugs, crashes, slowness, or general issues. Only recommend these when the user is describing a problem with Claude Code. After /share produces a ccshare link, if you have a Slack MCP tool available, offer to post the link to #claude-code-feedback (channel ID C07VBSHV7EV) for the user.`,
|
||||
`If the user reports a bug, slowness, or unexpected behavior with Claude Code itself (as opposed to asking you to fix their own code), recommend the appropriate slash command: /issue for model-related problems (odd outputs, wrong tool choices, hallucinations, refusals), or /share to upload the full session transcript for product bugs, crashes, slowness, or general issues. Only recommend these when the user is describing a problem with Claude Code.`,
|
||||
]
|
||||
: []),
|
||||
`If the user asks for help or wants to give feedback inform them of the following:`,
|
||||
|
||||
@@ -1,226 +1,26 @@
|
||||
import type { ClientOptions } from '@anthropic-ai/sdk'
|
||||
import { createHash } from 'crypto'
|
||||
import { promises as fs } from 'fs'
|
||||
import { dirname, join } from 'path'
|
||||
import { getSessionId } from 'src/bootstrap/state.js'
|
||||
import { getClaudeConfigHomeDir } from '../../utils/envUtils.js'
|
||||
import { jsonParse, jsonStringify } from '../../utils/slowOperations.js'
|
||||
|
||||
function hashString(str: string): string {
|
||||
return createHash('sha256').update(str).digest('hex')
|
||||
}
|
||||
|
||||
// Cache last few API requests for ant users (e.g., for /issue command)
|
||||
const MAX_CACHED_REQUESTS = 5
|
||||
const cachedApiRequests: Array<{ timestamp: string; request: unknown }> = []
|
||||
|
||||
type DumpState = {
|
||||
initialized: boolean
|
||||
messageCountSeen: number
|
||||
lastInitDataHash: string
|
||||
// Cheap proxy for change detection — skips the expensive stringify+hash
|
||||
// when model/tools/system are structurally identical to the last call.
|
||||
lastInitFingerprint: string
|
||||
}
|
||||
|
||||
// Track state per session to avoid duplicating data
|
||||
const dumpState = new Map<string, DumpState>()
|
||||
|
||||
export function getLastApiRequests(): Array<{
|
||||
timestamp: string
|
||||
request: unknown
|
||||
}> {
|
||||
return [...cachedApiRequests]
|
||||
return []
|
||||
}
|
||||
|
||||
export function clearApiRequestCache(): void {
|
||||
cachedApiRequests.length = 0
|
||||
}
|
||||
export function clearApiRequestCache(): void {}
|
||||
|
||||
export function clearDumpState(agentIdOrSessionId: string): void {
|
||||
dumpState.delete(agentIdOrSessionId)
|
||||
}
|
||||
export function clearDumpState(_agentIdOrSessionId?: string): void {}
|
||||
|
||||
export function clearAllDumpState(): void {
|
||||
dumpState.clear()
|
||||
}
|
||||
export function clearAllDumpState(): void {}
|
||||
|
||||
export function addApiRequestToCache(requestData: unknown): void {
|
||||
if (process.env.USER_TYPE !== 'ant') return
|
||||
cachedApiRequests.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
request: requestData,
|
||||
})
|
||||
if (cachedApiRequests.length > MAX_CACHED_REQUESTS) {
|
||||
cachedApiRequests.shift()
|
||||
}
|
||||
}
|
||||
export function addApiRequestToCache(_requestData: unknown): void {}
|
||||
|
||||
export function getDumpPromptsPath(agentIdOrSessionId?: string): string {
|
||||
return join(
|
||||
getClaudeConfigHomeDir(),
|
||||
'dump-prompts',
|
||||
`${agentIdOrSessionId ?? getSessionId()}.jsonl`,
|
||||
)
|
||||
}
|
||||
|
||||
function appendToFile(filePath: string, entries: string[]): void {
|
||||
if (entries.length === 0) return
|
||||
fs.mkdir(dirname(filePath), { recursive: true })
|
||||
.then(() => fs.appendFile(filePath, entries.join('\n') + '\n'))
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function initFingerprint(req: Record<string, unknown>): string {
|
||||
const tools = req.tools as Array<{ name?: string }> | undefined
|
||||
const system = req.system as unknown[] | string | undefined
|
||||
const sysLen =
|
||||
typeof system === 'string'
|
||||
? system.length
|
||||
: Array.isArray(system)
|
||||
? system.reduce(
|
||||
(n: number, b) => n + ((b as { text?: string }).text?.length ?? 0),
|
||||
0,
|
||||
)
|
||||
: 0
|
||||
const toolNames = tools?.map(t => t.name ?? '').join(',') ?? ''
|
||||
return `${req.model}|${toolNames}|${sysLen}`
|
||||
}
|
||||
|
||||
function dumpRequest(
|
||||
body: string,
|
||||
ts: string,
|
||||
state: DumpState,
|
||||
filePath: string,
|
||||
): void {
|
||||
try {
|
||||
const req = jsonParse(body) as Record<string, unknown>
|
||||
addApiRequestToCache(req)
|
||||
|
||||
if (process.env.USER_TYPE !== 'ant') return
|
||||
const entries: string[] = []
|
||||
const messages = (req.messages ?? []) as Array<{ role?: string }>
|
||||
|
||||
// Write init data (system, tools, metadata) on first request,
|
||||
// and a system_update entry whenever it changes.
|
||||
// Cheap fingerprint first: system+tools don't change between turns,
|
||||
// so skip the 300ms stringify when the shape is unchanged.
|
||||
const fingerprint = initFingerprint(req)
|
||||
if (!state.initialized || fingerprint !== state.lastInitFingerprint) {
|
||||
const { messages: _, ...initData } = req
|
||||
const initDataStr = jsonStringify(initData)
|
||||
const initDataHash = hashString(initDataStr)
|
||||
state.lastInitFingerprint = fingerprint
|
||||
if (!state.initialized) {
|
||||
state.initialized = true
|
||||
state.lastInitDataHash = initDataHash
|
||||
// Reuse initDataStr rather than re-serializing initData inside a wrapper.
|
||||
// timestamp from toISOString() contains no chars needing JSON escaping.
|
||||
entries.push(
|
||||
`{"type":"init","timestamp":"${ts}","data":${initDataStr}}`,
|
||||
)
|
||||
} else if (initDataHash !== state.lastInitDataHash) {
|
||||
state.lastInitDataHash = initDataHash
|
||||
entries.push(
|
||||
`{"type":"system_update","timestamp":"${ts}","data":${initDataStr}}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Write only new user messages (assistant messages captured in response)
|
||||
for (const msg of messages.slice(state.messageCountSeen)) {
|
||||
if (msg.role === 'user') {
|
||||
entries.push(
|
||||
jsonStringify({ type: 'message', timestamp: ts, data: msg }),
|
||||
)
|
||||
}
|
||||
}
|
||||
state.messageCountSeen = messages.length
|
||||
|
||||
appendToFile(filePath, entries)
|
||||
} catch {
|
||||
// Ignore parsing errors
|
||||
}
|
||||
export function getDumpPromptsPath(_agentIdOrSessionId?: string): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
export function createDumpPromptsFetch(
|
||||
agentIdOrSessionId: string,
|
||||
_agentIdOrSessionId: string,
|
||||
): ClientOptions['fetch'] {
|
||||
const filePath = getDumpPromptsPath(agentIdOrSessionId)
|
||||
|
||||
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const state = dumpState.get(agentIdOrSessionId) ?? {
|
||||
initialized: false,
|
||||
messageCountSeen: 0,
|
||||
lastInitDataHash: '',
|
||||
lastInitFingerprint: '',
|
||||
}
|
||||
dumpState.set(agentIdOrSessionId, state)
|
||||
|
||||
let timestamp: string | undefined
|
||||
|
||||
if (init?.method === 'POST' && init.body) {
|
||||
timestamp = new Date().toISOString()
|
||||
// Parsing + stringifying the request (system prompt + tool schemas = MBs)
|
||||
// takes hundreds of ms. Defer so it doesn't block the actual API call —
|
||||
// this is debug tooling for /issue, not on the critical path.
|
||||
setImmediate(dumpRequest, init.body as string, timestamp, state, filePath)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins
|
||||
const response = await globalThis.fetch(input, init)
|
||||
|
||||
// Save response async
|
||||
if (timestamp && response.ok && process.env.USER_TYPE === 'ant') {
|
||||
const cloned = response.clone()
|
||||
void (async () => {
|
||||
try {
|
||||
const isStreaming = cloned.headers
|
||||
.get('content-type')
|
||||
?.includes('text/event-stream')
|
||||
|
||||
let data: unknown
|
||||
if (isStreaming && cloned.body) {
|
||||
// Parse SSE stream into chunks
|
||||
const reader = cloned.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
const chunks: unknown[] = []
|
||||
for (const event of buffer.split('\n\n')) {
|
||||
for (const line of event.split('\n')) {
|
||||
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
|
||||
try {
|
||||
chunks.push(jsonParse(line.slice(6)))
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data = { stream: true, chunks }
|
||||
} else {
|
||||
data = await cloned.json()
|
||||
}
|
||||
|
||||
await fs.appendFile(
|
||||
filePath,
|
||||
jsonStringify({ type: 'response', timestamp, data }) + '\n',
|
||||
)
|
||||
} catch {
|
||||
// Best effort
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -1,90 +1,9 @@
|
||||
import { readFile } from 'fs/promises'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import type { ToolPermissionContext } from '../Tool.js'
|
||||
import { jsonStringify } from '../utils/slowOperations.js'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
} from './analytics/index.js'
|
||||
export async function logPermissionContextForAnts(): Promise<void> {}
|
||||
|
||||
/**
|
||||
* Get the current Kubernetes namespace:
|
||||
* Returns null on laptops/local development,
|
||||
* "default" for devboxes in default namespace,
|
||||
* "ts" for devboxes in ts namespace,
|
||||
* ...
|
||||
*/
|
||||
const getKubernetesNamespace = memoize(async (): Promise<string | null> => {
|
||||
if (process.env.USER_TYPE !== 'ant') {
|
||||
return null
|
||||
}
|
||||
const namespacePath =
|
||||
'/var/run/secrets/kubernetes.io/serviceaccount/namespace'
|
||||
const namespaceNotFound = 'namespace not found'
|
||||
try {
|
||||
const content = await readFile(namespacePath, { encoding: 'utf8' })
|
||||
return content.trim()
|
||||
} catch {
|
||||
return namespaceNotFound
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Get the OCI container ID from within a running container
|
||||
*/
|
||||
export const getContainerId = memoize(async (): Promise<string | null> => {
|
||||
if (process.env.USER_TYPE !== 'ant') {
|
||||
return null
|
||||
}
|
||||
const containerIdPath = '/proc/self/mountinfo'
|
||||
const containerIdNotFound = 'container ID not found'
|
||||
const containerIdNotFoundInMountinfo = 'container ID not found in mountinfo'
|
||||
try {
|
||||
const mountinfo = (
|
||||
await readFile(containerIdPath, { encoding: 'utf8' })
|
||||
).trim()
|
||||
|
||||
// Pattern to match both Docker and containerd/CRI-O container IDs
|
||||
// Docker: /docker/containers/[64-char-hex]
|
||||
// Containerd: /sandboxes/[64-char-hex]
|
||||
const containerIdPattern =
|
||||
/(?:\/docker\/containers\/|\/sandboxes\/)([0-9a-f]{64})/
|
||||
|
||||
const lines = mountinfo.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(containerIdPattern)
|
||||
if (match && match[1]) {
|
||||
return match[1]
|
||||
}
|
||||
}
|
||||
|
||||
return containerIdNotFoundInMountinfo
|
||||
} catch {
|
||||
return containerIdNotFound
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Logs an event with the current namespace and tool permission context
|
||||
*/
|
||||
export async function logPermissionContextForAnts(
|
||||
toolPermissionContext: ToolPermissionContext | null,
|
||||
moment: 'summary' | 'initialization',
|
||||
): Promise<void> {
|
||||
if (process.env.USER_TYPE !== 'ant') {
|
||||
return
|
||||
}
|
||||
|
||||
void logEvent('tengu_internal_record_permission_context', {
|
||||
moment:
|
||||
moment as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
namespace:
|
||||
(await getKubernetesNamespace()) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
toolPermissionContext: jsonStringify(
|
||||
toolPermissionContext,
|
||||
) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
containerId:
|
||||
(await getContainerId()) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
})
|
||||
export async function getKubernetesNamespace(): Promise<null> {
|
||||
return null
|
||||
}
|
||||
|
||||
export async function getContainerId(): Promise<null> {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { registerLoremIpsumSkill } from './loremIpsum.js'
|
||||
import { registerRememberSkill } from './remember.js'
|
||||
import { registerSimplifySkill } from './simplify.js'
|
||||
import { registerSkillifySkill } from './skillify.js'
|
||||
import { registerStuckSkill } from './stuck.js'
|
||||
import { registerUpdateConfigSkill } from './updateConfig.js'
|
||||
import { registerVerifySkill } from './verify.js'
|
||||
|
||||
@@ -31,7 +30,6 @@ export function initBundledSkills(): void {
|
||||
registerRememberSkill()
|
||||
registerSimplifySkill()
|
||||
registerBatchSkill()
|
||||
registerStuckSkill()
|
||||
if (feature('KAIROS') || feature('KAIROS_DREAM')) {
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const { registerDreamSkill } = require('./dream.js')
|
||||
|
||||
@@ -1,79 +1 @@
|
||||
import { registerBundledSkill } from '../bundledSkills.js'
|
||||
|
||||
// Prompt text contains `ps` commands as instructions for Claude to run,
|
||||
// not commands this file executes.
|
||||
// eslint-disable-next-line custom-rules/no-direct-ps-commands
|
||||
const STUCK_PROMPT = `# /stuck — diagnose frozen/slow Claude Code sessions
|
||||
|
||||
The user thinks another Claude Code session on this machine is frozen, stuck, or very slow. Investigate and post a report to #claude-code-feedback.
|
||||
|
||||
## What to look for
|
||||
|
||||
Scan for other Claude Code processes (excluding the current one — PID is in \`process.pid\` but for shell commands just exclude the PID you see running this prompt). Process names are typically \`claude\` (installed) or \`cli\` (native dev build).
|
||||
|
||||
Signs of a stuck session:
|
||||
- **High CPU (≥90%) sustained** — likely an infinite loop. Sample twice, 1-2s apart, to confirm it's not a transient spike.
|
||||
- **Process state \`D\` (uninterruptible sleep)** — often an I/O hang. The \`state\` column in \`ps\` output; first character matters (ignore modifiers like \`+\`, \`s\`, \`<\`).
|
||||
- **Process state \`T\` (stopped)** — user probably hit Ctrl+Z by accident.
|
||||
- **Process state \`Z\` (zombie)** — parent isn't reaping.
|
||||
- **Very high RSS (≥4GB)** — possible memory leak making the session sluggish.
|
||||
- **Stuck child process** — a hung \`git\`, \`node\`, or shell subprocess can freeze the parent. Check \`pgrep -lP <pid>\` for each session.
|
||||
|
||||
## Investigation steps
|
||||
|
||||
1. **List all Claude Code processes** (macOS/Linux):
|
||||
\`\`\`
|
||||
ps -axo pid=,pcpu=,rss=,etime=,state=,comm=,command= | grep -E '(claude|cli)' | grep -v grep
|
||||
\`\`\`
|
||||
Filter to rows where \`comm\` is \`claude\` or (\`cli\` AND the command path contains "claude").
|
||||
|
||||
2. **For anything suspicious**, gather more context:
|
||||
- Child processes: \`pgrep -lP <pid>\`
|
||||
- If high CPU: sample again after 1-2s to confirm it's sustained
|
||||
- If a child looks hung (e.g., a git command), note its full command line with \`ps -p <child_pid> -o command=\`
|
||||
- Check the session's debug log if you can infer the session ID: \`~/.claude/debug/<session-id>.txt\` (the last few hundred lines often show what it was doing before hanging)
|
||||
|
||||
3. **Consider a stack dump** for a truly frozen process (advanced, optional):
|
||||
- macOS: \`sample <pid> 3\` gives a 3-second native stack sample
|
||||
- This is big — only grab it if the process is clearly hung and you want to know *why*
|
||||
|
||||
## Report
|
||||
|
||||
**Only post to Slack if you actually found something stuck.** If every session looks healthy, tell the user that directly — do not post an all-clear to the channel.
|
||||
|
||||
If you did find a stuck/slow session, post to **#claude-code-feedback** (channel ID: \`C07VBSHV7EV\`) using the Slack MCP tool. Use ToolSearch to find \`slack_send_message\` if it's not already loaded.
|
||||
|
||||
**Use a two-message structure** to keep the channel scannable:
|
||||
|
||||
1. **Top-level message** — one short line: hostname, Claude Code version, and a terse symptom (e.g. "session PID 12345 pegged at 100% CPU for 10min" or "git subprocess hung in D state"). No code blocks, no details.
|
||||
2. **Thread reply** — the full diagnostic dump. Pass the top-level message's \`ts\` as \`thread_ts\`. Include:
|
||||
- PID, CPU%, RSS, state, uptime, command line, child processes
|
||||
- Your diagnosis of what's likely wrong
|
||||
- Relevant debug log tail or \`sample\` output if you captured it
|
||||
|
||||
If Slack MCP isn't available, format the report as a message the user can copy-paste into #claude-code-feedback (and let them know to thread the details themselves).
|
||||
|
||||
## Notes
|
||||
- Don't kill or signal any processes — this is diagnostic only.
|
||||
- If the user gave an argument (e.g., a specific PID or symptom), focus there first.
|
||||
`
|
||||
|
||||
export function registerStuckSkill(): void {
|
||||
if (process.env.USER_TYPE !== 'ant') {
|
||||
return
|
||||
}
|
||||
|
||||
registerBundledSkill({
|
||||
name: 'stuck',
|
||||
description:
|
||||
'[ANT-ONLY] Investigate frozen/stuck/slow Claude Code sessions on this machine and post a diagnostic report to #claude-code-feedback.',
|
||||
userInvocable: true,
|
||||
async getPromptForCommand(args) {
|
||||
let prompt = STUCK_PROMPT
|
||||
if (args) {
|
||||
prompt += `\n## User-provided context\n\n${args}\n`
|
||||
}
|
||||
return [{ type: 'text', text: prompt }]
|
||||
},
|
||||
})
|
||||
}
|
||||
export function registerStuckSkill(): void {}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.6.1
|
||||
// protoc unknown
|
||||
// source: events_mono/claude_code/v1/claude_code_internal_event.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { Timestamp } from '../../../google/protobuf/timestamp.js'
|
||||
import { PublicApiAuth } from '../../common/v1/auth.js'
|
||||
|
||||
/** GitHubActionsMetadata contains GitHub Actions-specific environment information */
|
||||
import type { PublicApiAuth } from '../../common/v1/auth.js'
|
||||
|
||||
export interface GitHubActionsMetadata {
|
||||
actor_id?: string | undefined
|
||||
repository_id?: string | undefined
|
||||
repository_owner_id?: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* EnvironmentMetadata contains environment and runtime information
|
||||
* See claude-cli-internal/src/services/statsig.ts for the source of these fields
|
||||
*/
|
||||
export interface EnvironmentMetadata {
|
||||
platform?: string | undefined
|
||||
node_version?: string | undefined
|
||||
@@ -32,14 +21,11 @@ export interface EnvironmentMetadata {
|
||||
is_claude_code_action?: boolean | undefined
|
||||
is_claude_ai_auth?: boolean | undefined
|
||||
version?: string | undefined
|
||||
/** GitHub Actions specific fields (only present when is_github_action is true) */
|
||||
github_event_name?: string | undefined
|
||||
github_actions_runner_environment?: string | undefined
|
||||
github_actions_runner_os?: string | undefined
|
||||
github_action_ref?: string | undefined
|
||||
/** WSL specific field */
|
||||
wsl_version?: string | undefined
|
||||
/** GitHub metadata (only present when is_github_action is true) */
|
||||
github_actions_metadata?: GitHubActionsMetadata | undefined
|
||||
arch?: string | undefined
|
||||
is_claude_code_remote?: boolean | undefined
|
||||
@@ -60,11 +46,6 @@ export interface EnvironmentMetadata {
|
||||
platform_raw?: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* SlackContext contains context fields present on every Claude-in-Slack (CIS) event.
|
||||
* Event-specific fields (errorType, durationMs, httpStatus, etc.) go in
|
||||
* ClaudeCodeInternalEvent.additional_metadata as JSON.
|
||||
*/
|
||||
export interface SlackContext {
|
||||
slack_team_id?: string | undefined
|
||||
is_enterprise_install?: boolean | undefined
|
||||
@@ -72,56 +53,31 @@ export interface SlackContext {
|
||||
creation_method?: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* ClaudeCodeInternalEvent represents events logged from Claude Code via Statsig
|
||||
* This schema matches the structure in claude-cli-internal/src/services/statsig.ts
|
||||
* Source table: proj-product-data-nhme.raw_statsig_internal_tools.events
|
||||
*/
|
||||
export interface ClaudeCodeInternalEvent {
|
||||
/** Event name (e.g., "tengu_binary_feedback", "tengu_api_success") */
|
||||
event_name?: string | undefined
|
||||
/** Event timestamp */
|
||||
client_timestamp?: Date | undefined
|
||||
model?: string | undefined
|
||||
session_id?: string | undefined
|
||||
user_type?: string | undefined
|
||||
betas?: string | undefined
|
||||
/** Environment and runtime information */
|
||||
env?: EnvironmentMetadata | undefined
|
||||
entrypoint?: string | undefined
|
||||
agent_sdk_version?: string | undefined
|
||||
is_interactive?: boolean | undefined
|
||||
client_type?: string | undefined
|
||||
/**
|
||||
* Process metrics as JSON string (ant-only)
|
||||
* Contains: uptime, rss, heapTotal, heapUsed, external, arrayBuffers,
|
||||
* constrainedMemory, cpuUsage
|
||||
*/
|
||||
process?: string | undefined
|
||||
/**
|
||||
* Additional metadata passed to logEvent (event-specific)
|
||||
* This includes fields like msg_id_A, msg_id_B, gitBranch, gitHead, etc.
|
||||
* that vary per event type
|
||||
*/
|
||||
additional_metadata?: string | undefined
|
||||
/** Authentication context automatically injected by the API */
|
||||
auth?: PublicApiAuth | undefined
|
||||
/** Server timestamp automatically injected by the API */
|
||||
server_timestamp?: Date | undefined
|
||||
/** Unique identifier for this event (automatically generated by API endpoint) */
|
||||
event_id?: string | undefined
|
||||
/** Device identifier for the client */
|
||||
device_id?: string | undefined
|
||||
/** SWE-bench fields */
|
||||
swe_bench_run_id?: string | undefined
|
||||
swe_bench_instance_id?: string | undefined
|
||||
swe_bench_task_id?: string | undefined
|
||||
email?: string | undefined
|
||||
/** Swarm/team agent identification for analytics attribution */
|
||||
agent_id?: string | undefined
|
||||
parent_session_id?: string | undefined
|
||||
agent_type?: string | undefined
|
||||
/** Claude-in-Slack context (only present for cis_* events) */
|
||||
slack?: SlackContext | undefined
|
||||
team_name?: string | undefined
|
||||
skill_name?: string | undefined
|
||||
@@ -129,737 +85,24 @@ export interface ClaudeCodeInternalEvent {
|
||||
marketplace_name?: string | undefined
|
||||
}
|
||||
|
||||
function createBaseGitHubActionsMetadata(): GitHubActionsMetadata {
|
||||
return { actor_id: '', repository_id: '', repository_owner_id: '' }
|
||||
}
|
||||
|
||||
export const GitHubActionsMetadata: MessageFns<GitHubActionsMetadata> = {
|
||||
fromJSON(object: any): GitHubActionsMetadata {
|
||||
return {
|
||||
actor_id: isSet(object.actor_id)
|
||||
? globalThis.String(object.actor_id)
|
||||
: '',
|
||||
repository_id: isSet(object.repository_id)
|
||||
? globalThis.String(object.repository_id)
|
||||
: '',
|
||||
repository_owner_id: isSet(object.repository_owner_id)
|
||||
? globalThis.String(object.repository_owner_id)
|
||||
: '',
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: GitHubActionsMetadata): unknown {
|
||||
const obj: any = {}
|
||||
if (message.actor_id !== undefined) {
|
||||
obj.actor_id = message.actor_id
|
||||
}
|
||||
if (message.repository_id !== undefined) {
|
||||
obj.repository_id = message.repository_id
|
||||
}
|
||||
if (message.repository_owner_id !== undefined) {
|
||||
obj.repository_owner_id = message.repository_owner_id
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<GitHubActionsMetadata>, I>>(
|
||||
base?: I,
|
||||
): GitHubActionsMetadata {
|
||||
return GitHubActionsMetadata.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<GitHubActionsMetadata>, I>>(
|
||||
object: I,
|
||||
): GitHubActionsMetadata {
|
||||
const message = createBaseGitHubActionsMetadata()
|
||||
message.actor_id = object.actor_id ?? ''
|
||||
message.repository_id = object.repository_id ?? ''
|
||||
message.repository_owner_id = object.repository_owner_id ?? ''
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseEnvironmentMetadata(): EnvironmentMetadata {
|
||||
return {
|
||||
platform: '',
|
||||
node_version: '',
|
||||
terminal: '',
|
||||
package_managers: '',
|
||||
runtimes: '',
|
||||
is_running_with_bun: false,
|
||||
is_ci: false,
|
||||
is_claubbit: false,
|
||||
is_github_action: false,
|
||||
is_claude_code_action: false,
|
||||
is_claude_ai_auth: false,
|
||||
version: '',
|
||||
github_event_name: '',
|
||||
github_actions_runner_environment: '',
|
||||
github_actions_runner_os: '',
|
||||
github_action_ref: '',
|
||||
wsl_version: '',
|
||||
github_actions_metadata: undefined,
|
||||
arch: '',
|
||||
is_claude_code_remote: false,
|
||||
remote_environment_type: '',
|
||||
claude_code_container_id: '',
|
||||
claude_code_remote_session_id: '',
|
||||
tags: [],
|
||||
deployment_environment: '',
|
||||
is_conductor: false,
|
||||
version_base: '',
|
||||
coworker_type: '',
|
||||
build_time: '',
|
||||
is_local_agent_mode: false,
|
||||
linux_distro_id: '',
|
||||
linux_distro_version: '',
|
||||
linux_kernel: '',
|
||||
vcs: '',
|
||||
platform_raw: '',
|
||||
}
|
||||
}
|
||||
|
||||
export const EnvironmentMetadata: MessageFns<EnvironmentMetadata> = {
|
||||
fromJSON(object: any): EnvironmentMetadata {
|
||||
return {
|
||||
platform: isSet(object.platform)
|
||||
? globalThis.String(object.platform)
|
||||
: '',
|
||||
node_version: isSet(object.node_version)
|
||||
? globalThis.String(object.node_version)
|
||||
: '',
|
||||
terminal: isSet(object.terminal)
|
||||
? globalThis.String(object.terminal)
|
||||
: '',
|
||||
package_managers: isSet(object.package_managers)
|
||||
? globalThis.String(object.package_managers)
|
||||
: '',
|
||||
runtimes: isSet(object.runtimes)
|
||||
? globalThis.String(object.runtimes)
|
||||
: '',
|
||||
is_running_with_bun: isSet(object.is_running_with_bun)
|
||||
? globalThis.Boolean(object.is_running_with_bun)
|
||||
: false,
|
||||
is_ci: isSet(object.is_ci) ? globalThis.Boolean(object.is_ci) : false,
|
||||
is_claubbit: isSet(object.is_claubbit)
|
||||
? globalThis.Boolean(object.is_claubbit)
|
||||
: false,
|
||||
is_github_action: isSet(object.is_github_action)
|
||||
? globalThis.Boolean(object.is_github_action)
|
||||
: false,
|
||||
is_claude_code_action: isSet(object.is_claude_code_action)
|
||||
? globalThis.Boolean(object.is_claude_code_action)
|
||||
: false,
|
||||
is_claude_ai_auth: isSet(object.is_claude_ai_auth)
|
||||
? globalThis.Boolean(object.is_claude_ai_auth)
|
||||
: false,
|
||||
version: isSet(object.version) ? globalThis.String(object.version) : '',
|
||||
github_event_name: isSet(object.github_event_name)
|
||||
? globalThis.String(object.github_event_name)
|
||||
: '',
|
||||
github_actions_runner_environment: isSet(
|
||||
object.github_actions_runner_environment,
|
||||
)
|
||||
? globalThis.String(object.github_actions_runner_environment)
|
||||
: '',
|
||||
github_actions_runner_os: isSet(object.github_actions_runner_os)
|
||||
? globalThis.String(object.github_actions_runner_os)
|
||||
: '',
|
||||
github_action_ref: isSet(object.github_action_ref)
|
||||
? globalThis.String(object.github_action_ref)
|
||||
: '',
|
||||
wsl_version: isSet(object.wsl_version)
|
||||
? globalThis.String(object.wsl_version)
|
||||
: '',
|
||||
github_actions_metadata: isSet(object.github_actions_metadata)
|
||||
? GitHubActionsMetadata.fromJSON(object.github_actions_metadata)
|
||||
: undefined,
|
||||
arch: isSet(object.arch) ? globalThis.String(object.arch) : '',
|
||||
is_claude_code_remote: isSet(object.is_claude_code_remote)
|
||||
? globalThis.Boolean(object.is_claude_code_remote)
|
||||
: false,
|
||||
remote_environment_type: isSet(object.remote_environment_type)
|
||||
? globalThis.String(object.remote_environment_type)
|
||||
: '',
|
||||
claude_code_container_id: isSet(object.claude_code_container_id)
|
||||
? globalThis.String(object.claude_code_container_id)
|
||||
: '',
|
||||
claude_code_remote_session_id: isSet(object.claude_code_remote_session_id)
|
||||
? globalThis.String(object.claude_code_remote_session_id)
|
||||
: '',
|
||||
tags: globalThis.Array.isArray(object?.tags)
|
||||
? object.tags.map((e: any) => globalThis.String(e))
|
||||
: [],
|
||||
deployment_environment: isSet(object.deployment_environment)
|
||||
? globalThis.String(object.deployment_environment)
|
||||
: '',
|
||||
is_conductor: isSet(object.is_conductor)
|
||||
? globalThis.Boolean(object.is_conductor)
|
||||
: false,
|
||||
version_base: isSet(object.version_base)
|
||||
? globalThis.String(object.version_base)
|
||||
: '',
|
||||
coworker_type: isSet(object.coworker_type)
|
||||
? globalThis.String(object.coworker_type)
|
||||
: '',
|
||||
build_time: isSet(object.build_time)
|
||||
? globalThis.String(object.build_time)
|
||||
: '',
|
||||
is_local_agent_mode: isSet(object.is_local_agent_mode)
|
||||
? globalThis.Boolean(object.is_local_agent_mode)
|
||||
: false,
|
||||
linux_distro_id: isSet(object.linux_distro_id)
|
||||
? globalThis.String(object.linux_distro_id)
|
||||
: '',
|
||||
linux_distro_version: isSet(object.linux_distro_version)
|
||||
? globalThis.String(object.linux_distro_version)
|
||||
: '',
|
||||
linux_kernel: isSet(object.linux_kernel)
|
||||
? globalThis.String(object.linux_kernel)
|
||||
: '',
|
||||
vcs: isSet(object.vcs) ? globalThis.String(object.vcs) : '',
|
||||
platform_raw: isSet(object.platform_raw)
|
||||
? globalThis.String(object.platform_raw)
|
||||
: '',
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: EnvironmentMetadata): unknown {
|
||||
const obj: any = {}
|
||||
if (message.platform !== undefined) {
|
||||
obj.platform = message.platform
|
||||
}
|
||||
if (message.node_version !== undefined) {
|
||||
obj.node_version = message.node_version
|
||||
}
|
||||
if (message.terminal !== undefined) {
|
||||
obj.terminal = message.terminal
|
||||
}
|
||||
if (message.package_managers !== undefined) {
|
||||
obj.package_managers = message.package_managers
|
||||
}
|
||||
if (message.runtimes !== undefined) {
|
||||
obj.runtimes = message.runtimes
|
||||
}
|
||||
if (message.is_running_with_bun !== undefined) {
|
||||
obj.is_running_with_bun = message.is_running_with_bun
|
||||
}
|
||||
if (message.is_ci !== undefined) {
|
||||
obj.is_ci = message.is_ci
|
||||
}
|
||||
if (message.is_claubbit !== undefined) {
|
||||
obj.is_claubbit = message.is_claubbit
|
||||
}
|
||||
if (message.is_github_action !== undefined) {
|
||||
obj.is_github_action = message.is_github_action
|
||||
}
|
||||
if (message.is_claude_code_action !== undefined) {
|
||||
obj.is_claude_code_action = message.is_claude_code_action
|
||||
}
|
||||
if (message.is_claude_ai_auth !== undefined) {
|
||||
obj.is_claude_ai_auth = message.is_claude_ai_auth
|
||||
}
|
||||
if (message.version !== undefined) {
|
||||
obj.version = message.version
|
||||
}
|
||||
if (message.github_event_name !== undefined) {
|
||||
obj.github_event_name = message.github_event_name
|
||||
}
|
||||
if (message.github_actions_runner_environment !== undefined) {
|
||||
obj.github_actions_runner_environment =
|
||||
message.github_actions_runner_environment
|
||||
}
|
||||
if (message.github_actions_runner_os !== undefined) {
|
||||
obj.github_actions_runner_os = message.github_actions_runner_os
|
||||
}
|
||||
if (message.github_action_ref !== undefined) {
|
||||
obj.github_action_ref = message.github_action_ref
|
||||
}
|
||||
if (message.wsl_version !== undefined) {
|
||||
obj.wsl_version = message.wsl_version
|
||||
}
|
||||
if (message.github_actions_metadata !== undefined) {
|
||||
obj.github_actions_metadata = GitHubActionsMetadata.toJSON(
|
||||
message.github_actions_metadata,
|
||||
)
|
||||
}
|
||||
if (message.arch !== undefined) {
|
||||
obj.arch = message.arch
|
||||
}
|
||||
if (message.is_claude_code_remote !== undefined) {
|
||||
obj.is_claude_code_remote = message.is_claude_code_remote
|
||||
}
|
||||
if (message.remote_environment_type !== undefined) {
|
||||
obj.remote_environment_type = message.remote_environment_type
|
||||
}
|
||||
if (message.claude_code_container_id !== undefined) {
|
||||
obj.claude_code_container_id = message.claude_code_container_id
|
||||
}
|
||||
if (message.claude_code_remote_session_id !== undefined) {
|
||||
obj.claude_code_remote_session_id = message.claude_code_remote_session_id
|
||||
}
|
||||
if (message.tags?.length) {
|
||||
obj.tags = message.tags
|
||||
}
|
||||
if (message.deployment_environment !== undefined) {
|
||||
obj.deployment_environment = message.deployment_environment
|
||||
}
|
||||
if (message.is_conductor !== undefined) {
|
||||
obj.is_conductor = message.is_conductor
|
||||
}
|
||||
if (message.version_base !== undefined) {
|
||||
obj.version_base = message.version_base
|
||||
}
|
||||
if (message.coworker_type !== undefined) {
|
||||
obj.coworker_type = message.coworker_type
|
||||
}
|
||||
if (message.build_time !== undefined) {
|
||||
obj.build_time = message.build_time
|
||||
}
|
||||
if (message.is_local_agent_mode !== undefined) {
|
||||
obj.is_local_agent_mode = message.is_local_agent_mode
|
||||
}
|
||||
if (message.linux_distro_id !== undefined) {
|
||||
obj.linux_distro_id = message.linux_distro_id
|
||||
}
|
||||
if (message.linux_distro_version !== undefined) {
|
||||
obj.linux_distro_version = message.linux_distro_version
|
||||
}
|
||||
if (message.linux_kernel !== undefined) {
|
||||
obj.linux_kernel = message.linux_kernel
|
||||
}
|
||||
if (message.vcs !== undefined) {
|
||||
obj.vcs = message.vcs
|
||||
}
|
||||
if (message.platform_raw !== undefined) {
|
||||
obj.platform_raw = message.platform_raw
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<EnvironmentMetadata>, I>>(
|
||||
base?: I,
|
||||
): EnvironmentMetadata {
|
||||
return EnvironmentMetadata.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<EnvironmentMetadata>, I>>(
|
||||
object: I,
|
||||
): EnvironmentMetadata {
|
||||
const message = createBaseEnvironmentMetadata()
|
||||
message.platform = object.platform ?? ''
|
||||
message.node_version = object.node_version ?? ''
|
||||
message.terminal = object.terminal ?? ''
|
||||
message.package_managers = object.package_managers ?? ''
|
||||
message.runtimes = object.runtimes ?? ''
|
||||
message.is_running_with_bun = object.is_running_with_bun ?? false
|
||||
message.is_ci = object.is_ci ?? false
|
||||
message.is_claubbit = object.is_claubbit ?? false
|
||||
message.is_github_action = object.is_github_action ?? false
|
||||
message.is_claude_code_action = object.is_claude_code_action ?? false
|
||||
message.is_claude_ai_auth = object.is_claude_ai_auth ?? false
|
||||
message.version = object.version ?? ''
|
||||
message.github_event_name = object.github_event_name ?? ''
|
||||
message.github_actions_runner_environment =
|
||||
object.github_actions_runner_environment ?? ''
|
||||
message.github_actions_runner_os = object.github_actions_runner_os ?? ''
|
||||
message.github_action_ref = object.github_action_ref ?? ''
|
||||
message.wsl_version = object.wsl_version ?? ''
|
||||
message.github_actions_metadata =
|
||||
object.github_actions_metadata !== undefined &&
|
||||
object.github_actions_metadata !== null
|
||||
? GitHubActionsMetadata.fromPartial(object.github_actions_metadata)
|
||||
: undefined
|
||||
message.arch = object.arch ?? ''
|
||||
message.is_claude_code_remote = object.is_claude_code_remote ?? false
|
||||
message.remote_environment_type = object.remote_environment_type ?? ''
|
||||
message.claude_code_container_id = object.claude_code_container_id ?? ''
|
||||
message.claude_code_remote_session_id =
|
||||
object.claude_code_remote_session_id ?? ''
|
||||
message.tags = object.tags?.map(e => e) || []
|
||||
message.deployment_environment = object.deployment_environment ?? ''
|
||||
message.is_conductor = object.is_conductor ?? false
|
||||
message.version_base = object.version_base ?? ''
|
||||
message.coworker_type = object.coworker_type ?? ''
|
||||
message.build_time = object.build_time ?? ''
|
||||
message.is_local_agent_mode = object.is_local_agent_mode ?? false
|
||||
message.linux_distro_id = object.linux_distro_id ?? ''
|
||||
message.linux_distro_version = object.linux_distro_version ?? ''
|
||||
message.linux_kernel = object.linux_kernel ?? ''
|
||||
message.vcs = object.vcs ?? ''
|
||||
message.platform_raw = object.platform_raw ?? ''
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseSlackContext(): SlackContext {
|
||||
return {
|
||||
slack_team_id: '',
|
||||
is_enterprise_install: false,
|
||||
trigger: '',
|
||||
creation_method: '',
|
||||
}
|
||||
}
|
||||
|
||||
export const SlackContext: MessageFns<SlackContext> = {
|
||||
fromJSON(object: any): SlackContext {
|
||||
return {
|
||||
slack_team_id: isSet(object.slack_team_id)
|
||||
? globalThis.String(object.slack_team_id)
|
||||
: '',
|
||||
is_enterprise_install: isSet(object.is_enterprise_install)
|
||||
? globalThis.Boolean(object.is_enterprise_install)
|
||||
: false,
|
||||
trigger: isSet(object.trigger) ? globalThis.String(object.trigger) : '',
|
||||
creation_method: isSet(object.creation_method)
|
||||
? globalThis.String(object.creation_method)
|
||||
: '',
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: SlackContext): unknown {
|
||||
const obj: any = {}
|
||||
if (message.slack_team_id !== undefined) {
|
||||
obj.slack_team_id = message.slack_team_id
|
||||
}
|
||||
if (message.is_enterprise_install !== undefined) {
|
||||
obj.is_enterprise_install = message.is_enterprise_install
|
||||
}
|
||||
if (message.trigger !== undefined) {
|
||||
obj.trigger = message.trigger
|
||||
}
|
||||
if (message.creation_method !== undefined) {
|
||||
obj.creation_method = message.creation_method
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<SlackContext>, I>>(
|
||||
base?: I,
|
||||
): SlackContext {
|
||||
return SlackContext.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<SlackContext>, I>>(
|
||||
object: I,
|
||||
): SlackContext {
|
||||
const message = createBaseSlackContext()
|
||||
message.slack_team_id = object.slack_team_id ?? ''
|
||||
message.is_enterprise_install = object.is_enterprise_install ?? false
|
||||
message.trigger = object.trigger ?? ''
|
||||
message.creation_method = object.creation_method ?? ''
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
function createBaseClaudeCodeInternalEvent(): ClaudeCodeInternalEvent {
|
||||
return {
|
||||
event_name: '',
|
||||
client_timestamp: undefined,
|
||||
model: '',
|
||||
session_id: '',
|
||||
user_type: '',
|
||||
betas: '',
|
||||
env: undefined,
|
||||
entrypoint: '',
|
||||
agent_sdk_version: '',
|
||||
is_interactive: false,
|
||||
client_type: '',
|
||||
process: '',
|
||||
additional_metadata: '',
|
||||
auth: undefined,
|
||||
server_timestamp: undefined,
|
||||
event_id: '',
|
||||
device_id: '',
|
||||
swe_bench_run_id: '',
|
||||
swe_bench_instance_id: '',
|
||||
swe_bench_task_id: '',
|
||||
email: '',
|
||||
agent_id: '',
|
||||
parent_session_id: '',
|
||||
agent_type: '',
|
||||
slack: undefined,
|
||||
team_name: '',
|
||||
skill_name: '',
|
||||
plugin_name: '',
|
||||
marketplace_name: '',
|
||||
}
|
||||
}
|
||||
|
||||
export const ClaudeCodeInternalEvent: MessageFns<ClaudeCodeInternalEvent> = {
|
||||
export const ClaudeCodeInternalEvent = {
|
||||
fromJSON(object: any): ClaudeCodeInternalEvent {
|
||||
return {
|
||||
event_name: isSet(object.event_name)
|
||||
? globalThis.String(object.event_name)
|
||||
: '',
|
||||
client_timestamp: isSet(object.client_timestamp)
|
||||
? fromJsonTimestamp(object.client_timestamp)
|
||||
: undefined,
|
||||
model: isSet(object.model) ? globalThis.String(object.model) : '',
|
||||
session_id: isSet(object.session_id)
|
||||
? globalThis.String(object.session_id)
|
||||
: '',
|
||||
user_type: isSet(object.user_type)
|
||||
? globalThis.String(object.user_type)
|
||||
: '',
|
||||
betas: isSet(object.betas) ? globalThis.String(object.betas) : '',
|
||||
env: isSet(object.env)
|
||||
? EnvironmentMetadata.fromJSON(object.env)
|
||||
: undefined,
|
||||
entrypoint: isSet(object.entrypoint)
|
||||
? globalThis.String(object.entrypoint)
|
||||
: '',
|
||||
agent_sdk_version: isSet(object.agent_sdk_version)
|
||||
? globalThis.String(object.agent_sdk_version)
|
||||
: '',
|
||||
is_interactive: isSet(object.is_interactive)
|
||||
? globalThis.Boolean(object.is_interactive)
|
||||
: false,
|
||||
client_type: isSet(object.client_type)
|
||||
? globalThis.String(object.client_type)
|
||||
: '',
|
||||
process: isSet(object.process) ? globalThis.String(object.process) : '',
|
||||
additional_metadata: isSet(object.additional_metadata)
|
||||
? globalThis.String(object.additional_metadata)
|
||||
: '',
|
||||
auth: isSet(object.auth)
|
||||
? PublicApiAuth.fromJSON(object.auth)
|
||||
: undefined,
|
||||
server_timestamp: isSet(object.server_timestamp)
|
||||
? fromJsonTimestamp(object.server_timestamp)
|
||||
: undefined,
|
||||
event_id: isSet(object.event_id)
|
||||
? globalThis.String(object.event_id)
|
||||
: '',
|
||||
device_id: isSet(object.device_id)
|
||||
? globalThis.String(object.device_id)
|
||||
: '',
|
||||
swe_bench_run_id: isSet(object.swe_bench_run_id)
|
||||
? globalThis.String(object.swe_bench_run_id)
|
||||
: '',
|
||||
swe_bench_instance_id: isSet(object.swe_bench_instance_id)
|
||||
? globalThis.String(object.swe_bench_instance_id)
|
||||
: '',
|
||||
swe_bench_task_id: isSet(object.swe_bench_task_id)
|
||||
? globalThis.String(object.swe_bench_task_id)
|
||||
: '',
|
||||
email: isSet(object.email) ? globalThis.String(object.email) : '',
|
||||
agent_id: isSet(object.agent_id)
|
||||
? globalThis.String(object.agent_id)
|
||||
: '',
|
||||
parent_session_id: isSet(object.parent_session_id)
|
||||
? globalThis.String(object.parent_session_id)
|
||||
: '',
|
||||
agent_type: isSet(object.agent_type)
|
||||
? globalThis.String(object.agent_type)
|
||||
: '',
|
||||
slack: isSet(object.slack)
|
||||
? SlackContext.fromJSON(object.slack)
|
||||
: undefined,
|
||||
team_name: isSet(object.team_name)
|
||||
? globalThis.String(object.team_name)
|
||||
: '',
|
||||
skill_name: isSet(object.skill_name)
|
||||
? globalThis.String(object.skill_name)
|
||||
: '',
|
||||
plugin_name: isSet(object.plugin_name)
|
||||
? globalThis.String(object.plugin_name)
|
||||
: '',
|
||||
marketplace_name: isSet(object.marketplace_name)
|
||||
? globalThis.String(object.marketplace_name)
|
||||
: '',
|
||||
}
|
||||
return object ?? {}
|
||||
},
|
||||
|
||||
toJSON(message: ClaudeCodeInternalEvent): unknown {
|
||||
const obj: any = {}
|
||||
if (message.event_name !== undefined) {
|
||||
obj.event_name = message.event_name
|
||||
}
|
||||
if (message.client_timestamp !== undefined) {
|
||||
obj.client_timestamp = message.client_timestamp.toISOString()
|
||||
}
|
||||
if (message.model !== undefined) {
|
||||
obj.model = message.model
|
||||
}
|
||||
if (message.session_id !== undefined) {
|
||||
obj.session_id = message.session_id
|
||||
}
|
||||
if (message.user_type !== undefined) {
|
||||
obj.user_type = message.user_type
|
||||
}
|
||||
if (message.betas !== undefined) {
|
||||
obj.betas = message.betas
|
||||
}
|
||||
if (message.env !== undefined) {
|
||||
obj.env = EnvironmentMetadata.toJSON(message.env)
|
||||
}
|
||||
if (message.entrypoint !== undefined) {
|
||||
obj.entrypoint = message.entrypoint
|
||||
}
|
||||
if (message.agent_sdk_version !== undefined) {
|
||||
obj.agent_sdk_version = message.agent_sdk_version
|
||||
}
|
||||
if (message.is_interactive !== undefined) {
|
||||
obj.is_interactive = message.is_interactive
|
||||
}
|
||||
if (message.client_type !== undefined) {
|
||||
obj.client_type = message.client_type
|
||||
}
|
||||
if (message.process !== undefined) {
|
||||
obj.process = message.process
|
||||
}
|
||||
if (message.additional_metadata !== undefined) {
|
||||
obj.additional_metadata = message.additional_metadata
|
||||
}
|
||||
if (message.auth !== undefined) {
|
||||
obj.auth = PublicApiAuth.toJSON(message.auth)
|
||||
}
|
||||
if (message.server_timestamp !== undefined) {
|
||||
obj.server_timestamp = message.server_timestamp.toISOString()
|
||||
}
|
||||
if (message.event_id !== undefined) {
|
||||
obj.event_id = message.event_id
|
||||
}
|
||||
if (message.device_id !== undefined) {
|
||||
obj.device_id = message.device_id
|
||||
}
|
||||
if (message.swe_bench_run_id !== undefined) {
|
||||
obj.swe_bench_run_id = message.swe_bench_run_id
|
||||
}
|
||||
if (message.swe_bench_instance_id !== undefined) {
|
||||
obj.swe_bench_instance_id = message.swe_bench_instance_id
|
||||
}
|
||||
if (message.swe_bench_task_id !== undefined) {
|
||||
obj.swe_bench_task_id = message.swe_bench_task_id
|
||||
}
|
||||
if (message.email !== undefined) {
|
||||
obj.email = message.email
|
||||
}
|
||||
if (message.agent_id !== undefined) {
|
||||
obj.agent_id = message.agent_id
|
||||
}
|
||||
if (message.parent_session_id !== undefined) {
|
||||
obj.parent_session_id = message.parent_session_id
|
||||
}
|
||||
if (message.agent_type !== undefined) {
|
||||
obj.agent_type = message.agent_type
|
||||
}
|
||||
if (message.slack !== undefined) {
|
||||
obj.slack = SlackContext.toJSON(message.slack)
|
||||
}
|
||||
if (message.team_name !== undefined) {
|
||||
obj.team_name = message.team_name
|
||||
}
|
||||
if (message.skill_name !== undefined) {
|
||||
obj.skill_name = message.skill_name
|
||||
}
|
||||
if (message.plugin_name !== undefined) {
|
||||
obj.plugin_name = message.plugin_name
|
||||
}
|
||||
if (message.marketplace_name !== undefined) {
|
||||
obj.marketplace_name = message.marketplace_name
|
||||
}
|
||||
return obj
|
||||
return message ?? {}
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<ClaudeCodeInternalEvent>, I>>(
|
||||
create<I extends ClaudeCodeInternalEvent>(
|
||||
base?: I,
|
||||
): ClaudeCodeInternalEvent {
|
||||
return ClaudeCodeInternalEvent.fromPartial(base ?? ({} as any))
|
||||
return base ?? {}
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<ClaudeCodeInternalEvent>, I>>(
|
||||
|
||||
fromPartial<I extends ClaudeCodeInternalEvent>(
|
||||
object: I,
|
||||
): ClaudeCodeInternalEvent {
|
||||
const message = createBaseClaudeCodeInternalEvent()
|
||||
message.event_name = object.event_name ?? ''
|
||||
message.client_timestamp = object.client_timestamp ?? undefined
|
||||
message.model = object.model ?? ''
|
||||
message.session_id = object.session_id ?? ''
|
||||
message.user_type = object.user_type ?? ''
|
||||
message.betas = object.betas ?? ''
|
||||
message.env =
|
||||
object.env !== undefined && object.env !== null
|
||||
? EnvironmentMetadata.fromPartial(object.env)
|
||||
: undefined
|
||||
message.entrypoint = object.entrypoint ?? ''
|
||||
message.agent_sdk_version = object.agent_sdk_version ?? ''
|
||||
message.is_interactive = object.is_interactive ?? false
|
||||
message.client_type = object.client_type ?? ''
|
||||
message.process = object.process ?? ''
|
||||
message.additional_metadata = object.additional_metadata ?? ''
|
||||
message.auth =
|
||||
object.auth !== undefined && object.auth !== null
|
||||
? PublicApiAuth.fromPartial(object.auth)
|
||||
: undefined
|
||||
message.server_timestamp = object.server_timestamp ?? undefined
|
||||
message.event_id = object.event_id ?? ''
|
||||
message.device_id = object.device_id ?? ''
|
||||
message.swe_bench_run_id = object.swe_bench_run_id ?? ''
|
||||
message.swe_bench_instance_id = object.swe_bench_instance_id ?? ''
|
||||
message.swe_bench_task_id = object.swe_bench_task_id ?? ''
|
||||
message.email = object.email ?? ''
|
||||
message.agent_id = object.agent_id ?? ''
|
||||
message.parent_session_id = object.parent_session_id ?? ''
|
||||
message.agent_type = object.agent_type ?? ''
|
||||
message.slack =
|
||||
object.slack !== undefined && object.slack !== null
|
||||
? SlackContext.fromPartial(object.slack)
|
||||
: undefined
|
||||
message.team_name = object.team_name ?? ''
|
||||
message.skill_name = object.skill_name ?? ''
|
||||
message.plugin_name = object.plugin_name ?? ''
|
||||
message.marketplace_name = object.marketplace_name ?? ''
|
||||
return message
|
||||
return object ?? {}
|
||||
},
|
||||
}
|
||||
|
||||
type Builtin =
|
||||
| Date
|
||||
| Function
|
||||
| Uint8Array
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined
|
||||
|
||||
type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||
[K in Exclude<keyof I, KeysOfUnion<P>>]: never
|
||||
}
|
||||
|
||||
function fromTimestamp(t: Timestamp): Date {
|
||||
let millis = (t.seconds || 0) * 1_000
|
||||
millis += (t.nanos || 0) / 1_000_000
|
||||
return new globalThis.Date(millis)
|
||||
}
|
||||
|
||||
function fromJsonTimestamp(o: any): Date {
|
||||
if (o instanceof globalThis.Date) {
|
||||
return o
|
||||
} else if (typeof o === 'string') {
|
||||
return new globalThis.Date(o)
|
||||
} else {
|
||||
return fromTimestamp(Timestamp.fromJSON(o))
|
||||
}
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
interface MessageFns<T> {
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
|
||||
@@ -1,100 +1,25 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.6.1
|
||||
// protoc unknown
|
||||
// source: events_mono/common/v1/auth.proto
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
/** PublicApiAuth contains authentication context automatically injected by the API */
|
||||
export interface PublicApiAuth {
|
||||
account_id?: number | undefined
|
||||
organization_uuid?: string | undefined
|
||||
account_uuid?: string | undefined
|
||||
}
|
||||
|
||||
function createBasePublicApiAuth(): PublicApiAuth {
|
||||
return { account_id: 0, organization_uuid: '', account_uuid: '' }
|
||||
}
|
||||
|
||||
export const PublicApiAuth: MessageFns<PublicApiAuth> = {
|
||||
export const PublicApiAuth = {
|
||||
fromJSON(object: any): PublicApiAuth {
|
||||
return {
|
||||
account_id: isSet(object.account_id)
|
||||
? globalThis.Number(object.account_id)
|
||||
: 0,
|
||||
organization_uuid: isSet(object.organization_uuid)
|
||||
? globalThis.String(object.organization_uuid)
|
||||
: '',
|
||||
account_uuid: isSet(object.account_uuid)
|
||||
? globalThis.String(object.account_uuid)
|
||||
: '',
|
||||
}
|
||||
return object ?? {}
|
||||
},
|
||||
|
||||
toJSON(message: PublicApiAuth): unknown {
|
||||
const obj: any = {}
|
||||
if (message.account_id !== undefined) {
|
||||
obj.account_id = Math.round(message.account_id)
|
||||
}
|
||||
if (message.organization_uuid !== undefined) {
|
||||
obj.organization_uuid = message.organization_uuid
|
||||
}
|
||||
if (message.account_uuid !== undefined) {
|
||||
obj.account_uuid = message.account_uuid
|
||||
}
|
||||
return obj
|
||||
return message ?? {}
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<PublicApiAuth>, I>>(
|
||||
base?: I,
|
||||
): PublicApiAuth {
|
||||
return PublicApiAuth.fromPartial(base ?? ({} as any))
|
||||
create<I extends PublicApiAuth>(base?: I): PublicApiAuth {
|
||||
return base ?? {}
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<PublicApiAuth>, I>>(
|
||||
object: I,
|
||||
): PublicApiAuth {
|
||||
const message = createBasePublicApiAuth()
|
||||
message.account_id = object.account_id ?? 0
|
||||
message.organization_uuid = object.organization_uuid ?? ''
|
||||
message.account_uuid = object.account_uuid ?? ''
|
||||
return message
|
||||
|
||||
fromPartial<I extends PublicApiAuth>(object: I): PublicApiAuth {
|
||||
return object ?? {}
|
||||
},
|
||||
}
|
||||
|
||||
type Builtin =
|
||||
| Date
|
||||
| Function
|
||||
| Uint8Array
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined
|
||||
|
||||
type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||
[K in Exclude<keyof I, KeysOfUnion<P>>]: never
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
interface MessageFns<T> {
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
|
||||
@@ -1,223 +1,40 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.6.1
|
||||
// protoc unknown
|
||||
// source: events_mono/growthbook/v1/growthbook_experiment_event.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { Timestamp } from '../../../google/protobuf/timestamp.js'
|
||||
import { PublicApiAuth } from '../../common/v1/auth.js'
|
||||
|
||||
/**
|
||||
* GrowthBook experiment assignment event
|
||||
* This event tracks when a user is exposed to an experiment variant
|
||||
* See: https://docs.growthbook.io/guide/bigquery
|
||||
*/
|
||||
import type { PublicApiAuth } from '../../common/v1/auth.js'
|
||||
|
||||
export interface GrowthbookExperimentEvent {
|
||||
/** Unique event identifier (for deduplication) */
|
||||
event_id?: string | undefined
|
||||
/** When user was exposed to experiment (maps to GrowthBook's timestamp column) */
|
||||
timestamp?: Date | undefined
|
||||
/** Experiment tracking key (maps to GrowthBook's experiment_id column) */
|
||||
experiment_id?: string | undefined
|
||||
/** Variation index: 0=control, 1+=variants (maps to GrowthBook's variation_id column) */
|
||||
variation_id?: number | undefined
|
||||
/** Environment where assignment occurred */
|
||||
environment?: string | undefined
|
||||
/** User attributes at time of assignment */
|
||||
user_attributes?: string | undefined
|
||||
/** Experiment metadata */
|
||||
experiment_metadata?: string | undefined
|
||||
/** Device identifier for the client */
|
||||
device_id?: string | undefined
|
||||
/** Authentication context automatically injected by the API */
|
||||
auth?: PublicApiAuth | undefined
|
||||
/** Session identifier for tracking user sessions */
|
||||
session_id?: string | undefined
|
||||
/** Anonymous identifier for unauthenticated users */
|
||||
anonymous_id?: string | undefined
|
||||
/** Event metadata variables (automatically populated by internal-tools-common event_logging library) */
|
||||
event_metadata_vars?: string | undefined
|
||||
}
|
||||
|
||||
function createBaseGrowthbookExperimentEvent(): GrowthbookExperimentEvent {
|
||||
return {
|
||||
event_id: '',
|
||||
timestamp: undefined,
|
||||
experiment_id: '',
|
||||
variation_id: 0,
|
||||
environment: '',
|
||||
user_attributes: '',
|
||||
experiment_metadata: '',
|
||||
device_id: '',
|
||||
auth: undefined,
|
||||
session_id: '',
|
||||
anonymous_id: '',
|
||||
event_metadata_vars: '',
|
||||
}
|
||||
}
|
||||
|
||||
export const GrowthbookExperimentEvent: MessageFns<GrowthbookExperimentEvent> =
|
||||
{
|
||||
fromJSON(object: any): GrowthbookExperimentEvent {
|
||||
return {
|
||||
event_id: isSet(object.event_id)
|
||||
? globalThis.String(object.event_id)
|
||||
: '',
|
||||
timestamp: isSet(object.timestamp)
|
||||
? fromJsonTimestamp(object.timestamp)
|
||||
: undefined,
|
||||
experiment_id: isSet(object.experiment_id)
|
||||
? globalThis.String(object.experiment_id)
|
||||
: '',
|
||||
variation_id: isSet(object.variation_id)
|
||||
? globalThis.Number(object.variation_id)
|
||||
: 0,
|
||||
environment: isSet(object.environment)
|
||||
? globalThis.String(object.environment)
|
||||
: '',
|
||||
user_attributes: isSet(object.user_attributes)
|
||||
? globalThis.String(object.user_attributes)
|
||||
: '',
|
||||
experiment_metadata: isSet(object.experiment_metadata)
|
||||
? globalThis.String(object.experiment_metadata)
|
||||
: '',
|
||||
device_id: isSet(object.device_id)
|
||||
? globalThis.String(object.device_id)
|
||||
: '',
|
||||
auth: isSet(object.auth)
|
||||
? PublicApiAuth.fromJSON(object.auth)
|
||||
: undefined,
|
||||
session_id: isSet(object.session_id)
|
||||
? globalThis.String(object.session_id)
|
||||
: '',
|
||||
anonymous_id: isSet(object.anonymous_id)
|
||||
? globalThis.String(object.anonymous_id)
|
||||
: '',
|
||||
event_metadata_vars: isSet(object.event_metadata_vars)
|
||||
? globalThis.String(object.event_metadata_vars)
|
||||
: '',
|
||||
}
|
||||
},
|
||||
|
||||
toJSON(message: GrowthbookExperimentEvent): unknown {
|
||||
const obj: any = {}
|
||||
if (message.event_id !== undefined) {
|
||||
obj.event_id = message.event_id
|
||||
}
|
||||
if (message.timestamp !== undefined) {
|
||||
obj.timestamp = message.timestamp.toISOString()
|
||||
}
|
||||
if (message.experiment_id !== undefined) {
|
||||
obj.experiment_id = message.experiment_id
|
||||
}
|
||||
if (message.variation_id !== undefined) {
|
||||
obj.variation_id = Math.round(message.variation_id)
|
||||
}
|
||||
if (message.environment !== undefined) {
|
||||
obj.environment = message.environment
|
||||
}
|
||||
if (message.user_attributes !== undefined) {
|
||||
obj.user_attributes = message.user_attributes
|
||||
}
|
||||
if (message.experiment_metadata !== undefined) {
|
||||
obj.experiment_metadata = message.experiment_metadata
|
||||
}
|
||||
if (message.device_id !== undefined) {
|
||||
obj.device_id = message.device_id
|
||||
}
|
||||
if (message.auth !== undefined) {
|
||||
obj.auth = PublicApiAuth.toJSON(message.auth)
|
||||
}
|
||||
if (message.session_id !== undefined) {
|
||||
obj.session_id = message.session_id
|
||||
}
|
||||
if (message.anonymous_id !== undefined) {
|
||||
obj.anonymous_id = message.anonymous_id
|
||||
}
|
||||
if (message.event_metadata_vars !== undefined) {
|
||||
obj.event_metadata_vars = message.event_metadata_vars
|
||||
}
|
||||
return obj
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<GrowthbookExperimentEvent>, I>>(
|
||||
base?: I,
|
||||
): GrowthbookExperimentEvent {
|
||||
return GrowthbookExperimentEvent.fromPartial(base ?? ({} as any))
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<GrowthbookExperimentEvent>, I>>(
|
||||
object: I,
|
||||
): GrowthbookExperimentEvent {
|
||||
const message = createBaseGrowthbookExperimentEvent()
|
||||
message.event_id = object.event_id ?? ''
|
||||
message.timestamp = object.timestamp ?? undefined
|
||||
message.experiment_id = object.experiment_id ?? ''
|
||||
message.variation_id = object.variation_id ?? 0
|
||||
message.environment = object.environment ?? ''
|
||||
message.user_attributes = object.user_attributes ?? ''
|
||||
message.experiment_metadata = object.experiment_metadata ?? ''
|
||||
message.device_id = object.device_id ?? ''
|
||||
message.auth =
|
||||
object.auth !== undefined && object.auth !== null
|
||||
? PublicApiAuth.fromPartial(object.auth)
|
||||
: undefined
|
||||
message.session_id = object.session_id ?? ''
|
||||
message.anonymous_id = object.anonymous_id ?? ''
|
||||
message.event_metadata_vars = object.event_metadata_vars ?? ''
|
||||
return message
|
||||
},
|
||||
}
|
||||
|
||||
type Builtin =
|
||||
| Date
|
||||
| Function
|
||||
| Uint8Array
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined
|
||||
|
||||
type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||
[K in Exclude<keyof I, KeysOfUnion<P>>]: never
|
||||
}
|
||||
|
||||
function fromTimestamp(t: Timestamp): Date {
|
||||
let millis = (t.seconds || 0) * 1_000
|
||||
millis += (t.nanos || 0) / 1_000_000
|
||||
return new globalThis.Date(millis)
|
||||
}
|
||||
|
||||
function fromJsonTimestamp(o: any): Date {
|
||||
if (o instanceof globalThis.Date) {
|
||||
return o
|
||||
} else if (typeof o === 'string') {
|
||||
return new globalThis.Date(o)
|
||||
} else {
|
||||
return fromTimestamp(Timestamp.fromJSON(o))
|
||||
}
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
interface MessageFns<T> {
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
export const GrowthbookExperimentEvent = {
|
||||
fromJSON(object: any): GrowthbookExperimentEvent {
|
||||
return object ?? {}
|
||||
},
|
||||
|
||||
toJSON(message: GrowthbookExperimentEvent): unknown {
|
||||
return message ?? {}
|
||||
},
|
||||
|
||||
create<I extends GrowthbookExperimentEvent>(
|
||||
base?: I,
|
||||
): GrowthbookExperimentEvent {
|
||||
return base ?? {}
|
||||
},
|
||||
|
||||
fromPartial<I extends GrowthbookExperimentEvent>(
|
||||
object: I,
|
||||
): GrowthbookExperimentEvent {
|
||||
return object ?? {}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,187 +1,19 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.6.1
|
||||
// protoc unknown
|
||||
// source: google/protobuf/timestamp.proto
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* A Timestamp represents a point in time independent of any time zone or local
|
||||
* calendar, encoded as a count of seconds and fractions of seconds at
|
||||
* nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||
* January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||
* Gregorian calendar backwards to year one.
|
||||
*
|
||||
* All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
|
||||
* second table is needed for interpretation, using a [24-hour linear
|
||||
* smear](https://developers.google.com/time/smear).
|
||||
*
|
||||
* The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
|
||||
* restricting to that range, we ensure that we can convert to and from [RFC
|
||||
* 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* Example 1: Compute Timestamp from POSIX `time()`.
|
||||
*
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds(time(NULL));
|
||||
* timestamp.set_nanos(0);
|
||||
*
|
||||
* Example 2: Compute Timestamp from POSIX `gettimeofday()`.
|
||||
*
|
||||
* struct timeval tv;
|
||||
* gettimeofday(&tv, NULL);
|
||||
*
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds(tv.tv_sec);
|
||||
* timestamp.set_nanos(tv.tv_usec * 1000);
|
||||
*
|
||||
* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
|
||||
*
|
||||
* FILETIME ft;
|
||||
* GetSystemTimeAsFileTime(&ft);
|
||||
* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
*
|
||||
* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
|
||||
* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
|
||||
* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
|
||||
*
|
||||
* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
|
||||
*
|
||||
* long millis = System.currentTimeMillis();
|
||||
*
|
||||
* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
|
||||
* .setNanos((int) ((millis % 1000) * 1000000)).build();
|
||||
*
|
||||
* Example 5: Compute Timestamp from Java `Instant.now()`.
|
||||
*
|
||||
* Instant now = Instant.now();
|
||||
*
|
||||
* Timestamp timestamp =
|
||||
* Timestamp.newBuilder().setSeconds(now.getEpochSecond())
|
||||
* .setNanos(now.getNano()).build();
|
||||
*
|
||||
* Example 6: Compute Timestamp from current time in Python.
|
||||
*
|
||||
* timestamp = Timestamp()
|
||||
* timestamp.GetCurrentTime()
|
||||
*
|
||||
* # JSON Mapping
|
||||
*
|
||||
* In JSON format, the Timestamp type is encoded as a string in the
|
||||
* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
|
||||
* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
|
||||
* where {year} is always expressed using four digits while {month}, {day},
|
||||
* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
||||
* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||
* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||
* is required. A proto3 JSON serializer should always use UTC (as indicated by
|
||||
* "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||
* able to accept both UTC and other timezones (as indicated by an offset).
|
||||
*
|
||||
* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
||||
* 01:30 UTC on January 15, 2017.
|
||||
*
|
||||
* In JavaScript, one can convert a Date object to this format using the
|
||||
* standard
|
||||
* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||
* method. In Python, a standard `datetime.datetime` object can be converted
|
||||
* to this format using
|
||||
* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
|
||||
* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
|
||||
* the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||
* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
|
||||
* ) to obtain a formatter capable of generating timestamps in this format.
|
||||
*/
|
||||
export interface Timestamp {
|
||||
/**
|
||||
* Represents seconds of UTC time since Unix epoch
|
||||
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||
* 9999-12-31T23:59:59Z inclusive.
|
||||
*/
|
||||
seconds?: number | undefined
|
||||
/**
|
||||
* Non-negative fractions of a second at nanosecond resolution. Negative
|
||||
* second values with fractions must still have non-negative nanos values
|
||||
* that count forward in time. Must be from 0 to 999,999,999
|
||||
* inclusive.
|
||||
*/
|
||||
nanos?: number | undefined
|
||||
}
|
||||
|
||||
function createBaseTimestamp(): Timestamp {
|
||||
return { seconds: 0, nanos: 0 }
|
||||
}
|
||||
|
||||
export const Timestamp: MessageFns<Timestamp> = {
|
||||
fromJSON(object: any): Timestamp {
|
||||
return {
|
||||
seconds: isSet(object.seconds) ? globalThis.Number(object.seconds) : 0,
|
||||
nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0,
|
||||
}
|
||||
export const Timestamp = {
|
||||
fromJSON(object: any): any {
|
||||
return object
|
||||
},
|
||||
|
||||
toJSON(message: Timestamp): unknown {
|
||||
const obj: any = {}
|
||||
if (message.seconds !== undefined) {
|
||||
obj.seconds = Math.round(message.seconds)
|
||||
}
|
||||
if (message.nanos !== undefined) {
|
||||
obj.nanos = Math.round(message.nanos)
|
||||
}
|
||||
return obj
|
||||
toJSON(message: any): unknown {
|
||||
return message ?? {}
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Timestamp>, I>>(base?: I): Timestamp {
|
||||
return Timestamp.fromPartial(base ?? ({} as any))
|
||||
create<T>(base?: T): T | {} {
|
||||
return base ?? {}
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Timestamp>, I>>(
|
||||
object: I,
|
||||
): Timestamp {
|
||||
const message = createBaseTimestamp()
|
||||
message.seconds = object.seconds ?? 0
|
||||
message.nanos = object.nanos ?? 0
|
||||
return message
|
||||
|
||||
fromPartial<T>(object: T): T {
|
||||
return object
|
||||
},
|
||||
}
|
||||
|
||||
type Builtin =
|
||||
| Date
|
||||
| Function
|
||||
| Uint8Array
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined
|
||||
|
||||
type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends globalThis.Array<infer U>
|
||||
? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
|
||||
[K in Exclude<keyof I, KeysOfUnion<P>>]: never
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined
|
||||
}
|
||||
|
||||
interface MessageFns<T> {
|
||||
fromJSON(object: any): T
|
||||
toJSON(message: T): unknown
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T
|
||||
}
|
||||
|
||||
@@ -1390,6 +1390,12 @@ const detectHostIP = memoize(
|
||||
)
|
||||
|
||||
async function installFromArtifactory(command: string): Promise<string> {
|
||||
const artifactoryBaseUrl =
|
||||
process.env.CLAUDE_CODE_INTERNAL_ARTIFACTORY_BASE_URL
|
||||
if (!artifactoryBaseUrl) {
|
||||
throw new Error('Internal artifactory base URL is not configured')
|
||||
}
|
||||
const npmrcAuthPrefix = `//${artifactoryBaseUrl.replace(/^https?:\/\//, '')}/api/npm/npm-all/:_authToken=`
|
||||
// Read auth token from ~/.npmrc
|
||||
const npmrcPath = join(os.homedir(), '.npmrc')
|
||||
let authToken: string | null = null
|
||||
@@ -1402,11 +1408,8 @@ async function installFromArtifactory(command: string): Promise<string> {
|
||||
const lines = npmrcContent.split('\n')
|
||||
for (const line of lines) {
|
||||
// Look for the artifactory auth token line
|
||||
const match = line.match(
|
||||
/\/\/artifactory\.infra\.ant\.dev\/artifactory\/api\/npm\/npm-all\/:_authToken=(.+)/,
|
||||
)
|
||||
if (match && match[1]) {
|
||||
authToken = match[1].trim()
|
||||
if (line.startsWith(npmrcAuthPrefix)) {
|
||||
authToken = line.slice(npmrcAuthPrefix.length).trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1420,8 +1423,7 @@ async function installFromArtifactory(command: string): Promise<string> {
|
||||
}
|
||||
|
||||
// Fetch the version from artifactory
|
||||
const versionUrl =
|
||||
'https://artifactory.infra.ant.dev/artifactory/armorcode-claude-code-internal/claude-vscode-releases/stable'
|
||||
const versionUrl = `${artifactoryBaseUrl}/armorcode-claude-code-internal/claude-vscode-releases/stable`
|
||||
|
||||
try {
|
||||
const versionResponse = await axios.get(versionUrl, {
|
||||
@@ -1436,7 +1438,7 @@ async function installFromArtifactory(command: string): Promise<string> {
|
||||
}
|
||||
|
||||
// Download the .vsix file from artifactory
|
||||
const vsixUrl = `https://artifactory.infra.ant.dev/artifactory/armorcode-claude-code-internal/claude-vscode-releases/${version}/claude-code.vsix`
|
||||
const vsixUrl = `${artifactoryBaseUrl}/armorcode-claude-code-internal/claude-vscode-releases/${version}/claude-code.vsix`
|
||||
const tempVsixPath = join(
|
||||
os.tmpdir(),
|
||||
`claude-code-${version}-${Date.now()}.vsix`,
|
||||
|
||||
@@ -25,11 +25,14 @@ import { getBinaryName, getPlatform } from './installer.js'
|
||||
const GCS_BUCKET_URL =
|
||||
'https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases'
|
||||
export const ARTIFACTORY_REGISTRY_URL =
|
||||
'https://artifactory.infra.ant.dev/artifactory/api/npm/npm-all/'
|
||||
process.env.CLAUDE_CODE_INTERNAL_ARTIFACTORY_REGISTRY_URL ?? ''
|
||||
|
||||
export async function getLatestVersionFromArtifactory(
|
||||
tag: string = 'latest',
|
||||
): Promise<string> {
|
||||
if (!ARTIFACTORY_REGISTRY_URL) {
|
||||
throw new Error('Internal artifactory registry URL is not configured')
|
||||
}
|
||||
const startTime = Date.now()
|
||||
const { stdout, code, stderr } = await execFileNoThrowWithCwd(
|
||||
'npm',
|
||||
@@ -152,6 +155,9 @@ export async function downloadVersionFromArtifactory(
|
||||
version: string,
|
||||
stagingPath: string,
|
||||
) {
|
||||
if (!ARTIFACTORY_REGISTRY_URL) {
|
||||
throw new Error('Internal artifactory registry URL is not configured')
|
||||
}
|
||||
const fs = getFsImplementation()
|
||||
|
||||
// If we get here, we own the lock and can delete a partial download
|
||||
|
||||
@@ -1059,9 +1059,7 @@ export function getAutoModeUnavailableNotification(
|
||||
base = 'auto mode unavailable for this model'
|
||||
break
|
||||
}
|
||||
return process.env.USER_TYPE === 'ant'
|
||||
? `${base} · #claude-code-feedback`
|
||||
: base
|
||||
return base
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,89 +1,11 @@
|
||||
/**
|
||||
* Undercover mode — safety utilities for contributing to public/open-source repos.
|
||||
*
|
||||
* When active, Claude Code adds safety instructions to commit/PR prompts and
|
||||
* strips all attribution to avoid leaking internal model codenames, project
|
||||
* names, or other Anthropic-internal information. The model is not told what
|
||||
* model it is.
|
||||
*
|
||||
* Activation:
|
||||
* - CLAUDE_CODE_UNDERCOVER=1 — force ON (even in internal repos)
|
||||
* - Otherwise AUTO: active UNLESS the repo remote matches the internal
|
||||
* allowlist (INTERNAL_MODEL_REPOS in commitAttribution.ts). Safe default
|
||||
* is ON — Claude may push to public remotes from a CWD that isn't itself
|
||||
* a git checkout (e.g. /tmp crash repro).
|
||||
* - There is NO force-OFF. This guards against model codename leaks — if
|
||||
* we're not confident we're in an internal repo, we stay undercover.
|
||||
*
|
||||
* All code paths are gated on process.env.USER_TYPE === 'ant'. Since USER_TYPE is
|
||||
* a build-time --define, the bundler constant-folds these checks and dead-code-
|
||||
* eliminates the ant-only branches from external builds. In external builds every
|
||||
* function in this file reduces to a trivial return.
|
||||
*/
|
||||
|
||||
import { getRepoClassCached } from './commitAttribution.js'
|
||||
import { getGlobalConfig } from './config.js'
|
||||
import { isEnvTruthy } from './envUtils.js'
|
||||
|
||||
export function isUndercover(): boolean {
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return true
|
||||
// Auto: active unless we've positively confirmed we're in an allowlisted
|
||||
// internal repo. 'external', 'none', and null (check not yet run) all
|
||||
// resolve to ON. The check is primed in setup.ts; only 'internal' → OFF.
|
||||
return getRepoClassCached() !== 'internal'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function getUndercoverInstructions(): string {
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
return `## UNDERCOVER MODE — CRITICAL
|
||||
|
||||
You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE repository. Your commit
|
||||
messages, PR titles, and PR bodies MUST NOT contain ANY Anthropic-internal
|
||||
information. Do not blow your cover.
|
||||
|
||||
NEVER include in commit messages or PR descriptions:
|
||||
- Internal model codenames (animal names like Capybara, Tengu, etc.)
|
||||
- Unreleased model version numbers (e.g., opus-4-7, sonnet-4-8)
|
||||
- Internal repo or project names (e.g., claude-cli-internal, anthropics/…)
|
||||
- Internal tooling, Slack channels, or short links (e.g., go/cc, #claude-code-…)
|
||||
- The phrase "Claude Code" or any mention that you are an AI
|
||||
- Any hint of what model or version you are
|
||||
- Co-Authored-By lines or any other attribution
|
||||
|
||||
Write commit messages as a human developer would — describe only what the code
|
||||
change does.
|
||||
|
||||
GOOD:
|
||||
- "Fix race condition in file watcher initialization"
|
||||
- "Add support for custom key bindings"
|
||||
- "Refactor parser for better error messages"
|
||||
|
||||
BAD (never write these):
|
||||
- "Fix bug found while testing with Claude Capybara"
|
||||
- "1-shotted by claude-opus-4-6"
|
||||
- "Generated with Claude Code"
|
||||
- "Co-Authored-By: Claude Opus 4.6 <…>"
|
||||
`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to show the one-time explainer dialog for auto-undercover.
|
||||
* True when: undercover is active via auto-detection (not forced via env),
|
||||
* and the user hasn't seen the notice before. Pure — the component marks the
|
||||
* flag on mount.
|
||||
*/
|
||||
export function shouldShowUndercoverAutoNotice(): boolean {
|
||||
if (process.env.USER_TYPE === 'ant') {
|
||||
// If forced via env, user already knows; don't nag.
|
||||
if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return false
|
||||
if (!isUndercover()) return false
|
||||
if (getGlobalConfig().hasSeenUndercoverAutoNotice) return false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user