2026-04-28 09:22:19 -07:00
|
|
|
import { z } from "zod";
|
|
|
|
|
import { baseApiService } from "./base-api.service";
|
|
|
|
|
|
|
|
|
|
const AgentActionReadSchema = z.object({
|
|
|
|
|
id: z.number(),
|
|
|
|
|
thread_id: z.number(),
|
|
|
|
|
user_id: z.string().nullable(),
|
|
|
|
|
search_space_id: z.number(),
|
|
|
|
|
tool_name: z.string(),
|
|
|
|
|
args: z.record(z.string(), z.unknown()).nullable(),
|
|
|
|
|
result_id: z.string().nullable(),
|
|
|
|
|
reversible: z.boolean(),
|
|
|
|
|
reverse_descriptor: z.record(z.string(), z.unknown()).nullable(),
|
|
|
|
|
error: z.record(z.string(), z.unknown()).nullable(),
|
|
|
|
|
reverse_of: z.number().nullable(),
|
|
|
|
|
reverted_by_action_id: z.number().nullable(),
|
|
|
|
|
is_revert_action: z.boolean(),
|
2026-04-29 07:20:31 -07:00
|
|
|
// Correlation ids added in migration 135. The LangChain
|
|
|
|
|
// ``tool_call_id`` joins this row to the chat tool card via the
|
|
|
|
|
// ``data-action-log.lc_tool_call_id`` SSE event, and
|
|
|
|
|
// ``chat_turn_id`` keys the per-turn revert endpoint.
|
|
|
|
|
tool_call_id: z.string().nullable().optional(),
|
|
|
|
|
chat_turn_id: z.string().nullable().optional(),
|
2026-04-28 09:22:19 -07:00
|
|
|
created_at: z.string(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type AgentAction = z.infer<typeof AgentActionReadSchema>;
|
|
|
|
|
|
|
|
|
|
const AgentActionListResponseSchema = z.object({
|
|
|
|
|
items: z.array(AgentActionReadSchema),
|
|
|
|
|
total: z.number(),
|
|
|
|
|
page: z.number(),
|
|
|
|
|
page_size: z.number(),
|
|
|
|
|
has_more: z.boolean(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type AgentActionListResponse = z.infer<typeof AgentActionListResponseSchema>;
|
|
|
|
|
|
|
|
|
|
const RevertResponseSchema = z.object({
|
|
|
|
|
status: z.literal("ok"),
|
|
|
|
|
message: z.string(),
|
|
|
|
|
new_action_id: z.number().nullable().optional(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type RevertResponse = z.infer<typeof RevertResponseSchema>;
|
|
|
|
|
|
2026-04-29 07:20:31 -07:00
|
|
|
// Per-turn batch revert. The route never returns whole-batch 4xx;
|
|
|
|
|
// partial success is the common case and surfaced as
|
|
|
|
|
// ``status === "partial"`` with a per-action result list.
|
|
|
|
|
const RevertTurnActionResultSchema = z.object({
|
|
|
|
|
action_id: z.number(),
|
|
|
|
|
tool_name: z.string(),
|
|
|
|
|
status: z.enum([
|
|
|
|
|
"reverted",
|
|
|
|
|
"already_reverted",
|
|
|
|
|
"not_reversible",
|
|
|
|
|
"permission_denied",
|
|
|
|
|
"failed",
|
|
|
|
|
"skipped",
|
|
|
|
|
]),
|
|
|
|
|
message: z.string().nullable().optional(),
|
|
|
|
|
new_action_id: z.number().nullable().optional(),
|
|
|
|
|
error: z.string().nullable().optional(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type RevertTurnActionResult = z.infer<typeof RevertTurnActionResultSchema>;
|
|
|
|
|
|
|
|
|
|
const RevertTurnResponseSchema = z.object({
|
|
|
|
|
status: z.enum(["ok", "partial"]),
|
|
|
|
|
chat_turn_id: z.string(),
|
|
|
|
|
total: z.number(),
|
|
|
|
|
reverted: z.number(),
|
|
|
|
|
already_reverted: z.number(),
|
|
|
|
|
not_reversible: z.number(),
|
|
|
|
|
// ``permission_denied`` and ``skipped`` are first-class counters so
|
|
|
|
|
// ``total === reverted + already_reverted +
|
|
|
|
|
// not_reversible + permission_denied + failed + skipped`` always
|
|
|
|
|
// holds. ``.default(0)`` keeps the schema backwards-compatible
|
|
|
|
|
// with older deployments that haven't shipped the response model
|
|
|
|
|
// update yet.
|
|
|
|
|
permission_denied: z.number().default(0),
|
|
|
|
|
failed: z.number(),
|
|
|
|
|
skipped: z.number().default(0),
|
|
|
|
|
results: z.array(RevertTurnActionResultSchema),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type RevertTurnResponse = z.infer<typeof RevertTurnResponseSchema>;
|
|
|
|
|
|
2026-04-28 09:22:19 -07:00
|
|
|
class AgentActionsApiService {
|
|
|
|
|
listForThread = async (
|
|
|
|
|
threadId: number,
|
|
|
|
|
opts: { page?: number; pageSize?: number } = {}
|
|
|
|
|
): Promise<AgentActionListResponse> => {
|
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
|
params.set("page", String(opts.page ?? 0));
|
|
|
|
|
params.set("page_size", String(opts.pageSize ?? 50));
|
|
|
|
|
return baseApiService.get(
|
|
|
|
|
`/api/v1/threads/${threadId}/actions?${params.toString()}`,
|
|
|
|
|
AgentActionListResponseSchema
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
revert = async (threadId: number, actionId: number): Promise<RevertResponse> => {
|
|
|
|
|
return baseApiService.post(
|
|
|
|
|
`/api/v1/threads/${threadId}/revert/${actionId}`,
|
|
|
|
|
RevertResponseSchema,
|
|
|
|
|
{ body: {} }
|
|
|
|
|
);
|
|
|
|
|
};
|
2026-04-29 07:20:31 -07:00
|
|
|
|
|
|
|
|
revertTurn = async (threadId: number, chatTurnId: string): Promise<RevertTurnResponse> => {
|
|
|
|
|
return baseApiService.post(
|
|
|
|
|
`/api/v1/threads/${threadId}/revert-turn/${encodeURIComponent(chatTurnId)}`,
|
|
|
|
|
RevertTurnResponseSchema,
|
|
|
|
|
{ body: {} }
|
|
|
|
|
);
|
|
|
|
|
};
|
2026-04-28 09:22:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const agentActionsApiService = new AgentActionsApiService();
|