세션 작업
세션 작업 (Session operations)
세션이 만들어지면 이 작업들로 세션을 읽거나 업데이트하거나 보관하거나 삭제할 수 있어요. 세션을 만들고 작업을 보내는 방법은 Start a session을 참고하세요. 세션 상태, 에이전트 구성 업데이트, 예산 업데이트, 조회·목록·보관·삭제까지 차근차근 안내해 드려요.
출처: 문서
본문
세션이 존재하면 이 작업들로 세션을 읽거나, 업데이트하거나, 보관하거나, 삭제할 수 있어요. 세션을 만들고 작업을 보내는 방법은 Start a session을 참고하세요.
Session statuses
세션은 이 상태들을 거쳐 진행돼요. 세션 수명주기는 Start a session을 참고하세요.
| Status | Description |
|---|---|
idle |
Agent is waiting for input, including user messages or tool confirmations. Sessions created without initial_events start in idle. |
running |
Agent is actively executing. |
rescheduling |
Transient error occurred, retrying automatically. |
terminated |
Session has ended, either because of an unrecoverable error or because it was archived. A session that finishes its work goes idle, not terminated. |
Updating the agent configuration
세션의 agent.tools와 agent.mcp_servers는 새 에이전트 버전을 만들지 않고 세션 중간에 업데이트할 수 있어요. 여기에는 권한 정책과 도메인 필터 같은 도구별 웹 설정이 포함돼요. 업데이트는 세션 로컬이며 기본 에이전트로 전파되지 않아요. 업데이트된 allowed_domains와 blocked_domains는 세션의 나머지에 적용돼요.
세션이 만들어진 후에는 에이전트의 tools와 mcp_servers만 변경할 수 있어요. 에이전트와 다른 model, system, skills 값으로 세션을 실행하려면 세션을 만들 때 에이전트 구성 오버라이드를 사용하세요. 에이전트의 모델 구성(inference_geo 고정 포함)도 세션 중간에 바꿀 수 없어요: 에이전트를 저장할 때 고정값을 설정하거나, 생성 시 model 오버라이드로 단일 세션에 대해 설정·해제하세요. 에이전트의 구성된 system 필드는 세션 수명 동안 고정돼요. 지원하는 모델에서는 system.message 이벤트를 보내 세션 중간에 시스템 수준 지침을 덧붙일 수 있어요.
tools나 mcp_servers 업데이트의 의미는 완전한 대체예요: 제공된 배열이 새 값이에요. 기존 항목을 보존하려면 GET으로 세션을 가져와 배열을 수정하고 POST로 다시 보내세요.
에이전트를 업데이트하려면 세션이 idle이어야 해요. 세션이 실행 중일 때 에이전트를 업데이트하려면 user.interrupt 이벤트를 단독으로 보내고 세션이 idle이 될 때까지 기다리세요.
ant beta:sessions update --session-id "$SESSION_ID" <<'YAML'
agent:
tools:
- type: agent_toolset_20260401
- type: mcp_toolset
mcp_server_name: linear
mcp_servers:
- type: url
name: linear
url: https://mcp.linear.app/sse
YAML
client.beta.sessions.update(
session.id,
agent={
"tools": [
{"type": "agent_toolset_20260401"},
{"type": "mcp_toolset", "mcp_server_name": "linear"},
],
"mcp_servers": [
{"type": "url", "name": "linear", "url": "https://mcp.linear.app/sse"}
],
},
)
await client.beta.sessions.update(session.id, {
agent: {
tools: [
{ type: "agent_toolset_20260401" },
{ type: "mcp_toolset", mcp_server_name: "linear" }
],
mcp_servers: [{ type: "url", name: "linear", url: "https://mcp.linear.app/sse" }]
}
});
await client.Beta.Sessions.Update(session.ID, new()
{
Agent = new()
{
Tools =
[
new BetaManagedAgentsAgentToolset20260401Params
{
Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
},
new BetaManagedAgentsMcpToolsetParams
{
Type = BetaManagedAgentsMcpToolsetParamsType.McpToolset,
McpServerName = "linear",
},
],
McpServers =
[
new()
{
Type = BetaManagedAgentsUrlMcpServerParamsType.Url,
Name = "linear",
Url = "https://mcp.linear.app/sse",
},
],
},
});
_, err = client.Beta.Sessions.Update(ctx, session.ID, anthropic.BetaSessionUpdateParams{
Agent: anthropic.BetaManagedAgentsSessionAgentUpdateParam{
Tools: []anthropic.BetaManagedAgentsSessionAgentUpdateToolUnionParam{
{
OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
},
},
{
OfMCPToolset: &anthropic.BetaManagedAgentsMCPToolsetParams{
Type: anthropic.BetaManagedAgentsMCPToolsetParamsTypeMCPToolset,
MCPServerName: "linear",
},
},
},
MCPServers: []anthropic.BetaManagedAgentsURLMCPServerParams{
{
Type: anthropic.BetaManagedAgentsURLMCPServerParamsTypeURL,
Name: "linear",
URL: "https://mcp.linear.app/sse",
},
},
},
})
if err != nil {
panic(err)
}
client.beta().sessions().update(
session.id(),
SessionUpdateParams.builder()
.agent(BetaManagedAgentsSessionAgentUpdate.builder()
.addTool(BetaManagedAgentsAgentToolset20260401Params.builder()
.type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
.build())
.addTool(BetaManagedAgentsMcpToolsetParams.builder()
.type(BetaManagedAgentsMcpToolsetParams.Type.MCP_TOOLSET)
.mcpServerName("linear")
.build())
.addMcpServer(BetaManagedAgentsUrlMcpServerParams.builder()
.type(BetaManagedAgentsUrlMcpServerParams.Type.URL)
.name("linear")
.url("https://mcp.linear.app/sse")
.build())
.build()
.build()
);
$client->beta->sessions->update(
$session->id,
agent: BetaManagedAgentsSessionAgentUpdate::with(
tools: [
BetaManagedAgentsAgentToolset20260401Params::with(type: 'agent_toolset_20260401'),
BetaManagedAgentsMCPToolsetParams::with(mcpServerName: 'linear', type: 'mcp_toolset'),
],
mcpServers: [
BetaManagedAgentsURLMCPServerParams::with(
name: 'linear',
type: 'url',
url: 'https://mcp.linear.app/sse',
),
],
),
);
client.beta.sessions.update(
session.id,
agent: {
tools: [
{type: :agent_toolset_20260401},
{type: :mcp_toolset, mcp_server_name: "linear"}
],
mcp_servers: [
{type: :url, name: "linear", url: "https://mcp.linear.app/sse"}
]
}
)
Updating the session budget
예산을 붙여 만든 세션은 두 가지 예산 업데이트를 받아요: 상한을 새 max_list_cost로 교체하거나, budget을 null로 설정해 제거하는 것이에요. 둘 다 상한에 도달해 일시 중지됐던 작업을 자동으로 재개해요. 교체 상한은 현재 것보다 높거나 낮을 수 있지만, 세션이 소비한 list cost보다 엄격하게 커야 해요. 그리고 제거는 단방향이에요: 비null budget은 현재 예산이 있는 세션에서만 받아들여지므로, 제거된 예산을 다시 추가하거나 예산 없이 만든 세션에 추가할 수 없어요. 요청 예시, 오류 동작, list cost에 포함되는 것은 Session budgets을 참고하세요.
Retrieving a session
ant beta:sessions retrieve --session-id "$SESSION_ID"
retrieved = client.beta.sessions.retrieve(session.id)
print(f"Status: {retrieved.status}")
const retrieved = await client.beta.sessions.retrieve(session.id);
console.log(`Status: ${retrieved.status}`);
var retrieved = await client.Beta.Sessions.Retrieve(session.ID);
Console.WriteLine($"Status: {retrieved.Status.Raw()}");
retrieved, err := client.Beta.Sessions.Get(ctx, session.ID, anthropic.BetaSessionGetParams{})
if err != nil {
panic(err)
}
fmt.Printf("Status: %s\n", retrieved.Status)
var retrieved = client.beta().sessions().retrieve(session.id());
IO.println("Status: " + retrieved.status());
$retrieved = $client->beta->sessions->retrieve($session->id);
echo "Status: {$retrieved->status}\n";
retrieved = client.beta.sessions.retrieve(session.id)
puts "Status: #{retrieved.status}"
Listing sessions
GET /v1/sessions의 결과는 페이지로 나뉘어요. limit 쿼리 파라미터로 페이지 크기를 제어하세요. 각 응답에는 next_page 커서가 포함되며, 다음 요청의 page 파라미터로 전달해 다음 페이지를 가져올 수 있어요. 더 이상 결과가 없으면 next_page는 null이에요.
이전 페이지로 돌아가려면 prev_page를 page 파라미터로 전달하세요. 첫 페이지에 있으면 prev_page는 null이에요.
page 커서는 불투명하며, 그것을 만든 요청의 order를 인코딩해요. order 쿼리 파라미터는 생성 시간 기준 결과의 정렬 방향(asc 또는 desc)을 설정하며, 기본은 desc(최신순)예요. 커서를 다른 order로 재사용하면 400 에러가 반환돼요. created_at 필터를 바꿔 커서의 위치를 제외하게 해도 마찬가지예요. 나머지 필터와 limit를 포함한 다른 쿼리 파라미터는 페이지 요청 사이에 바뀔 수 있어요. 목록 엔드포인트가 공유하는 페이지네이션 필드는 Pagination을 참고하세요.
next_cursor=$(jq -r '.next_page' <<< "$first_page")
second_page=$(curl -sS --fail-with-body
"https://api.anthropic.com/v1/sessions?agent_id=$AGENT_ID&limit=1&page=$next_cursor"
-H "x-api-key: $ANTHR...KEY"
-H "anthropic-version: 2023-06-01"
-H "anthropic-beta: managed-agents-2026-04-01")
prev_cursor=$(jq -r '.prev_page' <<< "$second_page")
curl -sS --fail-with-body
"https://api.anthropic.com/v1/sessions?agent_id=$AGENT_ID&limit=1&page=$prev_cursor"
-H "x-api-key: $ANTHR...KEY"
-H "anthropic-version: 2023-06-01"
-H "anthropic-beta: managed-agents-2026-04-01"
| jq '{prev_page, next_page}'
```bash CLI
# --format raw returns one page envelope with its prev_page and next_page
# cursors; the default output auto-paginates and emits only the sessions.
cursors=$(ant beta:sessions list \
--agent-id "$AGENT_ID" \
--limit 1 \
--format raw \
--transform '{prev_page,next_page}')
printf '%s\n' "$cursors"
# Pass the next_page cursor back as --page to fetch the next page.
NEXT_PAGE=$(jq -r '.next_page' <<< "$cursors")
ant beta:sessions list \
--agent-id "$AGENT_ID" \
--limit 1 \
--page "$NEXT_PAGE" \
--format raw \
--transform '{prev_page,next_page}'
# Pass that response's prev_page as --page to go back the same way.
# Set `limit` low so the results span more than one page.
first_page = client.beta.sessions.list(limit=1, agent_id=agent.id)
# `prev_page` is None on the first page; `next_page` is None on the last.
print(f"prev_page: {first_page.prev_page}")
print(f"next_page: {first_page.next_page}")
# Pass `next_page` back as `page` to fetch the next page.
second_page = client.beta.sessions.list(
limit=1, agent_id=agent.id, page=first_page.next_page
)
for listed_session in second_page.data:
print(f"{listed_session.id}: {listed_session.status}")
# Pass `prev_page` back as `page` to return to the previous page.
previous_page = client.beta.sessions.list(
limit=1, agent_id=agent.id, page=second_page.prev_page
)
for listed_session in previous_page.data:
print(f"{listed_session.id}: {listed_session.status}")
# For forward-only iteration, the page object is also directly iterable.
const firstPage = await client.beta.sessions.list({ limit: 1, agent_id: agent.id });
// prev_page is null on the first page; next_page is set when more sessions exist.
console.log(`prev_page: ${firstPage.prev_page}`);
console.log(`next_page: ${firstPage.next_page}`);
// Pass next_page as the `page` cursor to fetch the second page.
const secondPage = await client.beta.sessions.list({
limit: 1,
agent_id: agent.id,
page: firstPage.next_page
});
for (const listedSession of secondPage.data) {
console.log(`Page 2 has ${listedSession.id}: ${listedSession.status}`);
}
// Pass the second page's prev_page cursor to step back to the first page.
const previousPage = await client.beta.sessions.list({
limit: 1,
agent_id: agent.id,
page: secondPage.prev_page
});
for (const listedSession of previousPage.data) {
console.log(`Back on page 1: ${listedSession.id} is ${listedSession.status}`);
}
// For forward-only iteration, the page object is also directly iterable.
// The SessionListPage that `List` returns exposes the items but not the
// pagination cursors. To read `prev_page` / `next_page`, deserialize the raw
// response into SessionListPageResponse instead.
using var page1Response = await client.Beta.Sessions.WithRawResponse.List(
new SessionListParams { Limit = 1, AgentID = agent.ID }
);
var page1 = await page1Response.Deserialize<SessionListPageResponse>();
Console.WriteLine($"prev_page: {page1.PrevPage ?? "null"}");
Console.WriteLine($"next_page: {page1.NextPage ?? "null"}");
// Advance: pass `next_page` from page 1 as the `page` cursor.
using var page2Response = await client.Beta.Sessions.WithRawResponse.List(
new SessionListParams { Limit = 1, AgentID = agent.ID, Page = page1.NextPage }
);
var page2 = await page2Response.Deserialize<SessionListPageResponse>();
foreach (var listedSession in page2.Data ?? [])
{
Console.WriteLine($"Page 2: {listedSession.ID}: {listedSession.Status.Raw()}");
}
// Go back: pass `prev_page` from page 2 as the same `page` cursor.
using var previousPageResponse = await client.Beta.Sessions.WithRawResponse.List(
new SessionListParams { Limit = 1, AgentID = agent.ID, Page = page2.PrevPage }
);
var previousPage = await previousPageResponse.Deserialize<SessionListPageResponse>();
foreach (var listedSession in previousPage.Data ?? [])
{
Console.WriteLine($"Back to page 1: {listedSession.ID}: {listedSession.Status.Raw()}");
}
// For forward-only iteration, (await client.Beta.Sessions.List(...)).Paginate() returns an IAsyncEnumerable that auto-follows next_page.
// Page 1: prev_page is empty because nothing precedes the first page.
firstPage, err := client.Beta.Sessions.List(ctx, anthropic.BetaSessionListParams{
AgentID: anthropic.String(agent.ID),
Limit: anthropic.Int(1),
})
if err != nil {
panic(err)
}
fmt.Printf("Page 1 prev_page: %q\n", firstPage.PrevPage)
fmt.Printf("Page 1 next_page: %q\n", firstPage.NextPage)
// Advance: pass next_page as the Page cursor to fetch page 2.
secondPage, err := client.Beta.Sessions.List(ctx, anthropic.BetaSessionListParams{
AgentID: anthropic.String(agent.ID),
Limit: anthropic.Int(1),
Page: anthropic.String(firstPage.NextPage),
})
if err != nil {
panic(err)
}
for _, listedSession := range secondPage.Data {
fmt.Printf("Page 2: %s: %s\n", listedSession.ID, listedSession.Status)
}
// Go back: page 2's prev_page is the cursor for the page before it.
previousPage, err := client.Beta.Sessions.List(ctx, anthropic.BetaSessionListParams{
AgentID: anthropic.String(agent.ID),
Limit: anthropic.Int(1),
Page: anthropic.String(secondPage.PrevPage),
})
if err != nil {
panic(err)
}
for _, listedSession := range previousPage.Data {
fmt.Printf("Back to page 1: %s: %s\n", listedSession.ID, listedSession.Status)
}
// For forward-only iteration, use ListAutoPaging to auto-follow next_page.
var params = SessionListParams.builder()
.agentId(agent.id())
.limit(1)
.build();
var firstPage = client.beta().sessions().list(params);
for (var listedSession : firstPage.data()) {
IO.println(listedSession.id() + ": " + listedSession.status());
}
// prev_page is an empty Optional on the first page; next_page points to page 2.
IO.println("prev_page: " + firstPage.response().prevPage());
IO.println("next_page: " + firstPage.response().nextPage());
// Advance by passing next_page as the page cursor.
var nextCursor = firstPage.response().nextPage().orElseThrow();
var secondPage = client.beta().sessions().list(params.toBuilder().page(nextCursor).build());
// Go back by passing prev_page as the same page cursor.
var prevCursor = secondPage.response().prevPage().orElseThrow();
var previousPage = client.beta().sessions().list(params.toBuilder().page(prevCursor).build());
// Back on the first page, so prev_page is empty again.
IO.println("prev_page: " + previousPage.response().prevPage());
// For forward-only iteration, page.autoPager() returns an Iterable that auto-follows next_page.
// Page 1: prevPage is null because nothing precedes the first page.
$firstPage = $client->beta->sessions->list(agentID: $agent->id, limit: 1);
echo 'Page 1 prev_page: ' . ($firstPage->prevPage ?? 'null') . "\n";
echo 'Page 1 next_page: ' . ($firstPage->nextPage ?? 'null') . "\n";
// Advance: pass nextPage back as the `page` cursor to fetch page 2.
$secondPage = $client->beta->sessions->list(
agentID: $agent->id,
limit: 1,
page: $firstPage->nextPage,
);
foreach ($secondPage->getItems() as $listedSession) {
echo "Page 2: {$listedSession->id}: {$listedSession->status}\n";
}
// Go back: page 2's prevPage is the cursor for the page before it.
$previousPage = $client->beta->sessions->list(
agentID: $agent->id,
limit: 1,
page: $secondPage->prevPage,
);
foreach ($previousPage->getItems() as $listedSession) {
echo "Back to page 1: {$listedSession->id}: {$listedSession->status}\n";
}
// For forward-only iteration, $page->pagingEachItem() yields every session across pages.
first_page = client.beta.sessions.list(agent_id: agent.id, limit: 1)
first_page.data.each do |listed_session|
puts "#{listed_session.id}: #{listed_session.status}"
end
# `prev_page` is nil on the first page. The next-page cursor is exposed as
# `next_page_` (trailing underscore) because plain `next_page` is the helper
# method that fetches the next page object for you.
puts "prev_page: #{first_page.prev_page.inspect}"
puts "next_page: #{first_page.next_page_.inspect}"
# Pass either cursor back as `page` to move through the list in both directions.
second_page = client.beta.sessions.list(
agent_id: agent.id,
limit: 1,
page: first_page.next_page_
)
back_to_first = client.beta.sessions.list(
agent_id: agent.id,
limit: 1,
page: second_page.prev_page
)
back_to_first.data.each do |listed_session|
puts "#{listed_session.id}: #{listed_session.status}"
end
# For forward-only iteration, page.auto_paging_each auto-follows next_page.
Archiving a session
역사를 보존하면서 새 이벤트를 보내지 못하게 하려면 세션을 보관하세요. running 세션은 보관할 수 없어요. 보관하려면 user.interrupt 이벤트를 단독으로 보내고 세션이 idle이 될 때까지 기다리세요.
ant beta:sessions archive \
--session-id "$SESSION_ID"
client.beta.sessions.archive(session.id)
await client.beta.sessions.archive(session.id);
await client.Beta.Sessions.Archive(session.ID);
_, err = client.Beta.Sessions.Archive(ctx, session.ID, anthropic.BetaSessionArchiveParams{})
if err != nil {
panic(err)
}
client.beta().sessions().archive(session.id());
$client->beta->sessions->archive($session->id);
client.beta.sessions.archive(session.id)
Deleting a session
세션을 삭제하면 그 기록, 이벤트, 연결된 샌드박스가 영구적으로 제거돼요. running 세션은 삭제할 수 없어요. 삭제하려면 user.interrupt 이벤트를 단독으로 보내고 세션이 idle이 될 때까지 기다리세요.
메모리 스토어, 볼트, 스킬, 환경, 에이전트는 독립된 리소스라 세션 삭제의 영향을 받지 않아요. Files API로 업로드한 파일도 영향을 받지 않지만, 세션 자체가 만든 파일은 세션에 범위가 지정되어 파일시스템과 함께 영구 삭제돼요. 세션을 삭제하기 전에 보관할 것이 있으면 미리 다운로드하세요. 마지막 턴 끝에 쓰인 출력 파일은 세션이 유휴 상태가 된 후 세션 파일 목록에 나타나기까지 몇 초 걸릴 수 있으니, 기대하는 파일이 먼저 나열되었는지 확인하세요.
ant beta:sessions delete \
--session-id "$SESSION_ID"
client.beta.sessions.delete(session.id)
await client.beta.sessions.delete(session.id);
await client.Beta.Sessions.Delete(session.ID);
_, err = client.Beta.Sessions.Delete(ctx, session.ID, anthropic.BetaSessionDeleteParams{})
if err != nil {
panic(err)
}
client.beta().sessions().delete(session.id());
$client->beta->sessions->delete($session->id);
client.beta.sessions.delete(session.id)
더 알아보기 (Learn more)
- Start a session — 세션 만들고 작업 보내기
- Session event stream — 이벤트 보내고 스트리밍하기
- Session budgets — 세션 예산 업데이트하기