Scheduled and loop agents get workspaces; schedules UI speaks the user's timezone (#1909)

* fix(app): schedules UI speaks the user's timezone

* refactor(server): require explicit agent placement and bind create-agent command

* fix(server): scheduled agents get a workspace and show in the sidebar

* fix(server): loop agents get a workspace and show in the sidebar

* fix(server): preserve scheduled and loop run semantics through the create command

* fix(server): serialize all schedule record writes through the per-schedule queue

* fix(server): tighten create-agent dependency contracts

* fix(server): handle schedule restamps and loop cancellations

* fix(server): preserve scheduled and loop create semantics

* refactor(server): schedule store owns atomic mutations

* fix(server): keep schedule stamp reuse deterministic

* fix(server): handle loop workspace stamp rejection

* refactor(server): schedule store owns typed identity upsert

* fix(server): keep schedule workspace stamps server-owned

* fix(server): preserve scheduled run semantics

* refactor: required nullable workspaceId replaces placement union; pure cadence policy

* fix(server): tighten scheduled and loop agent edges

* fix(server): fail canceled scheduled runs

* fix(server): close schedule and loop cwd edges

* fix(server): refresh schedule workspace stamps

* fix(server): preserve schedule timezone on replace

* fix(server): close loop stop and cadence reset edges
This commit is contained in:
Mohamed Boudra
2026-07-07 17:13:28 +08:00
committed by GitHub
parent 783f43c580
commit d3e1fe48f6
29 changed files with 4814 additions and 1067 deletions
@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";
import { ScheduleCreateRequestSchema } from "./rpc-schemas.js";
describe("schedule RPC schemas", () => {
it("keeps new-agent workspace stamps out of create requests", () => {
const parsed = ScheduleCreateRequestSchema.parse({
type: "schedule/create",
requestId: "request-1",
prompt: "Run the task",
cadence: { type: "every", everyMs: 60_000 },
target: {
type: "new-agent",
config: {
provider: "claude",
cwd: "/tmp/project",
workspaceId: "client-owned-workspace",
},
},
});
expect(parsed.target.type).toBe("new-agent");
if (parsed.target.type === "new-agent") {
expect(parsed.target.config).not.toHaveProperty("workspaceId");
}
});
});
@@ -7,6 +7,10 @@ import {
ScheduleTargetSchema,
} from "./types.js";
const ScheduleCreateNewAgentConfigSchema = ScheduleTargetSchema.options[1].shape.config.omit({
workspaceId: true,
});
const ScheduleCreateTargetSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("self"),
@@ -18,7 +22,7 @@ const ScheduleCreateTargetSchema = z.discriminatedUnion("type", [
}),
z.object({
type: z.literal("new-agent"),
config: ScheduleTargetSchema.options[1].shape.config,
config: ScheduleCreateNewAgentConfigSchema,
}),
]);
+1
View File
@@ -27,6 +27,7 @@ export const ScheduleTargetSchema = z.discriminatedUnion("type", [
config: z.object({
provider: AgentProviderSchema,
cwd: z.string().trim().min(1),
workspaceId: z.string().optional(),
modeId: z.string().trim().min(1).optional(),
model: z.string().trim().min(1).optional(),
thinkingOptionId: z.string().trim().min(1).optional(),