322 lines · 9.8 KB · TypeScript
RawDownload
import { Effect, Schema } from "effect";import {  SessionGoalResponseSchema,  type SessionGoal,  type SessionGoalPatch,} from "@shared/agent/session-goal";import type { SessionUsageTotals } from "@shared/agent/session-summary";import { requestDecodedEffect } from "@/lib/api/request-json";import {  parseAgentTurnCommandResult,  type AgentTurnCommandResult,} from "@/features/agent/messages";import type { AgentQueueAction, AgentToolAccess } from "@shared/agent/agent-turn";import type { AgentImageInput } from "@shared/agent/agent-image-input";import type { BrowserBackend } from "@/features/agent/tools/types";import type {  ComposerPromptTemplateRef,  ComposerSkillRef,} from "@/features/agent/composer-context"; import {  RuntimeActivityPayloadSchema,  RuntimeStatusSchema,  RuntimeStatusResponseSchema,  type RuntimeActivityPayload,  type RuntimeContextUsage,  type RuntimeSessionSummary,  type RuntimeStatus,} from "@shared/agent/runtime-status";export type { RuntimeActivityPayload, RuntimeContextUsage, RuntimeSessionSummary, RuntimeStatus };export type { SessionUsageTotals }; const decodeActivityOption = Schema.decodeUnknownOption(RuntimeActivityPayloadSchema, {  onExcessProperty: "preserve",});const decodeStatusResponseOption = Schema.decodeUnknownOption(RuntimeStatusResponseSchema, {  onExcessProperty: "preserve",}); function decodeRuntimeActivityPayload(raw: unknown): RuntimeActivityPayload | null {  const option = decodeActivityOption(raw);  return option._tag === "Some" ? option.value : null;} function decodeRuntimeStatusResponse(raw: unknown): RuntimeStatus | null {  const option = decodeStatusResponseOption(raw);  return option._tag === "Some" ? (option.value.status ?? null) : null;} const AbortSessionResponseSchema = Schema.Struct({  ok: Schema.Boolean,  cleared: Schema.Struct({    steering: Schema.Array(Schema.String),    followUp: Schema.Array(Schema.String),  }),  status: RuntimeStatusSchema,}); const decodeAbortSessionResponse = Schema.decodeUnknownOption(AbortSessionResponseSchema, {  onExcessProperty: "preserve",}); export type AbortSessionResult = {  steering: string[];  followUp: string[];  status?: RuntimeStatus;}; export function parseAbortSessionResult(input: unknown): AbortSessionResult {  const decoded = decodeAbortSessionResponse(input);  return decoded._tag === "Some"    ? {        steering: [...decoded.value.cleared.steering],        followUp: [...decoded.value.cleared.followUp],        status: decoded.value.status,      }    : { steering: [], followUp: [] };} export function loadRuntimeStatus(  sessionId: string,  piSessionId?: string | null,): Promise<RuntimeStatus | null> {  const params = new URLSearchParams({ sessionId });  if (piSessionId) params.set("piSessionId", piSessionId);  return Effect.runPromise(    requestDecodedEffect(      `/api/agent/runtime/status?${params.toString()}`,      decodeRuntimeStatusResponse,      { cache: "no-store" },    ).pipe(Effect.catch(() => Effect.succeed(null))),  );} export function abortSession(  sessionId: string,  piSessionId?: string | null,): Promise<AbortSessionResult> {  return Effect.runPromise(    requestDecodedEffect("/api/agent/abort", parseAbortSessionResult, {      method: "POST",      headers: { "Content-Type": "application/json" },      body: JSON.stringify({ sessionId, piSessionId }),    }).pipe(Effect.catch(() => Effect.succeed({ steering: [], followUp: [] }))),  );} export function respondExtensionUi(  sessionId: string,  requestId: string,  response: { value?: string; confirmed?: boolean; cancelled?: boolean },): Promise<void> {  return Effect.runPromise(    requestDecodedEffect(      "/api/agent/runtime/extension-ui",      () => undefined,      {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify({ sessionId, requestId, ...response }),      },      "Extension response was rejected",    ),  );} export type CanonicalSessionMeta = {  title: string | null;  modelId: string | null;  startedAt: string | null;  piSessionId: string | null;  usage?: SessionUsageTotals | null;}; export type CanonicalSessionResult = {  messages: import("@/features/agent/messages").ChatMessage[];  tokenStats?: import("@/features/agent/messages").TokenStats;  // Byte-offset cursor to pass as `before` to load the previous (older) page,  // or null when this page already reaches the start of the session log.  cursor: number | null;  // Session metadata from a head-scan; present on an initial tail load only.  meta: CanonicalSessionMeta | null;}; // Default page size for the initial tail load — enough to fill a long scrollback// while keeping a giant log from being read/parsed whole.export const DEFAULT_SESSION_TAIL = 500; export type LoadCanonicalSessionOptions = { tail?: number; before?: number }; export function loadCanonicalSession(  piSessionId: string,  cwd: string,  options: LoadCanonicalSessionOptions = {},): Promise<CanonicalSessionResult> {  const params = new URLSearchParams({ cwd });  const tail = options.before === undefined ? (options.tail ?? DEFAULT_SESSION_TAIL) : undefined;  if (tail !== undefined) params.set("tail", String(tail));  if (options.before !== undefined) params.set("before", String(options.before));  return Effect.runPromise(    requestDecodedEffect(      `/api/agent/sessions/${encodeURIComponent(piSessionId)}?${params.toString()}`,      (input) => {        const payload = input as Partial<CanonicalSessionResult>;        return {          messages: payload.messages ?? [],          tokenStats: payload.tokenStats,          cursor: payload.cursor ?? null,          meta: payload.meta ?? null,        };      },      { cache: "no-store" },      "Failed to load session",    ),  );} export type CompactSessionArgs = {  sessionId: string;  modelId: string;  thinkingLevel?: import("@shared/agent/agent-turn").AgentThinkingLevel;  toolAccess?: AgentToolAccess;  cwd?: string;  piSessionId?: string | null;  browserToolEnabled: boolean;  browserSessionId?: string;  browserBackend?: BrowserBackend;  skills: ComposerSkillRef[];  promptTemplates?: ComposerPromptTemplateRef[];}; export type CompactSessionResult = {  status?: RuntimeStatus;}; export function compactSession(args: CompactSessionArgs): Promise<CompactSessionResult> {  return Effect.runPromise(    requestDecodedEffect(      "/api/agent/compact",      (input) => input as CompactSessionResult,      {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify(args),      },      "Compaction failed",    ),  );} export type SubmitTurnArgs = {  sessionId: string;  modelId: string;  thinkingLevel?: import("@shared/agent/agent-turn").AgentThinkingLevel;  toolAccess: AgentToolAccess;  message: string;  images?: AgentImageInput[];  cwd?: string;  piSessionId?: string | null;  /** Control mode for steer/follow-up; omitted for a normal prompt. */  mode?: "steer" | "follow_up";  queueAction?: AgentQueueAction;  queueReplacement?: string;  browserToolEnabled: boolean;  browserSessionId?: string;  browserBackend?: BrowserBackend;  skills: ComposerSkillRef[];  promptTemplates?: ComposerPromptTemplateRef[];}; export function submitTurnCommand(args: SubmitTurnArgs): Promise<AgentTurnCommandResult> {  return Effect.runPromise(    requestDecodedEffect(      "/api/agent/turn",      (input) => {        const parsed = parseAgentTurnCommandResult(input);        if (!parsed) throw new Error("Agent response was invalid");        if (parsed.outcome === "rejected")          throw new Error(parsed.error || "Agent request was rejected");        return parsed;      },      {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify(args),      },      "Agent request failed",    ),  );} export type RuntimeActivitySubscription = { close: () => void }; export function subscribeRuntimeActivity(  onPayload: (payload: RuntimeActivityPayload) => void,): RuntimeActivitySubscription {  const source = new EventSource("/api/agent/runtime/activity");  source.onmessage = (event) => {    let parsed: unknown;    try {      parsed = JSON.parse(event.data);    } catch {      return;    }    const payload = decodeRuntimeActivityPayload(parsed);    if (!payload) return;    onPayload(payload);  };  return {    close: () => source.close(),  };} const decodeSessionGoalResponseOption = Schema.decodeUnknownOption(SessionGoalResponseSchema, {  onExcessProperty: "preserve",}); function decodeSessionGoal(raw: unknown): SessionGoal | null {  if (!raw || typeof raw !== "object") return null;  const option = decodeSessionGoalResponseOption(raw);  return option._tag === "Some" ? option.value.goal : null;} const sessionGoalUrl = (piSessionId: string) =>  `/api/agent/goal?piSessionId=${encodeURIComponent(piSessionId)}`; export function loadSessionGoal(piSessionId: string): Promise<SessionGoal | null> {  return Effect.runPromise(    requestDecodedEffect(sessionGoalUrl(piSessionId), decodeSessionGoal, {      cache: "no-store",    }).pipe(Effect.catch(() => Effect.succeed(null))),  );} export function updateSessionGoal(  piSessionId: string,  patch: SessionGoalPatch,): Promise<SessionGoal | null> {  return Effect.runPromise(    requestDecodedEffect(      sessionGoalUrl(piSessionId),      decodeSessionGoal,      {        method: "PUT",        headers: { "Content-Type": "application/json" },        body: JSON.stringify(patch),      },      "Failed to update the goal",    ),  );} export function clearSessionGoal(piSessionId: string): Promise<void> {  return Effect.runPromise(    requestDecodedEffect(      sessionGoalUrl(piSessionId),      () => undefined,      { method: "DELETE" },      "Failed to clear the goal",    ),  );}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t