Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 2 additions & 3 deletions .agents/skills/best-practices/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ Instead, actually rename the type and fix all occurrences if needed.

## Result Types

- Use existing Result types from the codebase (`ToolResult`, `ActionResult`, `Result`) instead of creating custom ones.
- Return `ToolResult.ok({ message, ...data })` or `ToolResult.err(message, detail?)` for agent tools.
- Return `ActionResult.ok(message)` or `ActionResult.err(message, detail?)` for float actions.
- Use existing Result types from the codebase (`ToolResult`, `Result`) instead of creating custom ones.
- Return `ToolResult.ok({ message, ...data })` or `ToolResult.err(message, detail?)` for agent tools and float actions.
- Return `Result.ok(value)` or `Result.err(error)` for normal functions that need proper safe error handling.
- Never throw exceptions in tool/action implementations; always return Result types.
6 changes: 3 additions & 3 deletions .agents/skills/shift/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ description: Agent Tools and Float Actions Best Practices

## Float Actions (`packages/frontend/src/float/actions`)

- Use `ActionResult` type from `@/float/types`.
- Return `ActionResult.ok(message)` or `ActionResult.err(message, detail?)`.
- Use `ActionResult.schema` as the output schema (no arguments needed).
- Use `ToolResult` from `@/agent/types`, same as agent tools.
- Return `ToolResult.ok({ message })` or `ToolResult.err(message, detail?)`.
- Use `ToolResult.schema()` as the output schema.
- Get SDK via `const { sdk } = experimental_context as FloatToolContext`.

## User-Facing Messages
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
"shared": "workspace:*"
},
"devDependencies": {
"@caido/sdk-backend": "0.58.3"
"@caido/sdk-backend": "0.59.0"
}
}
7 changes: 4 additions & 3 deletions packages/backend/src/api/agent-binaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
type Result as ResultType,
} from "shared";

import { getCustomAgentsStore } from "../stores";
import { customAgentsStore } from "../stores";
import type { BackendSDK } from "../types";

import {
Expand Down Expand Up @@ -172,8 +172,9 @@ export async function executeAgentBinary(

const { executionId, agentId, binaryPath, args, stdin } = parsed.data;

const store = getCustomAgentsStore();
const agent = store.getAgentDefinitions().find((candidate) => candidate.id === agentId);
const agent = customAgentsStore
.getAgentDefinitions()
.find((candidate) => candidate.id === agentId);
if (agent === undefined) {
return Result.err("Agent not found");
}
Expand Down
42 changes: 15 additions & 27 deletions packages/backend/src/api/agents.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import {
type Result,
type ShiftMessage,
type StoredAgent,
type StoredAgentSessionState,
} from "shared";
import { Result, type ShiftMessage, type StoredAgent, type StoredAgentSessionState } from "shared";

import { getAgentsStore } from "../stores";
import { agentsStore } from "../stores";
import { type BackendSDK } from "../types";

function normalizeMessages(messages: ShiftMessage[]): ShiftMessage[] {
Expand All @@ -26,46 +21,39 @@ function normalizeMessages(messages: ShiftMessage[]): ShiftMessage[] {

export function getAgent(_sdk: BackendSDK, chatID: string): Result<StoredAgent | undefined> {
try {
const store = getAgentsStore();
const agent = store.getAgent(chatID);
return { kind: "Ok", value: agent };
return Result.ok(agentsStore.getAgent(chatID));
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export function getAgents(_sdk: BackendSDK): Result<StoredAgent[]> {
try {
const store = getAgentsStore();
const agents = store.getAgents();
return { kind: "Ok", value: agents };
return Result.ok(agentsStore.getAgents());
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export function writeAgent(
export async function writeAgent(
_sdk: BackendSDK,
chatID: string,
messages: ShiftMessage[],
sessionState: StoredAgentSessionState | undefined
): Result<void> {
): Promise<Result<void>> {
try {
const store = getAgentsStore();
const normalizedMessages = normalizeMessages(messages);
store.writeAgent(chatID, normalizedMessages, sessionState);
return { kind: "Ok", value: undefined };
await agentsStore.writeAgent(chatID, normalizeMessages(messages), sessionState);
return Result.ok(undefined);
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export function removeAgent(_sdk: BackendSDK, chatID: string): Result<void> {
export async function removeAgent(_sdk: BackendSDK, chatID: string): Promise<Result<void>> {
try {
const store = getAgentsStore();
store.removeAgent(chatID);
return { kind: "Ok", value: undefined };
await agentsStore.removeAgent(chatID);
return Result.ok(undefined);
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}
46 changes: 21 additions & 25 deletions packages/backend/src/api/custom-agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,72 @@ import {
type CreateCustomAgentInput,
CreateCustomAgentSchema,
type CustomAgent,
type CustomAgentsState,
type ResolvedCustomAgent,
type Result,
Result,
type UpdateCustomAgentInput,
UpdateCustomAgentSchema,
} from "shared";

import { getCustomAgentsStore } from "../stores";
import { customAgentsStore } from "../stores";
import type { BackendSDK } from "../types";

export function getResolvedCustomAgents(_sdk: BackendSDK): Result<ResolvedCustomAgent[]> {
try {
const store = getCustomAgentsStore();
const agents = store.getResolvedAgents();
return { kind: "Ok", value: agents };
return Result.ok(customAgentsStore.getResolvedAgents());
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export function getCustomAgentDefinitions(_sdk: BackendSDK): Result<CustomAgent[]> {
try {
const store = getCustomAgentsStore();
const definitions = store.getAgentDefinitions();
return { kind: "Ok", value: definitions };
return Result.ok(customAgentsStore.getAgentDefinitions());
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function addCustomAgent(
_sdk: BackendSDK,
input: CreateCustomAgentInput
): Promise<Result<string>> {
): Promise<Result<CustomAgentsState>> {
try {
const parsed = CreateCustomAgentSchema.safeParse(input);
if (!parsed.success) {
return { kind: "Error", error: parsed.error.message };
return Result.err(parsed.error.message);
}

const store = getCustomAgentsStore();
return await store.addAgent(parsed.data);
return await customAgentsStore.addAgent(parsed.data);
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function updateCustomAgent(
_sdk: BackendSDK,
id: string,
updates: UpdateCustomAgentInput
): Promise<Result<void>> {
): Promise<Result<CustomAgentsState>> {
try {
const parsed = UpdateCustomAgentSchema.safeParse(updates);
if (!parsed.success) {
return { kind: "Error", error: parsed.error.message };
return Result.err(parsed.error.message);
}

const store = getCustomAgentsStore();
return await store.updateAgent(id, parsed.data);
return await customAgentsStore.updateAgent(id, parsed.data);
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function removeCustomAgent(_sdk: BackendSDK, id: string): Promise<Result<void>> {
export async function removeCustomAgent(
_sdk: BackendSDK,
id: string
): Promise<Result<CustomAgentsState>> {
try {
const store = getCustomAgentsStore();
await store.removeAgent(id);
return { kind: "Ok", value: undefined };
return Result.ok(await customAgentsStore.removeAgent(id));
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}
64 changes: 22 additions & 42 deletions packages/backend/src/api/learnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,74 @@ import {
type LearningsConfig,
type RemoveLearningsInput,
RemoveLearningsSchema,
type Result,
Result,
type UpdateLearningInput,
UpdateLearningSchema,
} from "shared";

import { getLearningsStore } from "../stores";
import { learningsStore } from "../stores";
import type { BackendSDK } from "../types";

export function getLearnings(_sdk: BackendSDK): Result<LearningsConfig> {
try {
const store = getLearningsStore();
const learnings = store.getLearnings();
return { kind: "Ok", value: learnings };
return Result.ok(learningsStore.getLearnings());
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function addLearning(
_sdk: BackendSDK,
input: AddLearningInput
): Promise<Result<void>> {
): Promise<Result<LearningsConfig>> {
try {
const parsed = AddLearningSchema.safeParse(input);
if (parsed.success === false) {
return { kind: "Error", error: parsed.error.message };
if (!parsed.success) {
return Result.err(parsed.error.message);
}

const store = getLearningsStore();
await store.addLearning(parsed.data.content);
return { kind: "Ok", value: undefined };
return Result.ok(await learningsStore.addLearning(parsed.data.content));
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function updateLearning(
_sdk: BackendSDK,
input: UpdateLearningInput
): Promise<Result<void>> {
): Promise<Result<LearningsConfig>> {
try {
const parsed = UpdateLearningSchema.safeParse(input);
if (parsed.success === false) {
return { kind: "Error", error: parsed.error.message };
if (!parsed.success) {
return Result.err(parsed.error.message);
}

const store = getLearningsStore();
await store.updateLearning(parsed.data.index, parsed.data.content);
return { kind: "Ok", value: undefined };
return Result.ok(await learningsStore.updateLearning(parsed.data.index, parsed.data.content));
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function removeLearnings(
_sdk: BackendSDK,
input: RemoveLearningsInput
): Promise<Result<void>> {
): Promise<Result<LearningsConfig>> {
try {
const parsed = RemoveLearningsSchema.safeParse(input);
if (parsed.success === false) {
return { kind: "Error", error: parsed.error.message };
if (!parsed.success) {
return Result.err(parsed.error.message);
}

const store = getLearningsStore();
await store.removeLearnings(parsed.data.indexes);
return { kind: "Ok", value: undefined };
return Result.ok(await learningsStore.removeLearnings(parsed.data.indexes));
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}

export async function setLearnings(_sdk: BackendSDK, entries: string[]): Promise<Result<void>> {
export async function clearLearnings(_sdk: BackendSDK): Promise<Result<LearningsConfig>> {
try {
const store = getLearningsStore();
await store.setLearnings(entries);
return { kind: "Ok", value: undefined };
return Result.ok(await learningsStore.clearLearnings());
} catch (error) {
return { kind: "Error", error: (error as Error).message };
}
}

export async function clearLearnings(_sdk: BackendSDK): Promise<Result<void>> {
try {
const store = getLearningsStore();
await store.clearLearnings();
return { kind: "Ok", value: undefined };
} catch (error) {
return { kind: "Error", error: (error as Error).message };
return Result.err((error as Error).message);
}
}
Loading
Loading