mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 05:02:11 +00:00
Merge pull request #238 from rivet-dev/test-dev-webhooks-flow
Fix Foundry handoff creation flow
This commit is contained in:
commit
d30cc0bcc8
8 changed files with 28 additions and 13 deletions
|
|
@ -44,6 +44,11 @@ Use `pnpm` workspaces and Turborepo.
|
||||||
- Stop the preview stack: `just factory-preview-down`
|
- Stop the preview stack: `just factory-preview-down`
|
||||||
- Tail preview logs: `just factory-preview-logs`
|
- Tail preview logs: `just factory-preview-logs`
|
||||||
|
|
||||||
|
## Local Env
|
||||||
|
|
||||||
|
- For local The Foundry dev server setup, keep a personal env copy at `~/misc/the-foundry.env`.
|
||||||
|
- To run the dev server from this workspace, copy that content into the repo root `.env`. Root `.env` is gitignored in this repo, so keep local secrets there and do not commit them.
|
||||||
|
|
||||||
## Frontend + Client Boundary
|
## Frontend + Client Boundary
|
||||||
|
|
||||||
- Keep a browser-friendly GUI implementation aligned with the TUI interaction model wherever possible.
|
- Keep a browser-friendly GUI implementation aligned with the TUI interaction model wherever possible.
|
||||||
|
|
@ -163,6 +168,7 @@ For all Rivet/RivetKit implementation:
|
||||||
- Integration tests use `setupTest()` from `rivetkit/test` and are gated behind `HF_ENABLE_ACTOR_INTEGRATION_TESTS=1`.
|
- Integration tests use `setupTest()` from `rivetkit/test` and are gated behind `HF_ENABLE_ACTOR_INTEGRATION_TESTS=1`.
|
||||||
- End-to-end testing must run against the dev backend started via `docker compose -f compose.dev.yaml up` (host -> container). Do not run E2E against an in-process test runtime.
|
- End-to-end testing must run against the dev backend started via `docker compose -f compose.dev.yaml up` (host -> container). Do not run E2E against an in-process test runtime.
|
||||||
- E2E tests should talk to the backend over HTTP (default `http://127.0.0.1:7741/api/rivet`) and use real GitHub repos/PRs.
|
- E2E tests should talk to the backend over HTTP (default `http://127.0.0.1:7741/api/rivet`) and use real GitHub repos/PRs.
|
||||||
|
- Current org test repo: `rivet-dev/sandbox-agent-testing` (`https://github.com/rivet-dev/sandbox-agent-testing`).
|
||||||
- Secrets (e.g. `OPENAI_API_KEY`, `GITHUB_TOKEN`/`GH_TOKEN`) must be provided via environment variables, never hardcoded in the repo.
|
- Secrets (e.g. `OPENAI_API_KEY`, `GITHUB_TOKEN`/`GH_TOKEN`) must be provided via environment variables, never hardcoded in the repo.
|
||||||
- Treat client E2E tests in `packages/client/test` as the primary end-to-end source of truth for product behavior.
|
- Treat client E2E tests in `packages/client/test` as the primary end-to-end source of truth for product behavior.
|
||||||
- Keep backend tests small and targeted. Only retain backend-only tests for invariants or persistence rules that are not well-covered through client E2E.
|
- Keep backend tests small and targeted. Only retain backend-only tests for invariants or persistence rules that are not well-covered through client E2E.
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ export interface HandoffInput {
|
||||||
agentType: AgentType | null;
|
agentType: AgentType | null;
|
||||||
explicitTitle: string | null;
|
explicitTitle: string | null;
|
||||||
explicitBranchName: string | null;
|
explicitBranchName: string | null;
|
||||||
|
initialPrompt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InitializeCommand {
|
interface InitializeCommand {
|
||||||
|
|
@ -125,6 +126,7 @@ export const handoff = actor({
|
||||||
agentType: input.agentType,
|
agentType: input.agentType,
|
||||||
explicitTitle: input.explicitTitle,
|
explicitTitle: input.explicitTitle,
|
||||||
explicitBranchName: input.explicitBranchName,
|
explicitBranchName: input.explicitBranchName,
|
||||||
|
initialPrompt: input.initialPrompt,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
previousStatus: null as string | null,
|
previousStatus: null as string | null,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,10 @@ export async function initCreateSessionActivity(loopCtx: any, body: any, sandbox
|
||||||
const cwd = sandbox.metadata && typeof (sandbox.metadata as any).cwd === "string" ? ((sandbox.metadata as any).cwd as string) : undefined;
|
const cwd = sandbox.metadata && typeof (sandbox.metadata as any).cwd === "string" ? ((sandbox.metadata as any).cwd as string) : undefined;
|
||||||
|
|
||||||
return await sandboxInstance.createSession({
|
return await sandboxInstance.createSession({
|
||||||
prompt: buildAgentPrompt(loopCtx.state.task),
|
prompt:
|
||||||
|
typeof loopCtx.state.initialPrompt === "string"
|
||||||
|
? loopCtx.state.initialPrompt
|
||||||
|
: buildAgentPrompt(loopCtx.state.task),
|
||||||
cwd,
|
cwd,
|
||||||
agent: (loopCtx.state.agentType ?? config.default_agent) as any,
|
agent: (loopCtx.state.agentType ?? config.default_agent) as any,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ interface CreateHandoffCommand {
|
||||||
agentType: AgentType | null;
|
agentType: AgentType | null;
|
||||||
explicitTitle: string | null;
|
explicitTitle: string | null;
|
||||||
explicitBranchName: string | null;
|
explicitBranchName: string | null;
|
||||||
|
initialPrompt: string | null;
|
||||||
onBranch: string | null;
|
onBranch: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,6 +366,7 @@ async function createHandoffMutation(c: any, cmd: CreateHandoffCommand): Promise
|
||||||
agentType: cmd.agentType,
|
agentType: cmd.agentType,
|
||||||
explicitTitle: onBranch ? null : cmd.explicitTitle,
|
explicitTitle: onBranch ? null : cmd.explicitTitle,
|
||||||
explicitBranchName: onBranch ? null : cmd.explicitBranchName,
|
explicitBranchName: onBranch ? null : cmd.explicitBranchName,
|
||||||
|
initialPrompt: cmd.initialPrompt,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (onBranch) {
|
if (onBranch) {
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,7 @@ async function createHandoffMutation(c: any, input: CreateHandoffInput): Promise
|
||||||
agentType: input.agentType ?? null,
|
agentType: input.agentType ?? null,
|
||||||
explicitTitle: input.explicitTitle ?? null,
|
explicitTitle: input.explicitTitle ?? null,
|
||||||
explicitBranchName: input.explicitBranchName ?? null,
|
explicitBranchName: input.explicitBranchName ?? null,
|
||||||
|
initialPrompt: input.initialPrompt ?? null,
|
||||||
onBranch: input.onBranch ?? null,
|
onBranch: input.onBranch ?? null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -299,9 +300,10 @@ async function createHandoffMutation(c: any, input: CreateHandoffInput): Promise
|
||||||
|
|
||||||
const handoff = getHandoff(c, c.state.workspaceId, repoId, created.handoffId);
|
const handoff = getHandoff(c, c.state.workspaceId, repoId, created.handoffId);
|
||||||
await handoff.provision({ providerId });
|
await handoff.provision({ providerId });
|
||||||
|
const provisioned = await handoff.get();
|
||||||
|
|
||||||
await workspaceActions.notifyWorkbenchUpdated(c);
|
await workspaceActions.notifyWorkbenchUpdated(c);
|
||||||
return created;
|
return provisioned;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshProviderProfilesMutation(c: any, command?: RefreshProviderProfilesCommand): Promise<void> {
|
async function refreshProviderProfilesMutation(c: any, command?: RefreshProviderProfilesCommand): Promise<void> {
|
||||||
|
|
@ -437,16 +439,20 @@ export const workspaceActions = {
|
||||||
c.broadcast("workbenchUpdated", { at: Date.now() });
|
c.broadcast("workbenchUpdated", { at: Date.now() });
|
||||||
},
|
},
|
||||||
|
|
||||||
async createWorkbenchHandoff(c: any, input: HandoffWorkbenchCreateHandoffInput): Promise<{ handoffId: string }> {
|
async createWorkbenchHandoff(c: any, input: HandoffWorkbenchCreateHandoffInput): Promise<{ handoffId: string; tabId?: string }> {
|
||||||
const created = await workspaceActions.createHandoff(c, {
|
const created = await workspaceActions.createHandoff(c, {
|
||||||
workspaceId: c.state.workspaceId,
|
workspaceId: c.state.workspaceId,
|
||||||
repoId: input.repoId,
|
repoId: input.repoId,
|
||||||
task: input.task,
|
task: input.task,
|
||||||
...(input.title ? { explicitTitle: input.title } : {}),
|
...(input.title ? { explicitTitle: input.title } : {}),
|
||||||
...(input.branch ? { explicitBranchName: input.branch } : {}),
|
...(input.branch ? { explicitBranchName: input.branch } : {}),
|
||||||
|
...(input.initialPrompt !== undefined ? { initialPrompt: input.initialPrompt } : {}),
|
||||||
...(input.model ? { agentType: agentTypeForModel(input.model) } : {}),
|
...(input.model ? { agentType: agentTypeForModel(input.model) } : {}),
|
||||||
});
|
});
|
||||||
return { handoffId: created.handoffId };
|
return {
|
||||||
|
handoffId: created.handoffId,
|
||||||
|
...(created.activeSessionId ? { tabId: created.activeSessionId } : {}),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async markWorkbenchUnread(c: any, input: HandoffWorkbenchSelectInput): Promise<void> {
|
async markWorkbenchUnread(c: any, input: HandoffWorkbenchSelectInput): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -949,19 +949,13 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
|
||||||
throw new Error("Cannot create a task without an available repo");
|
throw new Error("Cannot create a task without an available repo");
|
||||||
}
|
}
|
||||||
|
|
||||||
const task = window.prompt("Describe the task", "Investigate and implement the requested change");
|
const task = "New task";
|
||||||
if (!task) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = window.prompt("Optional task title", "")?.trim() || undefined;
|
|
||||||
const branch = window.prompt("Optional branch name", "")?.trim() || undefined;
|
|
||||||
const { handoffId, tabId } = await handoffWorkbenchClient.createHandoff({
|
const { handoffId, tabId } = await handoffWorkbenchClient.createHandoff({
|
||||||
repoId,
|
repoId,
|
||||||
task,
|
task,
|
||||||
|
title: task,
|
||||||
model: "gpt-4o",
|
model: "gpt-4o",
|
||||||
...(title ? { title } : {}),
|
initialPrompt: "",
|
||||||
...(branch ? { branch } : {}),
|
|
||||||
});
|
});
|
||||||
await navigate({
|
await navigate({
|
||||||
to: "/workspaces/$workspaceId/handoffs/$handoffId",
|
to: "/workspaces/$workspaceId/handoffs/$handoffId",
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ export const CreateHandoffInputSchema = z.object({
|
||||||
task: z.string().min(1),
|
task: z.string().min(1),
|
||||||
explicitTitle: z.string().trim().min(1).optional(),
|
explicitTitle: z.string().trim().min(1).optional(),
|
||||||
explicitBranchName: z.string().trim().min(1).optional(),
|
explicitBranchName: z.string().trim().min(1).optional(),
|
||||||
|
initialPrompt: z.string().optional(),
|
||||||
providerId: ProviderIdSchema.optional(),
|
providerId: ProviderIdSchema.optional(),
|
||||||
agentType: AgentTypeSchema.optional(),
|
agentType: AgentTypeSchema.optional(),
|
||||||
onBranch: z.string().trim().min(1).optional(),
|
onBranch: z.string().trim().min(1).optional(),
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ export interface HandoffWorkbenchCreateHandoffInput {
|
||||||
title?: string;
|
title?: string;
|
||||||
branch?: string;
|
branch?: string;
|
||||||
model?: WorkbenchModelId;
|
model?: WorkbenchModelId;
|
||||||
|
initialPrompt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HandoffWorkbenchRenameInput {
|
export interface HandoffWorkbenchRenameInput {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue