%r8toeQ4W@FEw8~e)Zk=PW{zi
z|JlaPR7Gs0<1O1m&%v>Uba1OmorDA#R
zoRzrff9!AZFHGjOrkP*3{OnhEJ^w;bXvzx=L3--cTfhB{Z|!@Ef~qe*vF;#NVvUqU
zgP(PKg14Ngo@AIy|H#<
z?Xm4aYs3$zR8()q_a8iQ;`sU5JDZ>XW}`kr0&Pb)k{ok~4mn?RANAfWeRSsGFW#Om
z(qU5$6?ssWz5_T*jqsfDP{@MI+?RI{mHE-d6Z5f&VBgalAKO7C(fne$(xH)YI`*}#
z&Bo}>8}o0RxSaRN8AVj_B|`1dE6fU_Vt{X7r?*dD>bDztKN%F0@LVDN@`djvHzgKo
zFi^&n00y355T0_!x|Xd>Z42Wn<(k%QR7s8QFJ!>gANsANW
zBAFhO9VXj&<*2Z5oRgAuMzJ5uK~!S7C4}ki+FctNr65FY&=KHmaB@FWwI&@odbm~{
zd-dOc6{*2;J%pX=b?NK}^x+4$x~rkp4QnOZ2EKBN~IjK2JpFBC=g-n;LgJ4Z$CV@uLhi|OQvGb61{
zWv|Aa2RSlxrev&S0z-&70))_>0F)tB1Zd^Dhxrn!sMc+BnC(5VzfvQPY&<@g5O^V@
z{W@eoBBW-EzI1fo@Mz_=pT4?t?~5q4>@L&gi!^g>G33=K2+PdjCRBM)E59;7K?Z>h
z*%C!8%}Qz0AU3szh^yuLP4yi+Tg_oWnJ^Q+<+xXn6vP|}?(v3;LnCzbxyfgaPQLQW
z@k^IycRsm}*|z(^%NG{sb=FIm9d;(>EMOc^Owm`y3sh8VFt`#tks@^bUacMlQL%IP
zx}Cej$@K)ztTjAipiYlqji`7JAb=+H0O(=p%L_h8Q(9hL9UH4lUDDiastYBB(k9~q
zg8^2>bIN))Hz=P}K^R7*TipHBzN23WN7qm%B_5er<(d^D#I50g&N#wj9XMJo
z>bEJWqIoz=DRjtPi`RS$u3x1_eXO&ZXZ-@P$rajI3FIO;sMs$7{nCPq5Md+vr{6!+
z81iica)Lx#wgj+2odQ*Xt_^fU_50<;iLqxwG{CX*{r(_19|*rF9@R8I;GqQ9brsBdkJk6VuS|+q>fF-7hm?O1o0%6%suN
zZBh-RSPDN?eaG9CYPNP`@SWd3xc{j-D2^%39L5&N51f&40KK6krW;r3jT7aG*FRfX
z$+O&+#tS|A3tHsdUbuAY@|Eu7hGv{l5Gz1S_RPZLdq+mcCxgg|fMIxV-&iU0VD>i;
zJDpBmE={hj95}RPY=R_c1LZfdloC(@dtqc&m%w3k;-?SKpS_)SLqoA~Tm%8Q7&FPa
zWQJ+00qeOt?bnY_|IUlsH$)W35t-)mhmVfJ6Cf&AR&c&?su>8XR-pM_x9UymbX=T(
zY_xMa=5YaekEp~LTe>=}-}>1H%rzDkb7wHS@HavuTkbe~2ZE+Tfk=3grp4^7{>c-U
z{_<}gH#(4M(Y6CdCDG-gD})lV!YRu9S^$4j=rXDnC>!UI;;Mu*3F*Oo&kt{$xNzyh
zZ3bHdsSqtL5^DovjCN2kze;dtBwA_VbutKsd!2{V7neVLpEhhEUa2$|79cX10;JF<
z-X*chRSts^CN79Fp)$}0M9NVFHU)&b8J+($|KX4R=gRfveqKq7N-qm!PbAzJDvX$bHo#tZ+JS_iLJaK$
zL-QKoly*o7x^kP|e(Tq_Zq5Z^ja+DD=*mc{O0IcX7S3kFtz>8<7#(e{c8YuR3%Z>N
z%b+jXB3x3I{p;6e7Z*vUJ-Tyu_L(EwfhVTGjU`wfOsOO$10({f0$8XhXlaSweDn6l
zm*?pC$P9y`4KRZ6KFC~E}(+z
zjCxvSag$BY+^i2YBTA?~WS=;&b$D$;QI6AyUP&DlTlf=RAENb}lbIWrKKtxpmJV=Y
zy^VqgwSr)sUO##I&ircbWFg4LFeZR{Y8Y%u-$FElf=9Z5az`aay5!l#ne&&fQKuxd
zBCPNuFHH1Yn<@pxeJ$D|N%tI>f>`lX)2`iI+*kczO7e{Rfkg(dzxX_uu)&&6OpJV_-sSM$3J=GBexm!w*!fib_%DDy_)w
zSl#W}F|lcFrPYMywwdV5NV~x1XjByRxR{=M@ZR~w1r1L`i#e^?vS!t^U6h9PV*Boi
z4O0|E9$#ganPbmA+wG*EeR>_!gc%lr1hOBl*xR$qrK^x=W=^5hInP^>e(86g`QCRo
ze)l_%@7yt2tpF=I3k-`GBpZ+y)rM+K{^wi&cdtXYx^#b;KD~TNCBn(9V5?vL`qOr80evQ5?#LG>
zW@fH2$#S?|G$z1s2M%m%SP)eH?Y};5Hsj@X+Ft5r8YU^4nw)rYzo=qMF%akyp1#cK
z+uu60vfOJn237$^OH;2~)*GWh7wQPA(Lgn%JZ<9xhOZLQruDR8{hBxi%X^0d;g(Tc
zfM())r=W$E{^gmu#if;gnugV=H9EBK=^ZGiR~iiN4_^K|jFD6(s`S2icr-7H5IT!O
zI4X4bt8?#_vO~}XY)d(6KDNAcH5%PsZHgFGVYF>(_=68mm!fBzuHSIW)p8*z7As|c
zn0M>0SLcoVZnahp)qBT|Ozqu{JgDN680>(s1Z+E_C`ytfiUJPsW2gn32@N-!)|5tP
z&Vmk0$!xVbboS#<&_hbiVHnIup{wd(e5k4B>-x!qyLUAr-yrXed@$%1JPiH~Vk2=0b3xi@
zgAv2U1N%1%n<85(}~+$R@r>wDU7(LTL#Rn=pjh#(P_gjE6;Qy#i>
zNZQemAKAa>(5@%OYdjf5{X7r{ulp?>XVc>YQAkczLG3<(UOM;yK)4!a0u+-13D&Qy^v9*PT7FLAerN&=sU|9u6
zR1AvmXcD!eU@M|-TvrSE_D9+Hhx?}^NoMh;iD}UYlXa56uZwi%!Bqw0LjE)}M
z{qz6*pp&M$pN7$}b76_12&)CN_-jGHRm77J-mhqcd}_S4XUnF^dNK-e!{1#7xuCCj
zD16H21`r=J>-XCpJuvRJ#GVN{3@Pv%Z|^9uw<7R)AH-s4`pWW&>4#~a_RAEbR4Qnr
zO6t&VJ+*n=&Z$k?0^ds%wuYq-SR%qB9_{8Z<%124M?A6r2XSUv_
const TUI_AGENT_KIND_BY_AGENT = {
claude: 'claude-code',
'claude-agent-teams': 'claude-agent-teams',
+ codebuddy: 'codebuddy',
openclaude: 'openclaude',
codex: 'codex',
autohand: 'autohand',
diff --git a/src/shared/agent-node-package-entrypoints.ts b/src/shared/agent-node-package-entrypoints.ts
index 905d41f95f0..7f8f9abd7cb 100644
--- a/src/shared/agent-node-package-entrypoints.ts
+++ b/src/shared/agent-node-package-entrypoints.ts
@@ -6,6 +6,7 @@
// new agent's install path is a data edit, as in agent-node-entrypoint-identities.ts.
export const NODE_PACKAGE_SCRIPT_ENTRYPOINTS: Record = {
codex: ['node_modules/@openai/codex/'],
+ codebuddy: ['node_modules/@tencent-ai/codebuddy-code/'],
gemini: ['node_modules/@google/gemini-cli/'],
// Why: ZCode's npm bin is `dist/zcode.cjs`, so a package install runs as `node …zcode.cjs`
// and never shows `zcode` as the foreground name (a SEA build still matches by name).
diff --git a/src/shared/agent-session-option-catalog-codebuddy.ts b/src/shared/agent-session-option-catalog-codebuddy.ts
new file mode 100644
index 00000000000..d0a8afa04c5
--- /dev/null
+++ b/src/shared/agent-session-option-catalog-codebuddy.ts
@@ -0,0 +1,44 @@
+import { hasFlag } from './agent-cli-flag-detection'
+import { removeAgentArgOption } from './agent-session-option-agent-args'
+import type { AgentSessionOptionCatalog, CatalogOption } from './agent-session-option-catalog-types'
+
+const CODEBUDDY_EFFORT: CatalogOption = {
+ id: 'effort',
+ label: 'Reasoning effort',
+ category: 'thought_level',
+ kind: {
+ type: 'select',
+ choices: [
+ { value: 'minimal', label: 'Minimal' },
+ { value: 'low', label: 'Low' },
+ { value: 'medium', label: 'Medium' },
+ { value: 'high', label: 'High' },
+ { value: 'xhigh', label: 'Extra high' },
+ { value: 'max', label: 'Max' }
+ ],
+ defaultValue: 'medium'
+ },
+ apply: {
+ launchArgs: (value) => ['--effort', String(value)],
+ agentArgsOverride: (tokens) => hasFlag(tokens, ['--effort']),
+ removeAgentArgs: (tokens) => removeAgentArgOption(tokens, ['--effort'])
+ }
+}
+
+export const CODEBUDDY_SESSION_OPTION_CATALOG: AgentSessionOptionCatalog = {
+ supportsWorkerLaunchPreferences: true,
+ // Stable CLI aliases let CodeBuddy resolve the account's current model choices.
+ models: [
+ { id: 'default-model', label: 'Default' },
+ { id: 'fast-model', label: 'Fast' },
+ { id: 'balanced-model', label: 'Balanced' },
+ { id: 'primary-model', label: 'Primary' },
+ { id: 'deep-model', label: 'Deep' }
+ ].map((model) => ({ ...model, options: [CODEBUDDY_EFFORT] })),
+ modelApply: {
+ launchArgs: (value) => ['--model', String(value)],
+ agentArgsOverride: (tokens) => hasFlag(tokens, ['--model']),
+ removeAgentArgs: (tokens) => removeAgentArgOption(tokens, ['--model'])
+ },
+ unknownModelOptions: [CODEBUDDY_EFFORT]
+}
diff --git a/src/shared/agent-session-option-catalog.ts b/src/shared/agent-session-option-catalog.ts
index a8e26f5a0ed..74a7f384801 100644
--- a/src/shared/agent-session-option-catalog.ts
+++ b/src/shared/agent-session-option-catalog.ts
@@ -1,3 +1,4 @@
+import { CODEBUDDY_SESSION_OPTION_CATALOG } from './agent-session-option-catalog-codebuddy'
import type { AgentType } from './agent-status-types'
import { ANTIGRAVITY_SESSION_OPTION_CATALOG } from './agent-session-option-catalog-antigravity'
import {
@@ -34,6 +35,7 @@ export { createClaudeCatalogOptions }
const CATALOGS: AgentSessionOptionCatalogMap = {
antigravity: ANTIGRAVITY_SESSION_OPTION_CATALOG,
claude: CLAUDE_SESSION_OPTION_CATALOG,
+ codebuddy: CODEBUDDY_SESSION_OPTION_CATALOG,
codex: CODEX_SESSION_OPTION_CATALOG,
gemini: GEMINI_SESSION_OPTION_CATALOG,
cursor: CURSOR_SESSION_OPTION_CATALOG,
diff --git a/src/shared/agent-session-resume.test.ts b/src/shared/agent-session-resume.test.ts
index 577492ddefc..397a1a9451f 100644
--- a/src/shared/agent-session-resume.test.ts
+++ b/src/shared/agent-session-resume.test.ts
@@ -28,6 +28,10 @@ describe('agent session resume metadata', () => {
expect(isResumableTuiAgent('kimi')).toBe(true)
})
+ it('treats CodeBuddy as a resumable TUI agent', () => {
+ expect(isResumableTuiAgent('codebuddy')).toBe(true)
+ })
+
it.each([
['claude', { session_id: 'claude-session' }, { key: 'session_id', id: 'claude-session' }],
['codex', { session_id: 'codex-session' }, { key: 'session_id', id: 'codex-session' }],
@@ -70,6 +74,7 @@ describe('agent session resume metadata', () => {
it.each([
['claude', { key: 'session_id', id: 's1' }, ['claude', '--resume', 's1']],
+ ['codebuddy', { key: 'session_id', id: 's1' }, ['codebuddy', '--resume', 's1']],
['codex', { key: 'session_id', id: 's1' }, ['codex', 'resume', 's1']],
['gemini', { key: 'session_id', id: 's1' }, ['gemini', '--resume', 's1']],
['antigravity', { key: 'conversation_id', id: 's1' }, ['agy', '--conversation', 's1']],
diff --git a/src/shared/agent-session-resume.ts b/src/shared/agent-session-resume.ts
index 4cbc240c764..40a3699af3c 100644
--- a/src/shared/agent-session-resume.ts
+++ b/src/shared/agent-session-resume.ts
@@ -5,6 +5,7 @@ import type { TuiAgent } from './tui-agent'
export const RESUMABLE_TUI_AGENTS = [
'claude',
+ 'codebuddy',
'codex',
'qoder',
'gemini',
@@ -196,6 +197,7 @@ export function extractAgentProviderSession(
// since recent Claude Code names the transcript file with a UUID that differs
// from the hook session_id (so the id-based glob no longer finds it).
case 'qoder':
+ case 'codebuddy':
case 'claude':
case 'codex': {
const id = readSessionId(payload, ['session_id'])
@@ -277,6 +279,8 @@ export function getAgentResumeArgv(
): string[] | null {
const id = providerSession.id
switch (agent) {
+ case 'codebuddy':
+ return providerSession.key === 'session_id' ? ['codebuddy', '--resume', id] : null
case 'claude':
return providerSession.key === 'session_id' ? ['claude', '--resume', id] : null
case 'codex':
diff --git a/src/shared/agent-type-label.ts b/src/shared/agent-type-label.ts
index a4d70a76037..72d1b1b6d63 100644
--- a/src/shared/agent-type-label.ts
+++ b/src/shared/agent-type-label.ts
@@ -4,6 +4,7 @@ import type { AgentType } from './agent-status-types'
// (e.g. native chat's empty state on both surfaces) from one source of truth.
const WELL_KNOWN_LABELS: Record = {
claude: 'Claude',
+ codebuddy: 'CodeBuddy',
openclaude: 'OpenClaude',
codex: 'Codex',
qoder: 'Qoder CLI',
diff --git a/src/shared/ai-vault-resume-command.test.ts b/src/shared/ai-vault-resume-command.test.ts
index da8ced39e0e..db89cb41263 100644
--- a/src/shared/ai-vault-resume-command.test.ts
+++ b/src/shared/ai-vault-resume-command.test.ts
@@ -28,6 +28,17 @@ describe('buildAiVaultResumeCommand', () => {
).toBe('cmd /d /s /c "cd /d ""C:\\Users\\Ada Lovelace\\repo"" && codex resume ""session-1"""')
})
+ it('resumes CodeBuddy sessions with the Claude-compatible --resume flag', () => {
+ expect(
+ buildAiVaultResumeCommand({
+ agent: 'codebuddy',
+ sessionId: 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee',
+ cwd: '/repo/app',
+ platform: 'darwin'
+ })
+ ).toBe("cd '/repo/app' && codebuddy --resume 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee'")
+ })
+
it('builds a direct queued command for a live cmd shell', () => {
expect(
buildAiVaultResumeCommand({
diff --git a/src/shared/ai-vault-resume-command.ts b/src/shared/ai-vault-resume-command.ts
index cfff419c95c..efb2ddbe5d3 100644
--- a/src/shared/ai-vault-resume-command.ts
+++ b/src/shared/ai-vault-resume-command.ts
@@ -218,6 +218,7 @@ function buildAgentResumeInvocation(
return `${baseCommand} resume ${sessionArg}`
case 'cline':
return `${baseCommand} --id ${sessionArg}`
+ case 'codebuddy':
case 'claude':
case 'zcode':
case 'cursor':
diff --git a/src/shared/ai-vault-session-deletion.ts b/src/shared/ai-vault-session-deletion.ts
index d3c2211ab84..4875ab6e889 100644
--- a/src/shared/ai-vault-session-deletion.ts
+++ b/src/shared/ai-vault-session-deletion.ts
@@ -40,6 +40,7 @@ export const AI_VAULT_DELETABLE_AGENTS = [
'pi',
'omp',
'claude',
+ 'codebuddy',
'rovo',
'grok',
'cline'
diff --git a/src/shared/ai-vault-types.ts b/src/shared/ai-vault-types.ts
index 28f93dcfbe1..e337f98d7a0 100644
--- a/src/shared/ai-vault-types.ts
+++ b/src/shared/ai-vault-types.ts
@@ -3,6 +3,7 @@ import type { ExecutionHostId, ExecutionHostScope } from './execution-host'
export const AI_VAULT_AGENTS = [
'claude',
+ 'codebuddy',
'codex',
'hermes',
'pi',
@@ -51,6 +52,7 @@ export type AiVaultGroup = 'project' | 'folder' | 'agent'
export const AI_VAULT_AGENT_LABELS = {
claude: 'Claude',
+ codebuddy: 'CodeBuddy',
codex: 'Codex',
hermes: 'Hermes',
pi: 'Pi',
diff --git a/src/shared/codebuddy-agent.test.ts b/src/shared/codebuddy-agent.test.ts
new file mode 100644
index 00000000000..6915cf8d573
--- /dev/null
+++ b/src/shared/codebuddy-agent.test.ts
@@ -0,0 +1,67 @@
+import { readFileSync } from 'node:fs'
+import { join } from 'node:path'
+import { describe, expect, it } from 'vitest'
+import { createHookListenerState } from './agent-hook-listener/listener-state'
+import { normalizeAndAccept } from './agent-hook-listener-test-harness'
+import { recognizeAgentProcessFromCommandLine } from './agent-process-recognition'
+import { getAgentSessionOptionCatalog } from './agent-session-option-catalog'
+import { getAgentResumeArgv } from './agent-session-resume'
+
+describe('CodeBuddy harness', () => {
+ it('replays the live question lifecycle without settling at late SessionStart', () => {
+ const state = createHookListenerState()
+ const events = readFileSync(
+ join(__dirname, '__fixtures__/codebuddy-question-hooks.jsonl'),
+ 'utf8'
+ )
+ .trim()
+ .split('\n')
+ .map((line) => JSON.parse(line))
+ const rows = events.map((event) => normalizeAndAccept(state, 'codebuddy', event))
+ expect(rows.map((row) => row?.payload.state ?? null)).toEqual([
+ 'working',
+ null,
+ 'waiting',
+ 'working',
+ 'working',
+ 'done'
+ ])
+ expect(rows[2]?.payload).toMatchObject({ agentType: 'codebuddy', toolName: 'AskUserQuestion' })
+ expect(rows[5]?.payload).toMatchObject({ agentType: 'codebuddy', lastAssistantMessage: 'Red' })
+ expect(rows[5]?.providerSession?.id).toBe('codebuddy-verification')
+ })
+
+ it.each([
+ 'codebuddy',
+ 'cbc',
+ 'C:\\Tools\\codebuddy.cmd',
+ 'node /usr/lib/node_modules/@tencent-ai/codebuddy-code/dist/codebuddy.js'
+ ])('recognizes interactive %s', (command) => {
+ expect(recognizeAgentProcessFromCommandLine(command)?.agent).toBe('codebuddy')
+ })
+
+ it('excludes print runs and resumes the original provider session', () => {
+ for (const flag of ['--print', '--serve', '--acp', '--bg', '--background']) {
+ expect(recognizeAgentProcessFromCommandLine(`codebuddy ${flag}`)).toBeNull()
+ }
+ expect(recognizeAgentProcessFromCommandLine('codebuddy -- --serve')?.agent).toBe('codebuddy')
+ expect(getAgentResumeArgv('codebuddy', { key: 'session_id', id: 'session-1' })).toEqual([
+ 'codebuddy',
+ '--resume',
+ 'session-1'
+ ])
+ })
+
+ it('passes explicit model and effort preferences through the CLI flags', () => {
+ const catalog = getAgentSessionOptionCatalog('codebuddy')
+ expect(catalog?.modelApply.launchArgs?.('fast-model')).toEqual(['--model', 'fast-model'])
+ expect(catalog?.unknownModelOptions?.[0].apply.launchArgs?.('high')).toEqual([
+ '--effort',
+ 'high'
+ ])
+ expect(catalog?.modelApply.removeAgentArgs?.(['--model', 'old', '--', 'prompt'])).toEqual([
+ '--',
+ 'prompt'
+ ])
+ })
+})
diff --git a/src/shared/codebuddy-headless-command.ts b/src/shared/codebuddy-headless-command.ts
new file mode 100644
index 00000000000..5c632b27b3b
--- /dev/null
+++ b/src/shared/codebuddy-headless-command.ts
@@ -0,0 +1,13 @@
+import { agentArgOptionTokens } from './agent-session-option-agent-args'
+import { isPrintModeHeadlessOneShotCommand, optionName } from './print-mode-headless-command'
+
+const NON_INTERACTIVE_FLAGS = new Set(['--serve', '--acp', '--bg', '--background'])
+
+export function isCodebuddyNonInteractiveCommand(tokens: readonly string[]): boolean {
+ return (
+ isPrintModeHeadlessOneShotCommand(tokens) ||
+ agentArgOptionTokens(tokens)
+ .slice(1)
+ .some((token) => NON_INTERACTIVE_FLAGS.has(optionName(token)))
+ )
+}
diff --git a/src/shared/protocol-version.ts b/src/shared/protocol-version.ts
index bd8b3f62a31..3a877a80900 100644
--- a/src/shared/protocol-version.ts
+++ b/src/shared/protocol-version.ts
@@ -236,6 +236,8 @@ export const AGENT_SESSION_OPENCODE2_RESUME_RUNTIME_CAPABILITY =
'agent-session.opencode2-resume.v1' as const
export const AGENT_SESSION_MUSE_RESUME_RUNTIME_CAPABILITY = 'agent-session.muse-resume.v1' as const
export const AGENT_SESSION_DSH_RESUME_RUNTIME_CAPABILITY = 'agent-session.dsh-resume.v1' as const
+export const AGENT_SESSION_CODEBUDDY_RESUME_RUNTIME_CAPABILITY =
+ 'agent-session.codebuddy-resume.v1' as const
export const AGENT_SESSION_QODER_RESUME_RUNTIME_CAPABILITY =
'agent-session.qoder-resume.v1' as const
export const AGENT_SESSION_ZCODE_RESUME_RUNTIME_CAPABILITY =
@@ -402,6 +404,7 @@ export const RUNTIME_CAPABILITIES = [
AGENT_SESSION_MUSE_RESUME_RUNTIME_CAPABILITY,
AGENT_SESSION_DSH_RESUME_RUNTIME_CAPABILITY,
AGENT_SESSION_QODER_RESUME_RUNTIME_CAPABILITY,
+ AGENT_SESSION_CODEBUDDY_RESUME_RUNTIME_CAPABILITY,
AGENT_SESSION_ZCODE_RESUME_RUNTIME_CAPABILITY,
FILE_MUTATION_OWNERSHIP_RUNTIME_CAPABILITY,
GITHUB_MARK_PR_READY_RUNTIME_CAPABILITY,
diff --git a/src/shared/skills-cli-agent-keys.ts b/src/shared/skills-cli-agent-keys.ts
index f8d1a913172..1955bfa6d58 100644
--- a/src/shared/skills-cli-agent-keys.ts
+++ b/src/shared/skills-cli-agent-keys.ts
@@ -14,6 +14,7 @@ import type { TuiAgent } from './tui-agent'
export const SKILLS_CLI_AGENT_KEY_BY_TUI_AGENT = {
claude: 'claude-code',
'claude-agent-teams': 'claude-code',
+ codebuddy: 'codebuddy',
// Why: Orca states OpenClaude reads Claude-owned roots (native-chat-agent-profiles).
openclaude: 'claude-code',
codex: 'codex',
diff --git a/src/shared/telemetry-property-schemas.ts b/src/shared/telemetry-property-schemas.ts
index cee76b677d6..89f62138407 100644
--- a/src/shared/telemetry-property-schemas.ts
+++ b/src/shared/telemetry-property-schemas.ts
@@ -10,6 +10,7 @@ import type { GlobalSettings } from './global-settings-types'
export const AGENT_KIND_VALUES = [
'claude-code',
'claude-agent-teams',
+ 'codebuddy',
'openclaude',
'codex',
'autohand',
diff --git a/src/shared/tui-agent-config.ts b/src/shared/tui-agent-config.ts
index 15318913968..27cda1d1979 100644
--- a/src/shared/tui-agent-config.ts
+++ b/src/shared/tui-agent-config.ts
@@ -97,6 +97,11 @@ const TUI_AGENT_CONFIG_SOURCE: Record = {
promptInjectionMode: 'stdin-after-start',
pasteNeedsTypedRequest: true
},
+ codebuddy: {
+ detectCmd: 'codebuddy',
+ detectCmdAliases: ['cbc'],
+ promptInjectionMode: 'argv'
+ },
openclaude: {
detectCmd: 'openclaude',
promptInjectionMode: 'argv',
@@ -148,7 +153,6 @@ const TUI_AGENT_CONFIG_SOURCE: Record = {
detectCmd: 'opencode2',
// The private server inherits this pane's hook endpoint and identity.
launchCmd: 'opencode2 --standalone',
- expectedProcess: 'opencode2',
promptInjectionMode: 'flag-prompt',
draftPasteReadySignal: 'render-cursor-after-bracketed-paste',
draftPasteReadyTimeoutMs: 20_000
diff --git a/src/shared/tui-agent-display-names.ts b/src/shared/tui-agent-display-names.ts
index a90daa3242f..31b6035a953 100644
--- a/src/shared/tui-agent-display-names.ts
+++ b/src/shared/tui-agent-display-names.ts
@@ -8,6 +8,7 @@ import type { TuiAgent } from './tui-agent'
export const TUI_AGENT_DISPLAY_NAMES: Record = {
claude: 'Claude',
'claude-agent-teams': 'Claude Agent Teams',
+ codebuddy: 'CodeBuddy',
openclaude: 'OpenClaude',
codex: 'Codex',
devin: 'Devin',
diff --git a/src/shared/tui-agent-permissions.ts b/src/shared/tui-agent-permissions.ts
index 3a9912bf066..9fc8eb0a691 100644
--- a/src/shared/tui-agent-permissions.ts
+++ b/src/shared/tui-agent-permissions.ts
@@ -5,6 +5,7 @@ export type AgentPermissionMode = 'yolo' | 'manual' | 'mixed'
export const YOLO_TUI_AGENT_ARGS: Partial> = {
claude: '--dangerously-skip-permissions',
+ codebuddy: '--dangerously-skip-permissions',
'claude-agent-teams': '--dangerously-skip-permissions',
openclaude: '--dangerously-skip-permissions',
codex: '--dangerously-bypass-approvals-and-sandbox',
diff --git a/src/shared/tui-agent-rest-signal.test.ts b/src/shared/tui-agent-rest-signal.test.ts
index f7d11032875..6e573acc0b0 100644
--- a/src/shared/tui-agent-rest-signal.test.ts
+++ b/src/shared/tui-agent-rest-signal.test.ts
@@ -29,6 +29,7 @@ const EXPECTED_REST_SIGNALS: Record = {
grok: 'title',
muse: 'ready-body',
qoder: 'ready-body',
+ codebuddy: 'none',
autohand: 'none',
ante: 'none',
trae: 'none',
diff --git a/src/shared/tui-agent-selection.ts b/src/shared/tui-agent-selection.ts
index 85567dcb334..acf305dad59 100644
--- a/src/shared/tui-agent-selection.ts
+++ b/src/shared/tui-agent-selection.ts
@@ -45,7 +45,8 @@ export const TUI_AGENT_AUTO_PICK_ORDER = [
'rovo',
'hermes',
'devin',
- 'openclaw'
+ 'openclaw',
+ 'codebuddy'
] as const satisfies readonly TuiAgent[]
// Why: fresh installs should expose Claude Agent Teams in agent pickers; the
diff --git a/src/shared/tui-agent.ts b/src/shared/tui-agent.ts
index 19722220888..a98c45101fd 100644
--- a/src/shared/tui-agent.ts
+++ b/src/shared/tui-agent.ts
@@ -3,6 +3,7 @@
export type TuiAgent =
| 'claude' // Claude Code
| 'claude-agent-teams' // Claude Code Agent Teams via Orca native panes
+ | 'codebuddy' // CodeBuddy Code
| 'openclaude' // OpenClaude
| 'codex' // OpenAI Codex
| 'autohand' // Autohand Code CLI