tool for writing files

This commit is contained in:
James Long
2026-05-15 15:33:00 -04:00
parent 63abe037d8
commit 816f5e526b
4 changed files with 41 additions and 1 deletions
@@ -300,6 +300,14 @@ function createServer(options: Options) {
},
async (input) => toolResult(await control(options, "POST", "/experimental/simulation/filesystem/seed", input)),
)
server.registerTool(
"simulation_control_filesystem_write",
{
description: "Write one file into the backend simulated filesystem.",
inputSchema: z.object({ path: z.string(), content: FileContentSchema }),
},
async (input) => toolResult(await control(options, "POST", "/experimental/simulation/filesystem/write", input)),
)
server.registerTool(
"simulation_control_network_register",
{
@@ -28,6 +28,15 @@ export const simulationRoute = HttpRouter.use((router) =>
),
)
yield* router.add("POST", "/experimental/simulation/filesystem/write", () =>
json(
Effect.gen(function* () {
const input = yield* HttpServerRequest.schemaBodyJson(Simulation.FilesystemWriteInput)
return yield* simulation.writeFile(input)
}),
),
)
yield* router.add("POST", "/experimental/simulation/network/register", () =>
json(
Effect.gen(function* () {
@@ -12,6 +12,11 @@ export const FilesystemSeedInput = Schema.Struct({
files: Schema.Record(Schema.String, FileContent),
})
export const FilesystemWriteInput = Schema.Struct({
path: Schema.String,
content: FileContent,
})
export const NetworkRegisterInput = Schema.Union([
Schema.Struct({
kind: Schema.Literal("json"),
@@ -80,6 +85,7 @@ export class SimulationLLMError extends Schema.TaggedErrorClass<SimulationLLMErr
export interface Interface {
readonly reset: () => Effect.Effect<void>
readonly seedFilesystem: (input: typeof FilesystemSeedInput.Type) => Effect.Effect<{ files: string[] }, unknown>
readonly writeFile: (input: typeof FilesystemWriteInput.Type) => Effect.Effect<{ file: string }, unknown>
readonly registerNetwork: (input: typeof NetworkRegisterInput.Type) => Effect.Effect<{ registered: string }, unknown>
readonly enqueueLLM: (input: typeof LLMEnqueueInput.Type) => Effect.Effect<{ queued: number }>
readonly nextLLM: () => Effect.Effect<LLMScript, SimulationLLMError>
@@ -131,6 +137,12 @@ export const layer = Layer.effect(
return { files }
})
const writeFile = Effect.fn("Simulation.writeFile")(function* (input: typeof FilesystemWriteInput.Type) {
yield* fs.writeWithDirs(path.isAbsolute(input.path) ? input.path : path.join("/opencode", input.path), fileContent(input.content))
yield* Ref.update(state, (current) => ({ ...current, files: [...current.files, input.path] }))
return { file: input.path }
})
const registerNetwork = Effect.fn("Simulation.registerNetwork")(function* (input: typeof NetworkRegisterInput.Type) {
switch (input.kind) {
case "json":
@@ -178,7 +190,7 @@ export const layer = Layer.effect(
}
})
return Service.of({ reset, seedFilesystem, registerNetwork, enqueueLLM, nextLLM, snapshot })
return Service.of({ reset, seedFilesystem, writeFile, registerNetwork, enqueueLLM, nextLLM, snapshot })
}),
)
@@ -28,6 +28,17 @@ describe("Simulation", () => {
}),
)
it.effect("writes one file into the simulated filesystem", () =>
Effect.gen(function* () {
const simulation = yield* Simulation.Service
const fs = yield* AppFileSystem.Service
expect(yield* simulation.writeFile({ path: "src/generated.txt", content: "generated" })).toEqual({ file: "src/generated.txt" })
expect(yield* fs.readFileString("/opencode/src/generated.txt")).toBe("generated")
expect((yield* simulation.snapshot()).files).toContain("src/generated.txt")
}),
)
it.effect("registers network responses through control state", () =>
Effect.gen(function* () {
const simulation = yield* Simulation.Service