사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
Agent.Chat은 화면에 채팅을 붙이고, 그 채팅이 사용자 대신 화면을 조작합니다. 서버는 메시지를 전달만 하고, 모든 툴은 사용자 자신의 브라우저 탭에서 실행됩니다.runAgentTurn 엔드포인트입니다.st.tool로 공개한 동작 하나로, 보통 버튼이 이미 부르는 핸들러입니다._overrides.tsx에서 갈아 끼울 수 있는 채팅의 한 부분입니다.@로 가리켜 자기 메시지에 실어 보내는 데이터입니다.Agent.Zone으로 감싸 자기만의 대화를 가진 구획입니다.| 질문 |
|---|
| ↳ 답 |
| 환불은 어느 기계에서 실행됐나요? |
| 고객의 브라우저 탭입니다. 환불 버튼이 부르는 바로 그 핸들러로 실행됐습니다. |
| 누구의 자격증명으로 실행됐나요? |
| 로그인한 사용자 자신의 것입니다. 그 페이지에서 버튼을 누른 것과 똑같습니다. |
| 남의 주문에 손대지 못하게 막은 것은? |
| 모든 호출이 지나는 가드, 그리고 툴이 요구하면 승인 카드입니다. |
| 서버는 무엇을 했나요? |
runAgentTurn이 대화와 툴 설명을 프로바이더에 넘기고 응답 하나를 돌려줬습니다. |
| 서버에 남은 것은? |
| 없습니다. 세션도 대화도 서버에 남지 않습니다. |


AgentRelayAccess는 lib/option.ts에서 앱의 가드로 option.setAgentAccess(SignedIn)을 호출하기 전까지 모든 호출을 거절합니다. 다른 엔드포인트에 적는 것과 같은 가드를 받고, 여러 개면 모두 통과해야 합니다(AND).Agent.Chat은 에이전트가 닿아야 할 모든 화면을 감싸는 레이아웃에 한 번만 마운트합니다. 이 요소 하나에 런처, 대화창, 승인 카드, 스트리밍 루프가 모두 들어 있습니다:instructions는 항상 영어입니다. 가게가 어느 언어로 장사하든 모델이 읽는 글이라서, 모든 .desc()와 Agent.Guide도 마찬가지입니다.title과 intro는 l()을 거칩니다. l()은 사람이 읽는 문자열용이라서 instructions에는 쓰지 않습니다.persist는 새로고침해도 대화를 남깁니다. 어디에 보관되는지는 마지막 섹션에서 다룹니다.

open과 onOpenChange를 함께 넘겨 바깥에서 제어하세요.Agent.Guide가 그 위에 더합니다.onOpenChange와 함께 넘겨 바깥에서 제어하는 열림 상태이며, 빼면 패널이 스스로 관리합니다.false로 떠 있는 버튼을 그리지 않습니다.false면 inline 채팅용으로 헤더 바와 header를 빼며, 비우기는 /new로 합니다.?prompt= 값을 넣는 자리입니다.false면 이 단축키를 브라우저에 돌려줍니다.className은 두 곳 모두에 적용됩니다.builtins로 이 채팅의 에이전트가 받을 툴을 고릅니다.Link와 같은 라우터로 내부 경로를 엽니다.builtins={["readScreen", "readState", "highlight"]}로 둡니다. navigate와 goBack이 없으면 떠날 수 없습니다.builtins가 건드리지 않습니다.askUser는 이 목록에 없습니다. 세션의 툴이라 builtins로 빠지지 않습니다.page/**/_overrides.tsx 매니페스트에 묶으면 레이아웃처럼 라우트 트리를 따라 내려갑니다.label, hotkey, 새 메시지 뱃지용 unread를 받는 닫힌 상태의 버튼입니다.memo()로 감싸세요.isRunning을 받으며, 기본 구현은 요소를 더하지 않습니다.input recipe 슬롯에서 옵니다.remove* 툴이 기본으로 거치는, 작성창 위의 승인 관문입니다.askUser 카드이며, 직접 쓰는 답은 작성창에 입력합니다./ 커맨드와 @ 참조를 보여 주는, 작성창 위의 자동완성 목록입니다.dangerouslySetInnerHTML 없이 React 요소만 만듭니다.lang은 펜스에 적힌 언어입니다.AgentChat은 최후의 수단입니다. 패널 전체를 바꾸고, 겹쳐 쓸 기본 구현을 내보내지 않습니다.AgentSteps는 겉모습만 바꾸는 슬롯이 아닙니다. 턴 전체를 받으므로, 중간 단계는 <details>로 접고 최종 답만 밖에 둘 수 있습니다:isRunning은 세션이 작업 중인 마지막 턴에서만 true입니다. 이것이 없으면 진행 중인 줄과 끝난 턴의 머리글을 구별할 수 없습니다.DefaultSteps는 같은 말풍선을 Fragment 안에 평평하게 그리므로 className을 받지 않고, 기존 레이아웃도 차이를 느끼지 못합니다.st.tool(name)으로 툴을 선언하고 .desc()와 모델이 넘길 .arg()를 적습니다..exec(fn) 대신 .card(render)로 끝냅니다. 호출은 채팅에 멈춰 서고, 그 자리에 앱의 폼이 그려집니다.submit(value)로 답하거나 cancel(reason)으로 거절합니다.cancel(reason)은 오류로 전달되므로, 닫힌 카드도 에이전트가 반응할 수 있는 답이 됩니다..card()에서는 읽지 않습니다. 사용자 앞에 놓인 카드가 이미 묻는 행위이기 때문입니다.reference prop에 선언합니다. @ 메뉴가 앱의 검색으로 행을 찾습니다.<Agent.Chat reference={[…]} />useAgentReference()ReferenceSource 객체입니다:@ 메뉴에서 이 묶음을 부르는 이름이라 l()을 거칩니다.signal이 중단됩니다.type이 브라우저 밖으로 나가는 것을 정합니다. st.expose와 같은 방식으로, 지정한 모델 클래스가 값을 마스킹하므로 hidden, secret, visual 필드는 나가지 않습니다.Light 클래스에는 대개 그 필드가 없고, 그것으로 마스킹된 참조는 정작 가리킨 이유였던 필드 없이 도착합니다.search에는 브라우저가 부를 수 있는 쿼리가 필요합니다. 여기서는 q.search() 필터를 감싼 slice이며, 이런 slice는 목록으로 훑어도 괜찮은 데이터에만 둡니다.reference에는 함수가 들어 있어 서버 레이아웃이 넘길 수 없으므로, ui/ 아래 작은 컴포넌트에 둡니다.field(Any)로 저장된 리치 텍스트가 에디터 문서가 아니라 한 문단으로 읽혀야 한다는 것을 아는 것도 그 컴포넌트뿐입니다:Agent.Zone이나 AgentProvider안에 그리세요. children 옆의 루트 Agent.Chat은 세션을 내주지 않아서, zone 밖에서는 경고만 남기고 아무 일도 하지 않습니다.path가 필드를 가리킵니다. 문서 안의 점 경로이므로, 한 문서의 필드 두 개는 서로 다른 참조 두 개입니다.@[label](mention:…) 토큰 대신 가리키는 대상의 이름으로 보여 줍니다.mentions={false}는 평범한 textarea를 유지합니다. 작성창을 교체했거나 보내는 토큰을 그대로 보고 싶을 때 쓰며, 보내는 문자열은 어느 쪽이든 같습니다.

