핵심 요약
- 터미널 agent의 화면은 단순 renderer가 아니라 runtime state container에 가깝다.
- 화면에 보이는 메시지, 모델에게 보내는 메시지, transcript에 저장하는 메시지는 다르다.
- 승인 UI, 입력 큐, partial stream, overlay 상태를 한곳에서 조율하되 정책 판단은 분리해야 한다.
- AI 도구를 쓰는 개발자는 화면이 멈춘 것처럼 보일 때 어떤 상태가 대기 중인지 이해할 수 있어야 한다.
이번 글에서는 Claude Code류의 터미널 화면을 UI가 아니라 runtime shell로 보겠습니다.
겉으로는 입력창과 메시지 목록이 전부처럼 보입니다. 하지만 제품 수준 agent의 화면은 진행 중인 모델 스트림, tool approval, 입력 큐, local command overlay, 비용 알림, 취소 상태, 세션 복구 정보를 동시에 다룹니다.
이 지점에서 많은 agent 데모가 제품으로 넘어가지 못합니다. 화면에 보이는 상태와 모델에게 전달되는 상태를 같은 리스트로 관리하기 때문입니다.
1. 왜 화면이 runtime shell인가
터미널 agent에서 화면은 단순 출력창이 아닙니다. 사용자는 같은 화면에서 질문을 입력하고, 모델의 partial response를 보고, 도구 실행 승인을 결정하고, 취소를 누르고, 다시 새로운 입력을 넣습니다.
즉 화면은 여러 흐름이 만나는 표면입니다.
| 상태 | 화면에는 필요한가 | 모델에는 필요한가 | 기록에는 필요한가 |
|---|---|---|---|
| 사용자 입력 | 예 | 예 | 예 |
| assistant partial text | 예 | 최종 조립 후 필요 | 예, 보통 요약 또는 최종값 |
| 승인 dialog 문구 | 예 | 보통 아니오 | 예, 승인 결과 중심 |
| local command overlay | 예 | 경우에 따라 아니오 | 예 |
| 비용 알림 | 예 | 아니오 | 예 |
| tool result | 예, 요약 형태 | 예, 구조화 형태 | 예 |
화면의 모든 텍스트를 모델에게 보내면 context가 오염됩니다. 반대로 모델에게 필요한 tool result를 화면용 문자열로만 저장하면 다음 추론이 이어지지 않습니다.
2. 세 가지 메시지 표현
제품형 agent에서는 같은 사건을 세 가지 방식으로 투영해야 합니다.
- UI projection: 사람이 읽기 좋은 메시지, 접힘/펼침, 진행 표시.
- Model projection: role, content block, tool result, system context.
- Ledger projection: timestamp, turn id, cost, approval, restore metadata.
이 구분을 처음부터 해두면 이후 기능이 쉬워집니다. 예를 들어 tool result는 화면에는 “파일 3개 읽음”으로 보일 수 있지만, 모델에게는 각 파일의 요약 또는 관찰값이 구조화되어 들어가야 합니다. transcript에는 언제 읽었고 어떤 권한으로 읽었는지가 남아야 합니다.
3. Session shell의 상태 구조
session shell은 다음 상태를 가질 수 있습니다.
| 상태 그룹 | 예시 |
|---|---|
| conversation state | 사용자 메시지, assistant 메시지, tool result |
| input state | 현재 입력값, paste buffer, attachment, editor selection |
| execution state | active turn, abort signal, pending input queue |
| approval state | tool permission prompt, sandbox confirmation |
| overlay state | 설정 화면, help, cost notice, local command result |
| integration state | remote prompt, bridge event, session restore data |
중요한 것은 shell이 모든 정책을 결정하지 않는다는 점입니다. shell은 상태를 보관하고 사용자 interaction을 받지만, 권한 판단이나 도구 실행 여부는 별도 계층이 결정해야 합니다.
실무 팁
화면 계층은 “무엇을 보여줄지”를 담당하고, 정책 계층은 “무엇을 허용할지”를 담당하게 만드세요. 이 둘이 섞이면 headless batch 모드나 remote session을 만들 때 다시 뜯어고치게 됩니다.
4. 승인 UI와 정책 엔진 분리
agent가 파일을 쓰거나 shell 명령을 실행하려고 할 때 화면에는 승인 dialog가 떠야 합니다. 하지만 dialog 자체가 “이 작업이 위험한가”를 판단하면 안 됩니다.
정책 엔진이 먼저 판단합니다.
# 읽는 법: 아래 항목은 동작 흐름을 빠르게 확인하기 위한 요약 예시입니다.도구 요청→ policy engine: 허용 / 거부 / 질문 필요→ session shell: 질문 필요 상태를 UI로 표시→ 사용자 선택→ policy decision 기록→ tool runtime 실행 또는 거절 결과 생성
이 구조는 테스트에도 좋습니다. UI 없이도 정책 엔진을 테스트할 수 있고, interactive shell이 아닌 batch 모드에서는 “질문 필요” 상태를 자동 거절하거나 사전 정책으로 처리할 수 있습니다.
5. 개념 코드로 보는 shell 설계
아래 코드는 새로 작성한 개념 코드입니다.
# 읽는 법: 실제 구현 복제가 아니라 runtime 경계를 설명하는 개념 코드입니다.class SessionShell: # 객체가 이후 단계에서 참조할 runtime 의존성과 상태 저장소를 초기화합니다. def __init__(self): self.visible_timeline = [] self.model_history = [] self.ledger_buffer = [] self.input_buffer = "" self.pending_inputs = [] self.approval_stack = [] self.current_overlay = None self.active_turn_id = None # 하나의 runtime event를 화면용, 모델용, 기록용 표현으로 각각 라우팅합니다. def publish(self, event): if event.show_to_user: self.visible_timeline.append(render_for_screen(event)) if event.send_to_model: self.model_history.append(render_for_model(event)) if event.recordable: self.ledger_buffer.append(render_for_ledger(event)) # 도구 실행 전 사용자 승인이 필요한 요청을 overlay stack에 올립니다. def open_approval(self, approval_request): self.approval_stack.append(approval_request) self.current_overlay = "approval" # 승인 결과를 event로 기록하고 다음 overlay 상태를 복구합니다. def close_approval(self, decision): request = self.approval_stack.pop() self.publish(make_approval_event(request, decision)) self.current_overlay = "approval" if self.approval_stack else None
핵심은 publish()입니다. runtime event 하나가 UI, model, ledger에 서로 다른 방식으로 반영됩니다. 이 구조가 없으면 나중에 “사용자에게는 보여야 하지만 모델에게는 보여주면 안 되는 것”을 처리하기 어렵습니다.
6. AI 활용 개발자가 읽어야 할 신호
AI 도구를 사용하는 개발자는 화면을 보면서 runtime 상태를 추론할 수 있어야 합니다.
| 화면 신호 | 가능한 runtime 상태 |
|---|---|
| 답변이 stream 중인데 새 입력이 대기 표시됨 | active turn이 있고 input queue에 들어감 |
| 승인창이 떠 있고 답변이 멈춤 | tool runtime이 permission decision을 기다림 |
| local command 결과만 나오고 모델 답변이 없음 | 입력이 local-only action으로 처리됨 |
| 비용 또는 토큰 경고가 표시됨 | ledger/accounting이 budget threshold에 접근함 |
| 취소 후 “결과 없음”이 아니라 실패 결과가 표시됨 | tool request/result pair를 맞추기 위한 중단 result 생성 |
이런 신호가 명확한 도구일수록 실무에서 신뢰하기 쉽습니다.
7. Agent 개발자가 피해야 할 실수
첫 번째 실수는 UI message list를 model history로 그대로 쓰는 것입니다. 이러면 비용 알림, help 문구, 승인 dialog 설명까지 모델 context에 섞일 수 있습니다.
두 번째 실수는 partial stream을 final message처럼 저장하는 것입니다. streaming text는 중간 상태입니다. 최종 응답이 닫힌 뒤 조립된 assistant message와 구분해야 합니다.
세 번째 실수는 overlay가 queue processing을 영구적으로 막게 만드는 것입니다. 설정 화면이 열려 있어도 background event나 취소 신호는 처리되어야 합니다.
8. 실전 체크리스트
# 읽는 법: 아래 항목은 동작 흐름을 빠르게 확인하기 위한 요약 예시입니다.Session Shell 체크리스트 [ ] UI timeline과 model history가 분리되어 있다.[ ] ledger 기록은 UI/model projection과 별도로 만들어진다.[ ] partial stream과 final assistant message가 구분된다.[ ] approval UI는 정책 판단을 직접 하지 않는다.[ ] pending input queue와 approval queue가 독립적으로 관리된다.[ ] overlay가 열려 있어도 cancel/background event를 처리할 수 있다.[ ] remote input과 local input이 같은 submit boundary로 들어간다.
실무 예시: 화면 메시지와 transcript를 분리하기
터미널 화면에는 진행 중인 spinner, 짧은 안내, 승인 버튼, 실패 요약이 섞입니다. 하지만 transcript에는 나중에 재현 가능한 사건만 남아야 합니다. 예를 들어 파일 쓰기 승인을 요청하는 화면에서는 "이 파일을 수정할까요?"라는 UI 문구가 보일 수 있지만, transcript에는 승인 대상 경로, 요청한 tool call, 승인 결과, 실행 결과가 구조화되어 남아야 합니다.
| 화면에 필요한 정보 | transcript에 필요한 정보 |
|---|---|
| 현재 진행 상태 | turn id와 event type |
| 사용자에게 보여줄 짧은 설명 | tool request id와 인자 요약 |
| 승인 버튼 또는 취소 버튼 | permission decision과 근거 |
| 사람이 읽기 쉬운 오류 문구 | 재시도 가능 여부와 error code |
실패 사례는 화면 로그를 그대로 모델 context나 transcript에 넣는 경우입니다. spinner 문구, 중복 진행 메시지, 긴 shell 출력이 그대로 쌓이면 비용이 커지고 실제 판단에 필요한 이벤트를 찾기 어려워집니다. 반대로 transcript가 너무 빈약하면 "사용자가 무엇을 승인했는가"를 나중에 설명할 수 없습니다. Session shell은 UI를 예쁘게 만드는 계층이 아니라, 사람이 보는 표시와 runtime이 기록하는 사건을 분리하는 계층입니다.
마무리
Claude Code류의 terminal UI를 runtime shell로 보면, AI agent 개발의 난이도가 더 선명해집니다. 화면은 단순히 예쁘게 보여주는 곳이 아니라, 여러 runtime 상태가 충돌하지 않도록 투영하는 곳입니다.
다음 글에서는 사용자가 Enter를 누른 순간을 다룹니다. 한 줄 prompt는 곧바로 모델 호출이 아니라, submit boundary를 통과해 하나의 agent turn이 됩니다.
Key takeaways
- The terminal agent's screen is closer to a runtime state container than a simple renderer.
- The message displayed on the screen, the message sent to the model, and the message saved in the transcript are different.
- Approval UI, input queue, partial stream, and overlay status should be coordinated in one place, but policy judgment should be separated.
- Developers using AI tools need to be able to understand what state is pending when the screen appears to be frozen.
In this article, we will look at the Claude Code-type terminal screen as runtime shell, not UI.
On the surface, it looks like all there is is an input window and a message list. However, the product-level agent's screens simultaneously handle ongoing model streams, tool approval, input queues, local command overlays, cost notifications, cancellation status, and session recovery information.
At this point, many agent demos fail to make it into production. This is because the status displayed on the screen and the status transmitted to the model are managed in the same list.
1. Why is the screen a runtime shell?
The screen in the terminal agent is not a simple output window. On the same screen, the user enters a question, views the model's partial response, decides to approve the tool run, hits Cancel, and enters new input again.
In other words, the screen is a surface where multiple flows meet.
| situation | Is it necessary for the screen? | Is it necessary for the model? | Is it necessary for records? |
|---|---|---|---|
| user input | yes | yes | yes |
| assistant partial text | yes | Required after final assembly | Yes, usually summary or final value |
| Approval dialog text | yes | Usually no | Yes, based on approval results |
| local command overlay | yes | In some cases no | yes |
| Cost Reminder | yes | no | yes |
| tool result | Yes, in summary form | Yes, structured form | yes |
Sending all text on the screen to the model pollutes the context. Conversely, if the tool results needed for the model are saved only as screen strings, the next inference will not proceed.
2. Expression of three messages
A product agent must project the same event in three ways.
- UI projection: Human-readable message, collapse/expand, progress indicator.
- Model projection: role, content block, tool result, system context.
- Ledger projection: timestamp, turn id, cost, approval, restore metadata.
Making this distinction from the beginning will make subsequent functions easier. For example, the tool result may appear as “3 files read” on the screen, but the model should contain a structured summary or observation of each file. The transcript must indicate when it was read and with what authority.
3. Session shell state structure
A session shell can have the following states:
| status group | example |
|---|---|
| conversation state | User message, assistant message, tool result |
| input state | Current input value, paste buffer, attachment, editor selection |
| execution state | active turn, abort signal, pending input queue |
| approval state | tool permission prompt, sandbox confirmation |
| overlay state | Setting screen, help, cost notice, local command result |
| integration state | remote prompt, bridge event, session restore data |
The important thing is that the shell does not determine all policies. The shell stores state and receives user interaction, but a separate layer must decide whether to determine permissions or execute tools.
Practical Tips Make the screen layer responsible for “what to show” and the policy layer responsible for “what to allow.” If these two are mixed, they will have to be modified again when creating headless batch mode or remote sessions.
4. Separation of approval UI and policy engine
When the agent attempts to write a file or execute a shell command, a confirmation dialog should appear on the screen. However, the dialog itself should not determine “is this operation dangerous?”
The policy engine makes the decision first.
# How to read: The items below are summarized examples to quickly check the action flow.Tool Requestto policy engine: allow / deny / ask questionto session shell: Display question required status in UIto select userto policy decision recordto tool runtime generates execution or rejection results
This structure is also good for testing. You can test the policy engine without a UI, and in batch mode rather than an interactive shell, the “Question Required” status can be automatically rejected or handled with a prior policy.
5. Shell design through conceptual code
The code below is the newly written concept code.
# How to read: Conceptual code that describes runtime boundaries, not a clone of the actual implementation.class SessionShell: # Initializes runtime dependencies and state storage that the object will reference in later steps. def __init__(self): self.visible_timeline = [] self.model_history = [] self.ledger_buffer = [] self.input_buffer = "" self.pending_inputs = [] self.approval_stack = [] self.current_overlay = None self.active_turn_id = None # Route one runtime event to each representation for the screen, model, and record. def publish(self, event): if event.show_to_user: self.visible_timeline.append(render_for_screen(event)) if event.send_to_model: self.model_history.append(render_for_model(event)) if event.recordable: self.ledger_buffer.append(render_for_ledger(event)) # Before executing the tool, requests that require user approval are uploaded to the overlay stack. def open_approval(self, approval_request): self.approval_stack.append(approval_request) self.current_overlay = "approval" # The approval result is recorded as an event and the next overlay state is restored. def close_approval(self, decision): request = self.approval_stack.pop() self.publish(make_approval_event(request, decision)) self.current_overlay = "approval" if self.approval_stack else None
The key ispublish(). A runtime event is reflected in different ways in the UI, model, and ledger. Without this structure, it becomes difficult to deal with “what should be visible to the user but not to the model” later.
6. Signals that AI developers should read
Developers using AI tools must be able to infer the runtime state by looking at the screen.
| screen signal | Possible runtime states |
|---|---|
| Answers are streaming but new input is displayed | Has an active turn and enters the input queue |
| Approval window pops up and response stops | tool runtime waits for permission decision |
| Only local command results are displayed and there is no model response. | Input is treated as a local-only action |
| Cost or token warning displayed | ledger/accounting approaches budget threshold |
| After canceling, you see a failure result instead of “no result” | Generate abort results to match tool request/result pairs |
The clearer the signal, the easier it is to trust in practice.
7. Mistakes that agent developers should avoid
The first mistake is to use the UI message list as model history. In this way, cost notifications, help phrases, and approval dialog descriptions can be mixed into the model context.
The second mistake is storing partial streams as final messages. Streaming text is an intermediate state. It must be distinguished from the assistant message assembled after the final response is closed.
The third mistake is to allow the overlay to permanently block queue processing. Even if the settings screen is open, background events or cancellation signals must be handled.
8. Practical checklist
# How to read: The items below are summarized examples to quickly check the action flow.Session Shell Checklist [ ] UI timeline and model history are separated.[ ] Ledger records are created separately from UI/model projection.[ ] Partial stream and final assistant message are distinguished.[ ] The approval UI does not directly make policy decisions.[ ] The pending input queue and approval queue are managed independently.[ ] Cancel/background events can be handled even if the overlay is open.[ ] Remote input and local input enter the same submit boundary.
Practical example: Separating screen messages and transcripts
The terminal screen is a mix of ongoing spinners, short instructions, an accept button, and a failure summary. However, the transcript should contain only events that can be reproduced later. For example, on a screen requesting permission to write a file, the UI phrase “Do you want to modify this file?” may appear, but the transcript must contain the approval target path, requested tool call, approval result, and execution result in a structured manner.
| Information needed on screen | Information needed for transcript |
|---|---|
| current progress | turn id and event type |
| A short description to show to the user | Tool request id and argument summary |
| Accept button or Cancel button | Permission decision and basis |
| Human-readable error text | Whether retry is possible and error code |
A failure case is when the screen log is entered as is into the model context or transcript. If spinner phrases, duplicate progress messages, and long shell output pile up, costs increase and it becomes difficult to find events necessary for actual judgment. Conversely, if the transcript is too sparse, it cannot later explain “what the user approved”. The session shell is not a layer that makes the UI pretty, but a layer that separates the display that humans see from the events that the runtime records.
finish
If you look at Claude Code's terminal UI as a runtime shell, the difficulty of AI agent development becomes clearer. The screen is not just a place to display things pretty, but a place to project various runtime states to prevent them from colliding.
The next article will cover the moment the user presses Enter. A one-line prompt is not a direct model call, but an agent turn that passes the submit boundary.

댓글
GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.