Skip to content

Commit 0451c06

Browse files
foleydangclaude
andcommitted
feat(agentstudio): tool approval, event deltas, session resources/threads, security, message accessors
- P0: tool_approval_request/response (replaces tool_confirmation), StopReason pending_batch_id/pending_call_ids, PermissionPolicy, SessionStatus rescheduled/deleted, StopReasonType/PermissionPolicyType, userToolApprovalResponse, event_ids deleted, types[] query fix - P1: event_deltas streaming (stream(eventDeltas) + TextDeltaStream), SessionResources (add/retrieve/list/delete), SessionThreads (list/retrieve/archive + events.list), Security (overview/listAgentLogs) - P2: Message accessors (toolApprovalRequest/error/pendingToolApprovals/ modelRequestEnd/outcomeEvaluation/threadStatus/threadCreated/ sessionUpdated/threadMessageRouting/eventStart/eventDelta/deltaText), RosterEntry name/description, PermissionPolicy model Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent 8f09ec5 commit 0451c06

19 files changed

Lines changed: 1153 additions & 29 deletions

‎src/main/java/com/alibaba/dashscope/agentstudio/AgentStudioClient.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.alibaba.dashscope.agentstudio.resource.Deployments;
77
import com.alibaba.dashscope.agentstudio.resource.Environments;
88
import com.alibaba.dashscope.agentstudio.resource.Files;
9+
import com.alibaba.dashscope.agentstudio.resource.Security;
910
import com.alibaba.dashscope.agentstudio.resource.Sessions;
1011
import com.alibaba.dashscope.agentstudio.resource.Skills;
1112
import com.alibaba.dashscope.agentstudio.resource.Vaults;
@@ -25,6 +26,7 @@ public class AgentStudioClient implements Closeable {
2526
private final Vaults vaults;
2627
private final Files files;
2728
private final WebhookEndpoints webhookEndpoints;
29+
private final Security security;
2830
private final String baseUrl;
2931

3032
public AgentStudioClient() {
@@ -55,6 +57,7 @@ public AgentStudioClient(
5557
this.skills = new Skills(this.baseUrl, connectionOptions, apiKey, this.files);
5658
this.vaults = new Vaults(this.baseUrl, connectionOptions, apiKey);
5759
this.webhookEndpoints = new WebhookEndpoints(this.baseUrl, connectionOptions, apiKey);
60+
this.security = new Security(this.baseUrl, connectionOptions, apiKey);
5861
}
5962

6063
public static Builder builder() {
@@ -134,6 +137,10 @@ public WebhookEndpoints webhookEndpoints() {
134137
return webhookEndpoints;
135138
}
136139

140+
public Security security() {
141+
return security;
142+
}
143+
137144
public String getBaseUrl() {
138145
return baseUrl;
139146
}

‎src/main/java/com/alibaba/dashscope/agentstudio/AgentStudioConstants.java‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private AgentStudioConstants() {}
2525
public static final class SSEEventType {
2626
public static final String MESSAGE = "message";
2727
public static final String INTERRUPT = "interrupt";
28-
public static final String TOOL_CONFIRMATION = "tool_confirmation";
28+
public static final String TOOL_APPROVAL_RESPONSE = "tool_approval_response";
2929
public static final String FUNCTION_CALL_OUTPUT = "function_call_output";
3030
public static final String TOOL_CALL_OUTPUT = "tool_call_output";
3131
public static final String DEFINE_OUTCOME = "define_outcome";
@@ -34,6 +34,7 @@ public static final class SSEEventType {
3434
public static final String REASONING = "reasoning";
3535
public static final String MCP_CALL = "mcp_call";
3636
public static final String MCP_CALL_OUTPUT = "mcp_call_output";
37+
public static final String TOOL_APPROVAL_REQUEST = "tool_approval_request";
3738
public static final String SESSION_STATUS = "session_status";
3839
public static final String ERROR = "error";
3940
public static final String SESSION_UPDATED = "session_updated";
@@ -72,12 +73,30 @@ private BlockType() {}
7273
public static final class SessionStatusValue {
7374
public static final String IDLE = "idle";
7475
public static final String RUNNING = "running";
75-
public static final String RESCHEDULING = "rescheduling";
76+
public static final String RESCHEDULED = "rescheduled";
7677
public static final String TERMINATED = "terminated";
78+
public static final String DELETED = "deleted";
7779

7880
private SessionStatusValue() {}
7981
}
8082

83+
/** {@code stop_reason.type} values carried by {@code session_status} idle events. */
84+
public static final class StopReasonType {
85+
public static final String END_TURN = "end_turn";
86+
public static final String REQUIRES_ACTION = "requires_action";
87+
public static final String RETRIES_EXHAUSTED = "retries_exhausted";
88+
89+
private StopReasonType() {}
90+
}
91+
92+
/** Tool approval policy ({@code permission_policy.type}). */
93+
public static final class PermissionPolicyType {
94+
public static final String ALWAYS_ALLOW = "always_allow";
95+
public static final String ALWAYS_ASK = "always_ask";
96+
97+
private PermissionPolicyType() {}
98+
}
99+
81100
public static String resolveBaseUrl(String workspace, String region) {
82101
String ws = workspace;
83102
if (ws == null || ws.isEmpty()) {

‎src/main/java/com/alibaba/dashscope/agentstudio/message/ClientEvents.java‎

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,22 @@ public static JsonObject userInterrupt(String sessionThreadId) {
7474
return event;
7575
}
7676

77-
public static JsonObject userToolConfirmation(String toolUseId, String result) {
78-
return userToolConfirmation(toolUseId, result, null, null);
79-
}
80-
81-
public static JsonObject userToolConfirmation(
82-
String toolUseId, String result, String denyMessage) {
83-
return userToolConfirmation(toolUseId, result, denyMessage, null);
84-
}
85-
86-
public static JsonObject userToolConfirmation(
87-
String toolUseId, String result, String denyMessage, String sessionThreadId) {
77+
/**
78+
* Submit a tool approval ruling for an {@code always_ask} tool call.
79+
*
80+
* <p>{@code result} must be {@code "allow"} or {@code "deny"}; {@code denyMessage} is optional
81+
* and only meaningful when denying. The approval identity is the {@code (batchId, callId)}
82+
* composite key — {@code callId} may be reused across turns, so never match on it alone. Approval
83+
* responses target the primary thread only (no {@code sessionThreadId}).
84+
*/
85+
public static JsonObject userToolApprovalResponse(
86+
String batchId, String callId, String result, String denyMessage) {
8887
if (!"allow".equals(result) && !"deny".equals(result)) {
89-
throw new IllegalArgumentException("tool_confirmation result must be 'allow' or 'deny'");
88+
throw new IllegalArgumentException("result must be 'allow' or 'deny'");
9089
}
9190
JsonObject data = new JsonObject();
92-
data.addProperty("call_id", toolUseId);
91+
data.addProperty("batch_id", batchId);
92+
data.addProperty("call_id", callId);
9393
data.addProperty("result", result);
9494
if (denyMessage != null && "deny".equals(result)) {
9595
data.addProperty("deny_message", denyMessage);
@@ -100,12 +100,9 @@ public static JsonObject userToolConfirmation(
100100
JsonArray content = new JsonArray();
101101
content.add(dataBlock);
102102
JsonObject event = new JsonObject();
103-
event.addProperty("type", "tool_confirmation");
103+
event.addProperty("type", "tool_approval_response");
104104
event.addProperty("role", "user");
105105
event.add("content", content);
106-
if (sessionThreadId != null) {
107-
event.addProperty("session_thread_id", sessionThreadId);
108-
}
109106
return event;
110107
}
111108

‎src/main/java/com/alibaba/dashscope/agentstudio/message/Message.java‎

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.alibaba.dashscope.utils.JsonUtils;
66
import com.google.gson.JsonObject;
77
import com.google.gson.annotations.SerializedName;
8+
import java.util.HashMap;
89
import java.util.List;
910
import java.util.Map;
1011
import lombok.Data;
@@ -54,14 +55,28 @@ public class Message {
5455
@SerializedName("message")
5556
private String message;
5657

58+
// ── delta-protocol frame fields (opt-in via event_deltas) ──
59+
@SerializedName("error")
60+
private JsonObject errorPayload;
61+
62+
@SerializedName("event")
63+
private JsonObject eventPayload;
64+
65+
@SerializedName("event_id")
66+
private String deltaEventId;
67+
68+
@SerializedName("delta")
69+
private JsonObject deltaPayload;
70+
5771
/**
5872
* Extract stop_reason from a session_status event's data block.
5973
*
6074
* <p>The SSE session_status idle event carries stop_reason in its data: {@code
6175
* {"stop_reason":{"type":"end_turn"},"session_status":"idle"}}. This helper parses it out so
6276
* callers don't need to manually dig through DataContent blocks.
6377
*
64-
* @return stop_reason (has type, eventIds), or null if not a session_status event or absent
78+
* @return stop_reason (has type, pendingBatchId, pendingCallIds), or null if not a session_status
79+
* event or absent
6580
*/
6681
public Session.StopReason getStopReason() {
6782
if (!"session_status".equals(type) || content == null) return null;
@@ -75,4 +90,108 @@ public Session.StopReason getStopReason() {
7590
}
7691
return null;
7792
}
93+
94+
// typed data accessors
95+
96+
/** The first content block's {@code data} payload, for events that carry one. */
97+
public JsonObject getData() {
98+
if (content == null) return null;
99+
for (ContentBlock block : content) {
100+
if (block instanceof ContentBlock.DataContent) {
101+
return ((ContentBlock.DataContent) block).getData();
102+
}
103+
}
104+
return null;
105+
}
106+
107+
/** {@code tool_approval_request} payload: batch_id/call_id/name/arguments/tool_type. */
108+
public JsonObject getToolApprovalRequest() {
109+
if (!"tool_approval_request".equals(type)) return null;
110+
return getData();
111+
}
112+
113+
/** {@code {code, message}} from {@code type=error} events. */
114+
public JsonObject getError() {
115+
if (!"error".equals(type)) return null;
116+
return errorPayload;
117+
}
118+
119+
/** Suspend signal {@code {batch_id, call_ids}} from metadata while the approval barrier is up. */
120+
@SuppressWarnings("unchecked")
121+
public Map<String, Object> getPendingToolApprovals() {
122+
if (metadata == null) return null;
123+
Object pending = metadata.get("pending_tool_approvals");
124+
return pending instanceof Map ? (Map<String, Object>) pending : null;
125+
}
126+
127+
/** {@code model_request_end} usage payload. */
128+
public JsonObject getModelRequestEnd() {
129+
if (!"model_request_end".equals(type)) return null;
130+
return getData();
131+
}
132+
133+
/** {@code outcome_evaluation} payload (outcome_id/iteration/phase/result). */
134+
public JsonObject getOutcomeEvaluation() {
135+
if (!"outcome_evaluation".equals(type)) return null;
136+
return getData();
137+
}
138+
139+
/** {@code thread_status} payload (session_thread_id/agent_name/thread_status/stop_reason). */
140+
public JsonObject getThreadStatus() {
141+
if (!"thread_status".equals(type)) return null;
142+
return getData();
143+
}
144+
145+
/** {@code thread_created} payload (session_thread_id/agent_name). */
146+
public JsonObject getThreadCreated() {
147+
if (!"thread_created".equals(type)) return null;
148+
return getData();
149+
}
150+
151+
/** {@code session_updated} payload (title/session_metadata/agent). */
152+
public JsonObject getSessionUpdated() {
153+
if (!"session_updated".equals(type)) return null;
154+
return getData();
155+
}
156+
157+
/** Sub-agent routing from {@code thread_message_sent}/{@code received} metadata. */
158+
public Map<String, Object> getThreadMessageRouting() {
159+
if (!"thread_message_sent".equals(type) && !"thread_message_received".equals(type)) {
160+
return null;
161+
}
162+
if (metadata == null) return null;
163+
Map<String, Object> routing = new HashMap<>();
164+
for (String key :
165+
new String[] {
166+
"to_session_thread_id", "to_agent_name",
167+
"from_session_thread_id", "from_agent_name"
168+
}) {
169+
if (metadata.containsKey(key)) routing.put(key, metadata.get(key));
170+
}
171+
return routing.isEmpty() ? null : routing;
172+
}
173+
174+
// ── delta-protocol frames (opt-in via event_deltas) ──
175+
176+
/** {@code {id, type}} from {@code event_start} frames (preview, no content). */
177+
public JsonObject getEventStart() {
178+
if (!"event_start".equals(type)) return null;
179+
return eventPayload;
180+
}
181+
182+
/** Delta payload from {@code event_delta} frames. */
183+
public JsonObject getEventDelta() {
184+
if (!"event_delta".equals(type)) return null;
185+
return deltaPayload;
186+
}
187+
188+
/** Incremental text from an {@code event_delta}'s {@code content_delta}. */
189+
public String getDeltaText() {
190+
if (!"event_delta".equals(type) || deltaPayload == null) return null;
191+
if (deltaPayload.has("content") && deltaPayload.get("content").isJsonObject()) {
192+
JsonObject c = deltaPayload.getAsJsonObject("content");
193+
if (c.has("text")) return c.get("text").getAsString();
194+
}
195+
return null;
196+
}
78197
}

‎src/main/java/com/alibaba/dashscope/agentstudio/model/Configs.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ public static class RosterEntry {
9797

9898
@SerializedName("version")
9999
private Integer version;
100+
101+
@SerializedName("name")
102+
private String name;
103+
104+
@SerializedName("description")
105+
private String description;
100106
}
101107
}
108+
109+
@Data
110+
public static class PermissionPolicy {
111+
@SerializedName("type")
112+
private String type;
113+
}
102114
}

‎src/main/java/com/alibaba/dashscope/agentstudio/model/Session.java‎

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,64 @@ public static class StopReason {
101101
@SerializedName("type")
102102
private String type;
103103

104-
@SerializedName("event_ids")
105-
private List<String> eventIds;
104+
@SerializedName("pending_batch_id")
105+
private String pendingBatchId;
106+
107+
@SerializedName("pending_call_ids")
108+
private List<String> pendingCallIds;
109+
}
110+
111+
@Data
112+
@EqualsAndHashCode(callSuper = true)
113+
public static class SessionResource extends FlattenResultBase {
114+
@SerializedName("id")
115+
private String id;
116+
117+
@SerializedName("type")
118+
private String type;
119+
120+
@SerializedName("file_id")
121+
private String fileId;
122+
123+
@SerializedName("mount_path")
124+
private String mountPath;
125+
126+
@SerializedName("created_at")
127+
private String createdAt;
128+
129+
@SerializedName("updated_at")
130+
private String updatedAt;
131+
}
132+
133+
@Data
134+
@EqualsAndHashCode(callSuper = true)
135+
public static class SessionThread extends FlattenResultBase {
136+
@SerializedName("id")
137+
private String id;
138+
139+
@SerializedName("type")
140+
private String type;
141+
142+
@SerializedName("session_id")
143+
private String sessionId;
144+
145+
@SerializedName("parent_thread_id")
146+
private String parentThreadId;
147+
148+
@SerializedName("agent")
149+
private Map<String, Object> agent;
150+
151+
@SerializedName("status")
152+
private String status;
153+
154+
@SerializedName("created_at")
155+
private String createdAt;
156+
157+
@SerializedName("updated_at")
158+
private String updatedAt;
159+
160+
@SerializedName("archived_at")
161+
private String archivedAt;
106162
}
107163

108164
@Data

0 commit comments

Comments
 (0)