앱·서버
d7a8d6f3, 수치는 2026-10-23 GitHub 기준입니다.T3 Code는 이미 설치하거나 연결해 둔 코딩 에이전트에 공통 화면을 제공하는 애플리케이션입니다. README는 이를 agent harness control surface라고 부릅니다. 여기서 harness는 에이전트의 도구 실행·승인·대화 진행을 관리하는 실행 틀이고, control surface는 사용자가 그 작업을 조작하는 화면입니다.
프로젝트 파일과 에이전트 실행은 서버가 있는 컴퓨터에 두고, 여러 기기의 화면이 같은 작업을 제어하도록 만드는 것이 핵심입니다. 데스크톱 앱은 Electron으로 웹 화면을 감싸고 서버 실행기도 함께 제공합니다. 모바일 앱은 React Native로 별도 구현되어 다른 컴퓨터의 서버에 접속합니다. 이 역할 구분은 docs/internals/overview.md의 소유권 경계와 서버·클라이언트 패키지 구성에서 확인할 수 있습니다.
Codex나 Claude Code는 실제로 일을 하는 작업자입니다. 서버는 작업자에게 요청을 전달하고 결과를 기록하는 공방 관리자입니다. 웹·데스크톱·휴대폰은 관리자의 장부를 함께 보며 지시하는 단말입니다.
휴대폰으로 수정 요청을 보내도 파일은 공방의 컴퓨터에서 바뀝니다. 이 때문에 화면이 잠깐 끊겨도 작업의 주인과 기록은 서버에 남아야 합니다. 이후 나오는 저장·재시도·동기화 설계가 모두 여기서 출발합니다.
입문자는 모델의 추론 원리를 구현하는 프로젝트로 접근하기보다, 외부 프로그램을 안정적인 제품으로 연결하는 방법에 주목하면 좋습니다. packages/contracts의 데이터 규약, apps/server의 실행·저장, packages/client-runtime의 화면 공통 상태를 한 줄로 연결해 보는 것이 첫 목표입니다.
“이 버튼이 어떤 요청을 보내며, 그 요청이 무엇으로 저장되고, 어떤 응답이 화면을 바꾸는가?”를 설명할 수 있으면 충분한 출발입니다. 전체 코드를 한 번에 이해할 필요는 없습니다.
2026-10-23 분석 기준 GitHub Trending Monthly 16위이며, 스타는 29,528개, 포크는 7,919개입니다. 이 숫자만으로 실제 사용자 수·성능·상승 원인을 알 수는 없습니다. 다음 세 가지는 README의 지향과 구현 구조를 함께 볼 때 추론할 수 있는 주목 요인입니다.
README는 Claude Code·Codex·Cursor·Grok Build·OpenCode·Google Antigravity를 소개하며, 기존 구독과 연결하는 사용법을 설명합니다. 공급자마다 실행과 승인 방식이 다르지만, T3 Code는 공통 어댑터 규약으로 이를 받아들입니다. 공급자를 바꿀 때도 프로젝트·대화·차이 보기 같은 주변 경험을 이어 가려는 방향이 읽힙니다.
주목할 지점은 에이전트의 수보다, 서로 다른 에이전트를 하나의 상태 모델과 조작 방식으로 연결하는 설계입니다. 다만 실제 기능은 공급자별 역량에 따라 다릅니다. 공통 화면이 있다고 대화 복구·분기·실행 중 추가 지시까지 모두 같은 수준으로 지원되는 것은 아닙니다.
웹·데스크톱·모바일이 서버를 공유하려면 인증, 끊긴 연결의 복원, 작은 데이터 전송이 필요합니다. apps/server/src/ws.ts의 subscribeOrchestrationV2Shell()은 마지막 수신 순서 번호와 재생 예산에 따라 변경분 재생이나 새 스냅샷을 선택합니다. packages/client-runtime은 연결과 상태 처리를 웹·모바일에서 공유합니다. 휴대폰 지원이 별도 화면 하나를 추가하는 문제를 넘어서는 이유입니다.
README는 Codex 데스크톱 앱·Conductor·Claude Desktop·Cursor Glass에서 영감을 받았다고 밝힙니다. 아래는 이들 제품의 최신 기능 순위표가 아니라, 대안을 고를 때 확인할 설계 축입니다. 타 제품의 세부 기능이나 요금은 비교 범위에 포함하지 않습니다.
| 비교 축 | 대안에서 확인할 점 | T3 Code에서 확인되는 선택 |
|---|---|---|
| 에이전트 선택 | 주로 쓰는 공급자와 잘 맞는가 | 공통 어댑터와 공급자별 패키지를 둔다 |
| 조작 화면 | 터미널·편집기·데스크톱 중 어떤 흐름인가 | 웹·Electron·React Native 화면을 제공한다 |
| 원격 작업 | 파일과 실행 상태가 어느 기기에 남는가 | 서버가 파일·에이전트를 소유하고 화면은 RPC로 제어한다 |
| 수정 가능성 | 직접 고치거나 포크할 수 있는가 | 핵심 소스와 빌드·릴리스 구성이 MIT로 공개되어 있다 |
| 운영 부담 | 계정·업데이트·서버 관리를 누가 맡는가 | 자체 서버와 공급자 인증, 클라이언트 호환성을 관리해야 한다 |
여러 에이전트를 같은 흐름으로 다루고 원격 화면까지 직접 확장하려는 개발자에게 구조적 장점이 있습니다. 반면 한 공급자의 고유 기능만 깊게 사용하는 경우에는 그 공급자의 전용 화면이 더 자연스러울 수도 있습니다. 범용성이 모든 작업에서 우위를 뜻하지는 않습니다.
README의 주요 지원 목록은 여섯 공급자입니다. 하지만 apps/server/package.json에는 @t3tools/provider-pi와 @t3tools/provider-muse도 의존성으로 들어 있습니다. packages/provider-pi/src/server/adapter.ts에는 makePiAdapterV2와 PiAdapterV2Driver 구현이 있습니다.
docs/user/install.md의 공급자 표도 Pi와 Muse Code 설정을 설명합니다. 따라서 “README의 소개 목록”과 “해당 커밋에 들어 있는 구현 범위”를 구분해야 합니다. 패키지 존재만으로 모든 공급자의 기능 동등성이나 계정별 이용 가능성을 단정할 수는 없습니다.
README는 프로젝트가 초기 단계이며 버그가 있을 수 있다고 알립니다. 트렌딩 순위는 관심의 지표입니다. 실제 속도, 동시 실행 수, 장시간 안정성은 사용 환경에서 별도로 측정해야 합니다.
이 저장소는 여러 앱과 패키지를 함께 관리하는 모노레포입니다. pnpm-workspace.yaml이 apps/*, infra/*, packages/*, scripts, oxlint-plugin-t3code를 워크스페이스로 묶습니다. 아래 버전은 커밋 d7a8d6f3의 의존성 선언 또는 catalog 값입니다. 개별 선언이 공용 overrides로 바뀌는 경우에는 그 차이도 표시했습니다.
workspace:*는 같은 저장소의 패키지를 연결합니다. catalog:는 공용 버전 표를 참조합니다. ^0.3.276 같은 표기는 허용 범위이므로 정확히 그 버전만 설치한다는 뜻이 아닙니다. 설치 결과를 재현할 때는 pnpm-lock.yaml도 함께 읽어야 합니다.| 부품 | 실제 선언값·위치 | 담당하는 일 |
|---|---|---|
| Node.js | 루트 engines.node: 26.12.1 | 개발 도구와 서버 실행 기반 |
| 서버 호환 범위 | apps/server/package.json: ^22.16 || ^23.11 || >=24.10 | 배포 패키지의 허용 범위. 루트 개발 버전과 구분 |
| Effect | effect, @effect/platform-node: 4.0.5 (catalog) | 비동기 작업, 서비스 주입, 스트림, 오류 모델 |
| SQLite | packages/shared/src/nodeSqliteClient.ts가 node:sqlite 사용 | 서버 이벤트·조회 상태·명령 영수증 저장 |
| 공급자 SDK | Claude Agent SDK ^0.3.276, Cursor SDK 1.0.35, OpenCode SDK ^1.3.15 | 각 공급자 실행 체계와 연결 |
| 터미널 | node-pty: ^1.2.0-beta.15 → 1.2.0-beta.15 (공용 overrides로 고정) | 가상 터미널에서 셸 프로세스 실행 |
| 토큰·HTTP | jose: 6.2.2, undici: 8.11.2 (catalog) | 인증 토큰 처리와 HTTP 통신 |
SQLite 엔진 버전을 별도 npm SQLite 드라이버 버전으로 적으면 부정확합니다. 이 구현은 Node 내장 API를 사용합니다. apps/server/src/persistence/Sqlite.ts는 readerWorker: true와 checkpointWorker: true로 읽기 전용 작업과 체크포인트 동기화를 별도 워커에 맡기도록 설정합니다. 워커를 사용할 수 없는 경우에는 주 연결을 사용하는 대체 경로가 있습니다.
| 표면 | 주요 기술·선언값 | 소스에서 읽을 점 |
|---|---|---|
| 웹 | React·React DOM 19.3.0, TanStack Router ^1.160.2 | apps/web/package.json과 apps/web/vite.config.ts의 라우터 플러그인 |
| 입력·상태 | Tiptap ^3.31.3, Zustand ^5.0.11, @effect/atom-react: 4.0.5 | 풍부한 입력기와 공용 런타임 상태를 나누는 방법 |
| 웹 스타일 | Tailwind CSS ^4.0.0 선언 → 공용 고정값 4.3.3 | pnpm-workspace.yaml의 overrides가 catalog를 적용하며 잠금 파일도 4.3.3을 기록 |
| 차이 보기 | @pierre/diffs: 1.5.2 (catalog) | 코드 검토 화면과 변경 파일 표현 |
| 데스크톱 | Electron 44.9.3, electron-builder 26.16.1 | 웹 화면에 OS 연동과 서버 실행기를 결합 |
| 모바일 | Expo 58.0.14, React Native 0.88.0, Reanimated 4.7.1 | 별도 네이티브 화면에서 공용 계약·상태 로직 재사용 |
웹과 모바일은 HTML 컴포넌트를 그대로 공유하는 구조가 아닙니다. 두 앱이 함께 의존하는 @t3tools/client-runtime과 @t3tools/contracts가 공통 동작을 담당합니다. 화면이 달라도 재연결 규칙을 따로 구현하지 않게 하는 선택입니다.
| 영역 | 선언값·설정 | 역할 |
|---|---|---|
| 패키지 관리 | pnpm@12.14.0 | 루트 packageManager에 명시 |
| 빌드·검사 | vite-plus: 1.3.0 | vp 명령으로 빌드·테스트·정적 검사 구성 |
| Vite·TypeScript 이름 | vite → @voidzero-dev/vite-plus-core@1.3.0typescript → @tsc-rs/typescript@0.8.0 | catalog에서 npm 별칭 사용. 이름만 보고 일반 배포판 버전을 추정하지 않는다 |
| 브라우저 테스트 | Playwright 1.60.0, @vitest/browser-playwright: 5.0.5 | 웹 설정이 단위 테스트와 Chromium 테스트를 분리 |
| T3 Connect | Cloudflare Worker, Alchemy 2.0.0-beta.80, Drizzle 1.0.0-rc.5-ab785fc | infra/relay에서 환경 연결·단기 자격증명·알림 관리 |
| 네이티브 보조 도구 | Rust edition 2024, sysinfo 0.39.3, serde 1.0.228 | native/resource-monitor/Cargo.toml: 시스템 자원 측정 |
infra/relay/src/worker.ts는 Cloudflare Worker와 Postgres 관련 계층을 조합합니다. infra/relay/README.md는 연결된 뒤 일반 API·WebSocket 통신이 선택한 환경으로 향한다고 설명합니다. 이 제어 평면을 로컬 SQLite 서버의 저장 계층과 혼동하면 전체 구조를 잘못 이해하게 됩니다.
의존성 목록을 읽는 목표는 라이브러리 이름 암기가 아니라, 각 부품이 어느 경계와 책임을 맡는지 설명하는 것입니다.
.github/workflows/ci.yml의 changes 작업은 패키지 영향 범위를 계산합니다. 린트는 문서를 포함해 항상 실행하고, 빌드·타입 검사·서버·웹·모바일 테스트 등은 영향 범위에 따라 실행합니다. 변경 감지에 실패하면 검사를 넓게 실행하는 방향으로 설계되어 있습니다.
모노레포는 한 건물에 여러 작업실이 있는 형태입니다. 공용 전기 배선(contracts)을 고치면 여러 방을 점검해야 하지만, 한 방의 안내문만 바꾸면 모든 기계를 다시 돌릴 필요는 없습니다. CI의 의존성 그래프가 그 점검 범위를 결정합니다.
아래 그림의 핵심 경계는 조작하는 기기와 파일을 소유한 실행 환경 사이입니다. Electron의 렌더러도 이 규약을 따릅니다. T3 Connect의 연결 제어와 실제 에이전트 실행은 서로 다른 책임입니다.
packages/contracts/src/rpc.ts의 WsOrchestrationV2SubscribeShellRpc는 요청 입력, 성공 응답, 오류를 함께 선언합니다. TypeScript 타입을 공유하는 것에 더해 Effect Schema로 통신 경계의 데이터 모양을 표현합니다. 잘못된 JSON을 단순 타입 단언으로 믿는 방식과 구별되는 지점입니다.
// packages/contracts/src/rpc.ts의 선언
const WsOrchestrationV2SubscribeShellRpc = Rpc.make(
ORCHESTRATION_V2_WS_METHODS.subscribeShell,
{
payload: OrchestrationV2RpcSchemas.subscribeShell.input,
success: OrchestrationV2RpcSchemas.subscribeShell.output,
error: Schema.Union([
OrchestrationV2GetShellSnapshotError,
EnvironmentAuthorizationError,
]),
stream: true,
},
);
반대쪽 경계에는 공급자가 있습니다. packages/provider-core/src/server/ProviderAdapter.ts의 ProviderAdapterV2는 getCapabilities()·planSelectionTransition()·openSession()을 노출합니다. 열린 세션의 ProviderAdapterV2SessionRuntime에는 startTurn(), interruptTurn(), rollbackThread(), 이벤트 스트림 등이 정의됩니다.
구체적인 예는 packages/provider-pi/src/server/adapter.ts의 makePiAdapterV2()와 PiAdapterV2Driver.create()입니다. Pi 고유의 프로토콜 처리는 이 어댑터 안에 두고, 상위 계층에는 ProviderAdapterV2Event의 message.updated·turn.terminal 같은 공통 사건을 전달합니다.
통신 계약은 주문서 양식이고, 어댑터는 주방마다 다른 표현을 공용 양식으로 옮기는 통역사입니다. 새 주방이 생겼다고 계산대와 배달 앱 전체가 그 주방의 사투리를 배울 필요는 없습니다.
인증과 권한도 따로 읽어야 합니다. apps/server/src/auth/RpcAuthorization.ts의 RPC_REQUIRED_SCOPES는 목록 구독에 읽기 권한을, 명령 실행에 조작 권한을 지정합니다. 소켓에 접속할 수 있다는 사실과 모든 메서드를 실행할 수 있다는 사실은 다릅니다.
apps/server/src/orchestration-v2/Orchestrator.ts의 dispatchWithReceipt()는 스레드별 잠금을 적용합니다. 내부 dispatchWithReceiptEffect()는 같은 commandId의 영수증을 먼저 조회합니다. 이미 처리한 명령이면 기존 결과를 돌려주고, 다른 스레드에 같은 ID를 재사용한 충돌도 검사합니다.
새 명령은 현재 상태에 따라 이벤트와 후속 작업의 계획을 만듭니다. 이 단계에서 공급자 실행이나 프로젝트 파일 변경을 직접 수행하지 않습니다. 다만 저장된 상태를 조회하므로, 함수 전체가 데이터베이스와 무관한 순수 함수라고 부르는 것도 정확하지 않습니다.
// EventSink.ts의 commitCommandEffect() 내부 핵심 순서
// 예약·중복 처리·오류 처리를 생략한 발췌
const storedEvents = yield* eventStore.append({
commandId: input.commandId,
events: normalized,
});
const outcomes = yield* applyStoredEvents(storedEvents);
yield* effectOutbox.enqueue(input.effects);
// 이어서 결과 sequence를 담은 receipt를 upsert한다.
// 이 작업 전체를 commitThenPublish()의 withTransaction이 감싼다.
EventSink.ts의 commitCommandEffect()는 영수증 예약, 이벤트 추가, 조회용 상태 반영, outbox 등록, 최종 영수증 기록을 같은 트랜잭션에 넣습니다. commitThenPublish()는 커밋 후에 알림을 발행하며, 발행 순서를 위한 세마포어도 사용합니다. 화면에 보인 결과가 아직 저장되지 않은 상태가 되는 일을 줄이는 설계입니다.
| 끊기는 시점 | 이 구조에서 남는 것 | 학습할 질문 |
|---|---|---|
| 커밋 이전 | 트랜잭션이 성공하지 않았다면 변경은 수락되지 않음 | 재전송할 명령 ID를 유지하는가? |
| 커밋 후 응답 이전 | 영수증·이벤트·후속 작업이 함께 남음 | 같은 명령을 두 번 처리하지 않는가? |
| 외부 작업 실행 중 | outbox 상태와 실행 시도 정보가 남음 | 외부 동작을 재시도해도 안전한가? |
| 작업 완료 후 연결 단절 | 서버의 저장 상태와 이벤트가 남음 | 화면은 어느 순서 번호부터 복구하는가? |
EffectWorker.ts의 layerWithOptions()는 outbox.claimNext()로 작업을 확보하고, executor.execute()를 실행한 뒤 성공·재시도·실패를 기록합니다. 실행 직전 취소 상태를 다시 확인하고 실행과 취소 신호를 경쟁시키는 코드도 있습니다. runDaemonWithOptions()의 기본 작업자 동시성은 4입니다. 이는 에이전트 네 개만 실행할 수 있다는 제품 한도가 아니라 후속 작업 처리기의 기본 설정입니다.
서버가 요청을 수락했다는 응답은 의도가 저장됐다는 뜻이며, 에이전트 실행과 체크포인트까지 끝났다는 뜻은 아닙니다. 이 차이를 화면의 진행 상태와 테스트에서도 유지해야 합니다.
외부 작업이 성공한 직후 완료 표시 전에 프로세스가 종료되면, 저장소만 보고 성공 여부를 판단하기 어려울 수 있습니다. 명령 중복 방지와 외부 부작용의 중복 방지는 별개입니다. 공급자 프로세스를 잃은 작업의 복구 정책도 따로 필요합니다.
이 코드에서 shell은 문맥에 따라 뜻이 달라집니다. ShellStream.ts의 shell은 프로젝트·스레드 목록을 그릴 최소 요약입니다. 명령을 입력하는 터미널 셸과 구별해야 합니다. 전체 대화 본문은 별도 스레드 구독에서 다룹니다.
apps/server/src/ws.ts의 subscribeOrchestrationV2Shell()은 처음 접속하면 loadSnapshot()을 호출합니다. 재접속하면 먼저 latestApplicationSequence로 현재 상한을 읽고, 마지막 수신 번호부터 재생 가능한 범위인지 확인합니다. 가능하면 loadProjectMetadataSnapshot()으로 작은 메타데이터를 읽고, 해당 범위의 사건만 따라갑니다.
여기서 highWater는 “과거분을 어디까지 재생할지” 고정하는 경계입니다. 재생하는 동안 새 사건이 생겨도 과거분의 끝을 계속 옮기지 않고, 정해 둔 상한 뒤를 실시간 흐름으로 이어 붙입니다.
| 제한 | 설정값 | 실제 위치·의미 |
|---|---|---|
| 목록 재개 간격 | SHELL_RESUME_MAX_GAP = 1_000 | ws.ts: 재생 가능 범위 판단에 사용 |
| 목록 재생 원문 예산 | 8 MiB | ws.ts의 canReplayPersistedRange()가 원문 payload 크기도 검사 |
| 대화 재생 사건 수 | 128 | ThreadStream.ts: 초과하면 스냅샷 선택 |
| 대화 재생 데이터 | 인코딩 결과 1,048,576바이트 | decideThreadResume(): 사건 수와 별도로 제한 |
| 대화 재생 원문 예산 | 1,048,576바이트 | ThreadStream.ts의 isThreadReplayRawPayloadSafe(): 디코딩 전 비용 판단 |
| 실시간 보관 예산 | 1,000항목 / 직렬화 데이터 8 MiB | LiveStreamBudget.ts의 기본 제한 |
| 목록 변경 묶음 | 최대 512건 또는 50 ms | ws.ts의 Stream.groupedWithin() |
ShellStream.ts의 coalesceShellApplicationEvents()는 같은 프로젝트·스레드의 여러 사건을 최신 것 중심으로 묶습니다. bufferShellLiveStream()은 동일 요약 상태의 대기 중 갱신을 합칩니다. 반면 스냅샷·동기화 표식까지 같은 방식으로 버리면 순서 의미가 깨지므로 구분합니다.
커밋 제목도 이 학습 주제와 연결됩니다. d7a8d6f3의 제목은 fix(server): avoid full shell read on resume (#23987)입니다. 현재 소스에는 재개 경로가 전체 목록 로드를 우회하는 분기가 있으며, apps/server/src/ws.test.ts에는 알 수 없는 사건을 건너뛰고 동기화 뒤 실시간 사건을 받는 테스트가 있습니다. 성능 개선율을 수치로 주장할 근거는 이 코드만으로는 없습니다.
apps/server/src/checkpointing/Utils.ts의 checkpointRefForThreadTurn()은 refs/t3/checkpoints/…/turn/… 형태의 ref를 만듭니다. CheckpointStore.ts의 captureCheckpoint()는 현재 VCS 드라이버의 체크포인트 기능을 찾아 위임합니다.
실제 Git 구현은 apps/server/src/vcs/GitVcsDriver.ts의 GitVcsDriver.checkpoints.captureCheckpoint에 있습니다. 임시 인덱스를 만들고 GIT_INDEX_FILE로 지정한 뒤, write-tree·commit-tree·update-ref를 사용합니다. 체크포인트 커밋 객체는 만들지만 사용자의 브랜치를 앞으로 이동시키지 않는 방식입니다.
초보자에게 좋은 발견은 Git이 명령 모음인 동시에 파일 상태를 저장하는 객체 데이터베이스라는 점입니다. Git 공식 문서의 객체 설명을 함께 읽으면 트리와 커밋의 관계를 이해하기 쉽습니다. 이 저장소에서는 그 기능을 제품의 차이 보기와 되돌리기에 이용합니다. 단, 파일을 되돌리는 것과 에이전트가 기억하는 대화를 되돌리는 것은 함께 조정해야 합니다. 내부 아키텍처 문서는 공급자가 대화 롤백을 지원하지 않으면 파일 변경 전에 거부해야 한다고 설명합니다.
규약은 경계의 모양을, 서버 로그는 작업 이력을, 조회 상태는 화면용 결과를, Git 객체는 파일 상태를 맡습니다. 저장소가 여러 개인 것보다 각 저장소가 답하는 질문이 다르다는 점을 이해하는 것이 중요합니다.
| 읽기 층 | 대표 경로 | 확인할 질문 |
|---|---|---|
| 규약 | packages/contracts/src/orchestrationV2.ts | 스레드·실행·이벤트의 모양은 무엇인가? |
| 공통 동작 | packages/client-runtime/src/state/shellReducer.ts | 변경 사건이 어떤 화면 상태로 바뀌는가? |
| 서버 진입점 | apps/server/src/ws.ts | 요청이 어느 서비스로 연결되는가? |
| 결정과 저장 | apps/server/src/orchestration-v2/Orchestrator.ts·apps/server/src/orchestration-v2/EventSink.ts | 어떤 의도를 어떻게 원자적으로 남기는가? |
| 외부 실행 | packages/provider-*/src/server/ | 외부 프로토콜을 공통 사건으로 어떻게 옮기는가? |
폴더 이름이 비슷해도 역할은 다릅니다. packages/source-control-github 등은 호스팅 서비스와의 연동을 담당하고, apps/server/src/vcs는 작업 디렉터리의 버전 관리 연산을 담당합니다. GitHub API에서 PR을 읽는 일과 로컬 Git 인덱스를 다루는 일을 같은 책임으로 묶지 않습니다.
공급자 구현도 모두 packages/provider-*로 이동한 것은 아닙니다. Codex·Claude 관련 구현은 apps/server/src/provider에도 많이 남아 있습니다. 디렉토리의 대칭성을 가정하지 말고 실제 import와 서비스 연결을 따라가야 합니다.
집계 범위는 Git 추적 파일 중 apps·packages·infra·native·scripts·oxlint-plugin-t3code입니다. 경로 구성요소가 Vendor, vendor, deps, node_modules, dist, dist-electron, build인 파일은 제외했습니다. .repos의 참조 코드는 포함하지 않았습니다.
| 언어·확장자 | 파일 수 | 물리 줄 수 |
|---|---|---|
| TypeScript | 4,303개 (.ts·.mts) | 1,191,488줄 |
| TSX | 1,035개 | 256,589줄 |
| JavaScript | 53개 (.js·.mjs·.cjs) | 5,456줄 |
| Rust | 10개 | 4,237줄 |
| Swift / Kotlin | 21개 / 27개 | 7,010줄 / 7,120줄 |
| C・C++・헤더 | 41개 (.c·.cpp·.h) | 9,699줄 |
| CSS | 6개 | 5,974줄 |
| 기타 집계 확장자 | 23개 | 2,102줄 |
| 합계 | 5,519개 | 1,489,675줄 |
기타에는 .m·.mm·.py·.sh·.ps1·.qml·.zig를 포함했습니다. 공백·주석·테스트·생성 코드를 포함하는 물리 줄 수이며, 외부 유래 헤더까지 모두 제거한 순수 수작업 구현량은 아닙니다. JSON·YAML·Markdown·바이너리는 합계에서 제외했습니다.
이 범위의 TypeScript·TSX 테스트 파일은 .test.·.spec. 이름 기준 1,485개, 466,623줄입니다. TypeScript 계열 약 145만 줄의 약 32%지만, 테스트 비중만으로 검증 품질을 단정하지는 않습니다. 규모가 큰 이유 중 하나는 packages/effect-codex-app-server처럼 상세한 프로토콜 스키마가 코드로 들어 있기 때문입니다.
ThreadStream.ts의 재개 판단 → shellReducer.ts의 상태 갱신 → rpc.ts의 구독 계약 → ws.ts의 실제 연결 순서로 읽어 보세요. 작은 규칙을 알고 큰 파일에 들어가면 조건문의 목적이 보입니다.
ProviderAdapterV2Event는 type으로 사건 종류를 구분하는 유니온입니다. 메시지 갱신과 명령 출력 조각은 필요한 필드가 다릅니다. 모든 필드를 선택 사항으로 만드는 대신 종류별 모양을 나누면, 분기 안에서 필요한 값이 존재한다는 것을 타입으로 설명할 수 있습니다.
// 학습용 축소 모형. 실제 계약의 필드와는 다르다.
type Update =
| { type: "message.updated"; text: string }
| { type: "turn.terminal"; status: "completed" | "failed" };
function summarize(update: Update): string {
switch (update.type) {
case "message.updated":
return update.text;
case "turn.terminal":
return update.status;
}
}
실습 아이디어: 새 사건 종류를 추가하고 처리하지 않은 분기를 찾아보세요. 이어서 실제 packages/provider-core/src/server/ProviderAdapter.ts에서 command_output.delta와 turn.terminal의 필드를 비교합니다. 타입만 맞는 데이터와 실행 중 검증을 통과한 데이터의 차이도 설명해 봅니다.
Effect 코드의 낯선 기호부터 줄여 봅시다. Effect.Effect<A, E, R>에서 A는 성공값, E는 예상 가능한 실패, R은 필요한 서비스입니다. yield*는 이 작업을 조합하는 문맥에서 값을 받는 표현이며, Layer는 서비스와 그 의존성을 구성합니다. 세 타입 매개변수의 의미는 Effect 4 공식 타입 문서에서도 확인할 수 있습니다.
apps/server/src/checkpointing/CheckpointStore.ts의 make는 VcsDriverRegistry를 받아 체크포인트 서비스를 만듭니다. 마지막의 Layer.effect(CheckpointStore, make)는 “이 서비스는 이 구성 작업으로 만든다”는 연결입니다. 단순 유틸리티 함수와 외부 자원을 사용하는 서비스를 구분해서 읽어 보세요.
실습 아이디어: 체크포인트 서비스 선언의 각 메서드에서 성공값과 실패 타입을 표로 옮깁니다. Effect.Effect<boolean, CheckpointStoreError>에서 false와 오류가 서로 다른 상태인 이유를 설명해 보세요. 없는 ref를 찾는 일과 Git 실행 자체가 실패한 일은 같지 않습니다.
packages/client-runtime/src/state/shellReducer.ts의 reuseUnchangedThreadShells()는 이전 스레드를 ID로 찾고, 의미상 같으면 기존 객체를 재사용합니다. applyShellStreamEvent()는 같은 요약이 와도 수신 순서 번호를 갱신할 수 있습니다. 화면에 보이는 값의 변화와 동기화 진행을 분리한 사례입니다.
mergeShellSnapshotProjects()는 프로젝트 메타데이터 보강 응답과 전체 상태를 확정하는 응답도 구분합니다. 작은 보강 응답의 빈 스레드 배열을 전체 상태로 오해해 목록을 지우면 안 됩니다. “응답 모양이 비슷하다”는 이유만으로 같은 병합 규칙을 적용하지 않는 것이 핵심입니다.
실습 아이디어: 이름이 같은 스레드 객체가 다시 왔을 때 객체 참조를 유지하는 경우와 새로 만드는 경우를 그림으로 비교합니다. 이후 렌더링 횟수를 측정할 수 있는 작은 별도 예제로 옮겨 보세요. 객체를 재사용한다는 사실만으로 모든 React 렌더링이 사라진다고 단정하지는 않습니다.
apps/server/src/persistence/Sqlite.ts는 journal_mode = WAL, synchronous = NORMAL, busy_timeout = 5000, foreign_keys = ON을 설정합니다. WAL은 변경 기록을 별도 로그에 쌓는 방식이고, busy timeout은 잠긴 데이터베이스를 만났을 때 기다리는 시간을 지정합니다.
같은 파일은 WAL 재사용 시 남길 파일 크기의 기준을 32 * 1024 * 1024바이트로 지정합니다. 설정 이름은 journal_size_limit이며, 실행 중 WAL이 절대 넘지 못하는 상한은 아닙니다. 체크포인트 디스크 동기화는 별도 워커에 맡기도록 설정합니다. 여기서 SQLite 체크포인트는 앞 절의 Git 체크포인트와 다른 개념입니다. SQLite에서는 WAL 내용을 주 데이터베이스에 반영하는 작업을 뜻합니다.
소스 주석은 매 커밋의 동기화가 이벤트 루프를 막는 문제를 설명합니다. 선택한 설정에서는 운영체제 중단이나 전원 손실 때 마지막 체크포인트 이후 커밋이 되돌아갈 수 있습니다. 응답성과 내구성을 어떤 장애 모델에서 맞바꾸는지 읽어야 합니다. SQLite WAL 공식 문서도 NORMAL 설정에서의 동기화와 전원 손실 절충을 설명합니다.
실습 아이디어: “앱 종료”, “운영체제 중단”, “네트워크 단절”을 서로 다른 열로 놓고 어떤 데이터가 이미 디스크에 있는지 표시합니다. 실제 데이터베이스를 열거나 변경하지 않아도 트랜잭션과 동기화의 차이를 연습할 수 있습니다.
LiveStreamBudget.ts는 보관 중인 항목 수와 직렬화 바이트를 따로 셉니다. 큰 사건 몇 개와 작은 사건 수천 개는 서로 다른 부담이기 때문입니다. ShellStream.ts의 composeShellStreamWithEnrichment()는 초기 응답이 메타데이터 보강 응답보다 먼저 나가도록 흐름을 구성합니다.
실습 아이디어: 서버가 100개의 갱신을 만들고 화면이 10개만 읽는 상황을 그려 보세요. 같은 대상의 전체 상태 갱신은 최신 것으로 합칠 수 있지만, 모든 종류의 사건을 버려도 되는 것은 아닙니다. 어떤 사건에 합치기를 허용할지 먼저 분류합니다.
packages/shared/src/DrainableWorker.ts의 makeDrainableWorker()는 대기열뿐 아니라 처리 중인 작업을 포함한 outstanding을 관리합니다. drain은 이 값이 0이 되는 것을 기다립니다. 대기열에서 꺼냈지만 아직 끝나지 않은 작업을 놓치지 않는 설계입니다.
apps/server/src/ws.test.ts는 재연결 중 알 수 없는 미래 사건이 있어도 이후 정상 사건과 실시간 꼬리를 받는 경우를 다룹니다. 이런 테스트는 성공 화면만 확인하는 것보다 프로토콜 호환성의 경계를 잘 드러냅니다.
비동기 테스트의 완료 기준은 몇 초가 지났는지가 아니라, 필요한 사건과 작업이 실제로 끝났는지여야 합니다.
실습 아이디어: “대기열이 비었다”와 “작업이 완료됐다”가 다른 상황을 두 단계 도식으로 작성합니다. 이후 별도 연습 예제에서는 Deferred 신호나 drain을 완료 조건으로 사용해 봅니다.
2026-10-31 기능 개정: 고정 커밋 79f1fadfc602과 v0.1.6 릴리스에서 사이드 채팅, 프로젝트 고정·보관, 1Password 자격증명 연결을 확인했다. 사이드 채팅은 lineage.sideChat와 parentThreadId로 부모 스레드를 검증한 뒤 별도 탭을 연다. 도구 diff와 셸 실행은 부모의 diff 패널·터미널로 연결하므로 대화 분리와 작업 환경 분리는 구별해야 한다.
1Password 드라이버는 op CLI의 read와 inject를 호출하며 읽기를 45초로 제한한다. 여러 참조는 한 번에 읽고, 제한시간이 끝나면 참조별 재호출로 대기를 늘리지 않는다. 프로젝트 고정·보관 시각은 SQLite projection의 pinned_at·archived_at 열에 남긴다. 실습에서는 보관과 파일 삭제가 어떻게 구분되는지 확인하고, 1Password는 별도 CLI 설치·인증을 먼저 준비한다.
코드 근거: 사이드 채팅 패널 연결 · 1Password 읽기 드라이버 · 프로젝트 상태 마이그레이션 · v0.1.6 릴리스.
2026-10-31 추가 소스 검토: 고정 커밋 d2d1e4f에서 데스크톱 원격 노출 상태는 현재 네트워크 인터페이스를 다시 읽어 광고 주소를 계산합니다. Wi-Fi·DHCP 주소가 바뀌어도 오래된 접속 주소를 계속 보여 주지 않도록 한 변화입니다. 네트워크 접근 모드의 바인딩과 사용자에게 안내하는 endpoint를 구분해서 읽습니다.
Clerk 로그인 저장소는 전용 디렉터리 0700·토큰 파일 0600 권한을 적용하고, 옛 파일을 이관하며, 심볼릭 링크·일반 파일 여부를 검사합니다. 읽기·쓰기·삭제 작업 뒤에도 파일 권한을 다시 좁힙니다. 이는 파일 접근 권한 관리이며 암호화를 보장한다는 뜻은 아닙니다. Linux 테마는 XDG portal의 변경 신호를 따라가고 portal이 없으면 Chromium의 선택을 유지합니다.
2026-10-30 소스 대조: 고정 커밋 32ecd71의 서버 package.json은 0.1.5입니다. 아래 Node 허용 범위와 루트 Node·pnpm 조건은 여전히 일치합니다. 초판의 0.1.2·스타·포크는 당시 기록으로 남깁니다. 현재 README는 Codex·Claude·Cursor·Grok Build·OpenCode·Antigravity 경로를 안내하며 스레드를 시작하기 전에 최소 한 provider의 설치·인증이 필요합니다.
최신 내부 문서는 설정 소유권을 더 명확히 구분합니다. 클라이언트 선호는 해당 클라이언트에, 환경 기본값과 프로젝트 덮어쓰기는 작업 서버에 있습니다. 사용할 수 없는 설정 대상에서 다른 환경으로 자동 대체하지 않으며, All environments는 현재 연결되고 로드된 서버에 대한 명시적 일괄 수정입니다. 오프라인·미래 환경까지 동기화하는 전역 기본값으로 해석하면 안 됩니다. 원격 단말이 자기 파일·인증 정보를 서버 상태 대신 쓰지 않는다는 기존 실행 경계도 유지됩니다.
| 대상 | 확인되는 조건 | 근거 |
|---|---|---|
| 에이전트 호스트 | 사용할 공급자 설치 또는 관리형 런타임 연결과 인증 필요 | docs/user/install.md |
| 서버 실행 | 패키지 Node 범위 ^22.16 || ^23.11 || >=24.10 | apps/server/package.json |
| 개발 도구 | 루트 Node 26.12.1, pnpm 12.14.0, Vite+ 1.3.0 | 루트 manifest와 catalog |
| 모바일 빌드 설정 | iOS deploymentTarget: 18.0, Android minSdkVersion: 24 | apps/mobile/app.config.ts. 배포 스토어의 요구사항과는 별도 |
| Intel Mac 서버 | 설치 문서는 독립 t3 실행 파일이 없으며 소스 빌드를 안내 | docs/user/install.md. 데스크톱 앱은 별도로 안내 |
| 원격 화면 | 에이전트 호스트가 켜져 있고 접속 가능해야 함 | docs/user/remote-access.md |
배포 실행 파일이나 Electron 앱을 쓰는 경우와, Node로 소스를 직접 실행하는 경우의 준비물이 다릅니다. 또한 공급자 자체의 요구사항이 추가될 수 있습니다. Node 허용 범위를 만족한다는 것만으로 공급자·네이티브 모듈까지 검증됐다는 뜻은 아닙니다.
아래는 이 구조를 공부하기 위한 보수적인 출발점입니다. 저장소에서 보장하는 최소 RAM·CPU·디스크 수치가 아니며, 에이전트 수·프로젝트 크기·브라우저 테스트·시뮬레이터에 따라 달라집니다.
| 학습 범위 | 권장 출발점 | 비용이 늘어나는 지점 |
|---|---|---|
| 코드 읽기 | RAM 8 GB급 일반 개발용 컴퓨터와 편집기 | 참조 코드까지 포함한 전체 검색·대형 파일 인덱싱 |
| 서버·웹 개발 | CPU 4코어, RAM 16 GB, SSD 여유 15~20 GB 정도 | 의존성 캐시, 여러 에이전트, 브라우저·빌드 동시 실행 |
| 모바일 개발 | RAM 24~32 GB 정도면 시뮬레이터 병행에 여유 | 플랫폼 SDK·네이티브 빌드·시뮬레이터 저장 공간 |
| 원격 관찰 | 호환되는 브라우저 또는 모바일 앱 | 실제 코드 작업 부하는 접속 대상 호스트에 발생 |
필요한 자원은 화면의 크기보다, 호스트에서 동시에 실행하는 에이전트와 개발 작업의 양에 더 크게 좌우됩니다. T3 Code 자체를 사용하는 것과 로컬 LLM을 추가로 실행하는 것은 별도 문제입니다. 로컬 모델을 선택하면 그 모델의 메모리·GPU 조건이 추가됩니다.
apps/server/src/config.ts의 deriveServerPaths()는 statev2.sqlite, 첨부 파일, 브라우저 산출물, 로그, worktree 경로를 나눕니다. 기본 포트 상수는 3773입니다. 데이터베이스 파일 크기만으로 전체 디스크 사용량을 계산하면 worktree와 산출물 증가를 놓칠 수 있습니다.
127.0.0.1은 휴대폰 자신을 가리킵니다. 접속 가능한 호스트 주소와 인증 절차가 필요합니다.휴대폰은 공방의 CCTV와 주문 단말에 가깝습니다. 휴대폰을 새것으로 바꾸어도 공방의 톱이 빨라지지는 않습니다. 반대로 화면이 가벼워도 공방 컴퓨터가 잠들거나 네트워크가 끊기면 일을 제어할 수 없습니다.
처음 두 과제는 소스를 읽고 표를 만드는 것만으로 끝낼 수 있습니다. 코드를 작성하는 과제는 원본 제품과 분리된 연습용 프로젝트에서 진행합니다. 기존 계정·실제 프로젝트·운영 데이터베이스 없이도 핵심 규칙을 연습하도록 범위를 작게 잡았습니다.
목표: subscribeShell이 선언되고 처리되는 곳을 연결합니다. 저장소 루트에서 다음 읽기 명령으로 시작합니다.
rg -n 'WsOrchestrationV2SubscribeShellRpc' packages/contracts/src/rpc.ts
rg -n 'subscribeOrchestrationV2Shell' apps/server/src/ws.ts
rg -n 'RPC_REQUIRED_SCOPES' apps/server/src/auth/RpcAuthorization.ts
rg -n 'applyShellStreamEvent' packages/client-runtime/src/state/shellReducer.ts
완료 기준: “클라이언트 → 계약 → 권한 검사 → 구독 서비스 → 상태 병합”의 다섯 칸에 실제 식별자와 파일 경로가 들어 있어야 합니다. 타입 선언만 보고 구현 동작까지 추정한 부분은 따로 표시합니다.
목표: apps/server/src/orchestration-v2/ThreadStream.ts의 decideThreadResume()을 읽고 아래 표의 판정 이유를 설명합니다. 사건 수와 바이트 수 중 하나만 초과해도 어떤 선택을 하는지 확인합니다.
| 조건 | 입력 | 예상 선택 |
|---|---|---|
| 정확한 상한 | 128건, 1,048,576바이트, 정상 커서 | 재생 |
| 사건 수 초과 | 129건, 100바이트, 정상 커서 | 스냅샷 |
| 바이트 초과 | 1건, 1,048,577바이트, 정상 커서 | 스냅샷 |
| 커서가 미래 | afterSequence = 126, highWater = 125 | 스냅샷 |
추가 질문: 입력이 정확히 상한과 같을 때와 1만큼 넘을 때를 다르게 처리하는 연산자는 무엇인가요? 목록 재개의 1,000 간격과 대화 재개의 128건을 혼동하지 않고 설명해 보세요.
완료 기준: 네 경우의 결과와 이유를 한 문장씩 쓰고, “바이트는 작지만 사건이 많은 상황”이 왜 부담인지 적습니다.
목표: shellReducer.ts를 참고해 아래 학습 모형의 reduce()를 구현합니다. 실제 계약의 축약판이며 제품의 함수와 그대로 호환되지는 않습니다.
type Thread = { id: string; title: string };
type State = { sequence: number; threads: ReadonlyArray<Thread> };
type Item =
| { kind: "thread.updated"; sequence: number; thread: Thread }
| { kind: "thread.removed"; sequence: number; threadId: string };
// reduce(state, item): State를 구현한다.
// 제목이 같으면 기존 Thread 객체를 재사용한다.
// 화면 내용이 같아도 수신 sequence는 진행할 수 있다.
확인할 사례: 새 항목 추가, 기존 제목 변경, 같은 항목 재수신, 삭제를 차례로 적용합니다. 메타데이터 보강만 왔을 때 목록을 유지하는 상황도 별도 입력으로 설계해 보세요.
완료 기준: 이전 상태를 직접 수정하지 않고, 같은 값의 재수신 때 스레드 객체 참조를 유지하며, 최종 순서 번호가 진행해야 합니다. 오래된 사건·중복 사건을 처리할 책임을 통신 계층과 reducer 중 어디에 둘지도 기록합니다. 객체를 직접 수정하지 않는 이유는 React 공식 상태 갱신 문서와 비교해 보세요.
목표: EventSink.ts의 commitCommandEffect()와 EffectWorker.ts를 읽고, 작은 메모리 모형으로 이벤트·영수증·작업 목록을 분리합니다. 실제 공급자를 호출할 필요는 없습니다.
commandId를 두 번 보내도 이벤트와 실행 의도가 한 번만 추가되게 설계합니다.완료 기준: 명령 중복 방지와 외부 실행 중복 방지를 구별해야 합니다. 단순 메모리 예제에는 실제 트랜잭션·프로세스 복구가 없다는 한계도 설명합니다. 작업 처리 결과를 다시 명령으로 넣는 순환을 그리면 더 좋습니다.
목표: ProviderAdapterV2Event, LiveStreamBudget.ts, ThreadStream.ts를 참고해 외부 서비스가 없는 연습 프로그램을 만듭니다. 전체 제품 어댑터를 구현하기 전에 메시지 갱신과 실행 종료 두 사건만 흉내 냅니다.
완료 기준: 연결이 끊겨도 최종 화면 상태가 수렴하고, 메모리 보관량의 상한을 설명할 수 있어야 합니다. 테스트는 고정 대기 시간보다 사건 전달·완료 신호를 기준으로 끝나게 설계합니다.
가장 좋은 첫 기여 연습은 기능을 크게 늘리는 것이 아니라, 작은 규칙 하나의 정상·중복·실패 경계를 설명하는 것입니다. 실제 기여를 계획한다면 README와 CONTRIBUTING.md의 현재 수용 범위를 먼저 확인하세요.
주당 4~6시간을 가정한 학습안입니다. TypeScript 기본 문법이 낯설면 1주차를 두 주로 늘려도 좋습니다. 매주 읽는 코드와 남길 산출물을 연결하면 큰 저장소에서도 진도를 확인할 수 있습니다.
| 주차 | 배울 내용 | 읽을 코드 | 남길 산출물 |
|---|---|---|---|
| 1주차 | 모듈, 유니온, 제네릭, 런타임 검증 | packages/contracts/src/baseSchemas.ts, packages/provider-core/src/server/ProviderAdapter.ts | 공급자 사건 두 종류의 필드 비교표 |
| 2주차 | React 상태, 불변 갱신, 객체 참조 | packages/client-runtime/src/state/shellReducer.ts | 최소 reducer와 네 가지 입력 결과 |
| 3주차 | Effect 서비스, 오류, Scope, 스트림 | apps/server/src/checkpointing/CheckpointStore.ts, apps/server/src/orchestration-v2/ShellStream.ts | 서비스 의존성 그림과 자원 정리 시점 |
| 4주차 | 이벤트 소싱, 조회 모델, 트랜잭션, outbox | apps/server/src/orchestration-v2/EventSink.ts, apps/server/src/orchestration-v2/EffectWorker.ts | 장애 시점별 데이터 보존 표 |
| 5주차 | WebSocket 구독, 커서, 데이터 예산, 역압 | apps/server/src/ws.ts, apps/server/src/orchestration-v2/ThreadStream.ts | 최초 연결과 재연결 시퀀스 도식 |
| 6주차 | Git 객체·인덱스·ref, 권한, 통합 테스트 | apps/server/src/vcs/GitVcsDriver.ts, apps/server/src/auth/RpcAuthorization.ts | 가짜 공급자 설계와 체크포인트 연결도 |
apps/mobile에서 화면과 플랫폼 기능을 읽고, 공용 런타임이 어느 부분까지 맡는지 추적합니다.infra/relay의 계정 연결·자격증명·알림과 환경 서버의 작업 실행을 서로 다른 시퀀스로 그립니다.“무엇을 입력받는가?”, “결과의 근거는 어디에 저장되는가?”, “중간에 끊기면 무엇을 다시 할 수 있는가?”에 답해 보세요. 답에 파일 경로와 함수가 붙으면 개념을 실제 코드와 연결한 것입니다.
이 저장소를 공부한 결과물은 라이브러리 목록이 아니라, 요청의 성공과 실패를 끝까지 설명하는 자기만의 시스템 그림이어야 합니다.
| 용어 | 쉬운 설명 |
|---|---|
| Harness | 에이전트가 도구를 쓰고 승인을 받으며 작업하도록 관리하는 실행 틀. |
| Provider | 연결할 에이전트 실행 체계. 모델 이름 자체보다 넓은 개념이다. |
| Environment | 서버 하나와 그 컴퓨터의 파일·인증·실행 상태를 묶은 작업 환경. |
| Project | 한 환경 안의 실제 디렉터리를 루트로 삼는 프로젝트 기록. |
| Thread | 프로젝트에서 계속 이어지는 대화와 작업 이력의 단위. |
| Turn / Run | Turn은 사용자와 에이전트 사이의 한 작업 주기이며 V2 run으로 관리한다. 공급자 실행 종료와 체크포인트·차이 보기 처리 완료는 별개다. |
| Adapter | 공급자 고유의 입력·출력을 앱의 공통 규약으로 번역하는 연결 부품. |
| Capability | 특정 서버나 공급자가 실제로 지원한다고 알리는 기능 정보. |
| RPC | 다른 프로세스·컴퓨터의 기능을 이름과 입력 데이터로 호출하는 방식. |
| WebSocket | 연결을 유지하며 양방향 메시지를 주고받는 통신 방식. |
| Schema | 데이터가 어떤 모양이어야 하는지 표현하고 검증하는 규칙. |
| Command / Event | “해 달라”는 요청과 “이 일이 일어났다”는 기록의 차이. |
| Event sourcing | 사건 이력을 상태의 근거로 보관하는 설계. |
| Projection | 사건에서 만들어 둔 조회용 상태. 화면이 매번 전체 이력을 계산하지 않게 한다. |
| CQRS | 변경을 결정하는 경로와 읽기 좋은 결과를 조회하는 경로를 나누는 설계. |
| Receipt | 명령이 이미 처리됐는지와 결과 순서 번호를 확인하는 영수증. |
| Idempotency | 같은 요청을 반복해도 중복 변경을 만들지 않도록 하는 성질. |
| Outbox | 커밋 뒤 해야 할 외부 작업을 의도와 함께 저장하는 작업 목록. |
| Lease | 작업자가 일정 조건 아래 해당 작업을 맡았음을 나타내는 임시 소유권. |
| Snapshot / Delta | 기준 시점의 상태 묶음과, 그 상태 이후에 바뀐 부분. |
| Sequence / Cursor | 사건의 순서 번호와 클라이언트가 어디까지 받았는지 기억하는 위치. |
| High-water mark | 재생 시작 때 정해 두는 과거 사건의 상한선. |
| Shell | 이 글의 목록 스트림에서는 프로젝트·대화의 최소 요약. 터미널 문맥의 셸과는 다르다. |
| Coalescing | 같은 대상의 대기 중 갱신을 합쳐 전달량을 줄이는 것. |
| Backpressure | 받는 쪽의 처리 속도에 맞춰 생산·전달·보관량을 제어하는 개념. |
| Effect / Layer | 결과·실패·필요 서비스를 표현하는 작업과, 그 서비스를 구성하는 조립 단위. |
| Scope / Deferred | 자원을 정리할 수명 범위와, 나중에 확정될 완료 신호를 기다리는 도구. |
| WAL | SQLite가 변경을 별도 로그에 먼저 쌓는 방식. Git 체크포인트와 별개다. |
| Git ref / Index | Git 객체를 가리키는 이름과, 다음 파일 상태를 구성하는 스테이징 자료구조. |
| Checkpoint | 작업 전후를 비교·복원하기 위한 파일 상태 기록. 이 구현은 숨은 Git ref를 활용한다. |
| Worktree | 같은 Git 저장소의 객체를 공유하면서 별도의 작업 디렉터리를 두는 기능. |
| Control plane | 실제 작업 실행과 구분되는 연결·설정·자격증명 관리 계층. |
| Monorepo | 여러 앱과 공통 패키지를 한 저장소에서 함께 관리하는 형태. |
| 본문 주제 | 커밋 d7a8d6f3의 근거 |
|---|---|
| 버전·워크스페이스 | 루트 package.json · pnpm-workspace.yaml · 잠금 파일 |
| 앱별 의존성 | 서버 · 웹 · 데스크톱 · 모바일 |
| RPC 계약·권한 | rpc.ts · RPC_REQUIRED_SCOPES |
| 공급자 추상화 | ProviderAdapterV2와 세션 규약 · Pi 어댑터 구현 |
| 명령 결정·중복 방지 | dispatchWithReceipt()와 dispatchWithReceiptEffect() |
| 원자적 저장 | commitCommandEffect()와 commitThenPublish() |
| 후속 작업 실행 | layerWithOptions()와 runDaemonWithOptions() |
| 재연결·예산 | subscribeOrchestrationV2Shell() · decideThreadResume() · 실시간 보관 예산 |
| 목록 상태 병합 | reuseUnchangedThreadShells()와 applyShellStreamEvent() |
| Git 체크포인트 | ref 이름 규칙 · captureCheckpoint 구현 |
| SQLite | WAL·동기화 설정 · Node 내장 SQLite와 워커 |
| 테스트·CI | 재연결 테스트 · 완료 대기 구조 · CI 설정 |
| 모바일·네이티브 | 모바일 플랫폼 빌드 조건 · 자원 모니터 Rust 의존성 |
| 원격 제어 평면 | relay 책임 설명 · Cloudflare Worker 구성 |
| 항목 | 확인 내용 |
|---|---|
| 저장소 | pingdotgg/t3code |
| 트렌딩 | GitHub Trending Monthly #16 (2026-10-23 분석 기준) |
| 스타 / 포크 | 29,528 / 7,919 (2026-10-23 GitHub 기준) |
| 주 언어 | TypeScript |
| 생성일 | 2026-02-08 |
| 최신 릴리스 | v0.1.2 · 2026-10-22 |
| 최근 push | 2026-10-23 |
| 최근 커밋 | d7a8d6f3 · 2026-10-23fix(server): avoid full shell read on resume (#23987) |
| 코드 규모 | 정의한 집계 범위에서 파일 5,519개 · 1,489,675줄. TypeScript·TSX 합계 1,448,077줄. 공백·주석·테스트·생성 코드 포함 |
| 릴리스와 개발 버전 | 서버·웹·데스크톱 manifest는 0.1.2, 모바일 manifest는 0.0.0. 패키지별 version을 제품 전체의 동일 버전으로 해석하지 않음 |
| 라이선스 | MIT · Copyright 2026 T3 Tools Inc. |
이 글의 파일 경로와 설정값은 커밋 d7a8d6f3 기준입니다. 이후의 코드·릴리스·통신 규약은 달라질 수 있습니다. 과거 전체의 커밋 수나 증가 속도는 단일 시점의 코드만으로 알 수 없습니다.
재시도·인증·체크포인트 구조가 있다는 사실은 실제 성능, 모든 장애에서의 데이터 보존, 모든 공급자의 기능 동등성을 보증하지 않습니다. RAM·디스크 권장치는 학습용 추정이며 공식 최소 사양이 아닙니다.
README는 프로젝트 초기 단계의 버그 가능성과 기여 범위 제한을 알립니다. MIT는 T3 Code 소스의 이용 조건이며 외부 공급자의 구독·요금·이용 조건까지 대신하지 않습니다. 실제 작업에서는 사용하는 환경과 공급자의 현재 문서를 함께 확인해야 합니다.