AgentQueued 카드에서 되돌리거나 버릴 수 있게 합니다.page().prompt()로 만들며, 이는 MCP 클라이언트에만 나열되고 인페이지 채팅에는 나오지 않습니다./단어는 평범한 텍스트로 모델에게 전송됩니다./retry와 /compact는 에이전트가 작업 중이라고 알려 줍니다.shortcut={false}가 아니면 채팅을 열고 작성창에 포커스합니다./help 출력을 그냥 붙이면 다음 턴에 어시스턴트가 자기가 한 말로 받아들입니다.Agent.Zone의 onSession이 세션을 건네주고, zone 안에서는 useAgent()로 읽으며, 교체한 작성창은 session prop으로 받습니다.persist를 켜면 새로고침 뒤에도 다시 부를 수 있습니다. ↑/↓ 목록은 복원된 대화에서 채워지고, 쓰다 만 초안은 목록 끝에서 다시 돌아옵니다.persist는 새로고침해도 대화를 남기고, Agent.History는 앱의 서버에 보관합니다.| 쓰는 법 |
|---|
| ↳ 보관 방식 |
| persist |
새로고침은 견디고 탭과 함께 사라져 공용 PC에 남지 않는 sessionStorage에 둡니다. |
| persist={{ storage: "local" }} |
탭을 닫아도 대화가 남아야 할 때 localStorage에 둡니다. |
| persist={{ key: "…" }} |
저장 키를 직접 정하며, 기본값은 akan.agent.<appName>에 zone 안이면 zone 경로가 붙습니다. |
| <Agent.History /> |
| 직접 작성한 함수 세 개로 앱의 서버에 보관합니다. |
SessionHistory입니다. 함수는 prop으로 RSC 경계를 넘지 못하므로, prop으로 넘기면 세션을 만드는 곳까지의 모든 조상이 클라이언트 컴포넌트가 됩니다. 그래서 Agent.Guide처럼 잎 컴포넌트로, 보관할 zone 안에 마운트합니다:Agent.History는 Agent.Zone이나 AgentProvider 안에 둡니다. 그 밖에서는 렌더링 중에 오류를 던집니다.Agent.Zone 안의 채팅은 zone의 세션에 붙으므로, 그 Agent.Chat에 적은 persist, builtins, instructions는 무시됩니다.onCompact는 압축 뒤에 불립니다. 압축이 메시지들을 요약 하나로 바꾼 뒤 호출되며, 서버에 자체 요약을 두는 호스트가 기준점을 옮기는 자리입니다.Agent.History는 내용까지 포함한 메시지를 그대로 받습니다.st.tool로 동작 하나를 선언하고, 컴포넌트가 구독한 스토어 키만 에이전트도 읽을 수 있으며, 스토어 클래스에서 저절로 생기는 것은 없습니다.