feat: link schedule runs to the session they created (AGE-2144) (#1085) · truefoundry/trueforge@5bb093e · GitHub
Skip to content

Commit 5bb093e

Browse files
authored
feat: link schedule runs to the session they created (AGE-2144) (#1085)
1 parent 258f528 commit 5bb093e

22 files changed

Lines changed: 240 additions & 44 deletions

File tree

Lines changed: 5 additions & 0 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@truefoundry/trueforge': patch
3+
---
4+
5+
Schedule runs now return `session_id`, the session each run created, so clients can open a run's
6+
session. Existing runs are backfilled; runs without a session return `null`.

‎.github/fern/openapi/openapi.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,12 @@
39013901
"format": "date-time",
39023902
"type": "string"
39033903
},
3904+
"session_id": {
3905+
"type": [
3906+
"string",
3907+
"null"
3908+
]
3909+
},
39043910
"status": {
39053911
"$ref": "#/components/schemas/ScheduleRunStatus"
39063912
},
@@ -3925,6 +3931,7 @@
39253931
"created_by_subject",
39263932
"triggered_at",
39273933
"reason",
3934+
"session_id",
39283935
"created_at",
39293936
"updated_at"
39303937
],

‎docs/openapi.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,12 @@
39013901
"format": "date-time",
39023902
"type": "string"
39033903
},
3904+
"session_id": {
3905+
"type": [
3906+
"string",
3907+
"null"
3908+
]
3909+
},
39043910
"status": {
39053911
"$ref": "#/components/schemas/ScheduleRunStatus"
39063912
},
@@ -3925,6 +3931,7 @@
39253931
"created_by_subject",
39263932
"triggered_at",
39273933
"reason",
3934+
"session_id",
39283935
"created_at",
39293936
"updated_at"
39303937
],

‎packages/trueforge-sdk/src/api/types/ScheduleRun.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ScheduleRun {
1010
reason: string | null;
1111
scheduleId: string;
1212
scheduledFor: Date;
13+
sessionId: string | null;
1314
status: TrueForge.ScheduleRunStatus;
1415
triggeredAt: Date | null;
1516
updatedAt: Date;

‎packages/trueforge-sdk/src/serialization/types/ScheduleRun.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const ScheduleRun: core.serialization.ObjectSchema<serializers.ScheduleRu
1515
reason: core.serialization.string().nullable(),
1616
scheduleId: core.serialization.property("schedule_id", core.serialization.string()),
1717
scheduledFor: core.serialization.property("scheduled_for", core.serialization.date()),
18+
sessionId: core.serialization.property("session_id", core.serialization.string().nullable()),
1819
status: ScheduleRunStatus,
1920
triggeredAt: core.serialization.property("triggered_at", core.serialization.date().nullable()),
2021
updatedAt: core.serialization.property("updated_at", core.serialization.date()),
@@ -29,6 +30,7 @@ export declare namespace ScheduleRun {
2930
reason?: string | null;
3031
schedule_id: string;
3132
scheduled_for: string;
33+
session_id?: string | null;
3234
status: ScheduleRunStatus.Raw;
3335
triggered_at?: string | null;
3436
updated_at: string;

‎packages/trueforge-sdk/tests/wire/schedules.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ describe("SchedulesClient", () => {
252252
reason: "reason",
253253
schedule_id: "schedule_id",
254254
scheduled_for: "2024-01-15T09:30:00Z",
255+
session_id: "session_id",
255256
status: "scheduled",
256257
triggered_at: "2024-01-15T09:30:00Z",
257258
updated_at: "2024-01-15T09:30:00Z",
@@ -283,6 +284,7 @@ describe("SchedulesClient", () => {
283284
reason: "reason",
284285
scheduleId: "schedule_id",
285286
scheduledFor: new Date("2024-01-15T09:30:00.000Z"),
287+
sessionId: "session_id",
286288
status: "scheduled",
287289
triggeredAt: new Date("2024-01-15T09:30:00.000Z"),
288290
updatedAt: new Date("2024-01-15T09:30:00.000Z"),
@@ -725,6 +727,7 @@ describe("SchedulesClient", () => {
725727
reason: "reason",
726728
schedule_id: "schedule_id",
727729
scheduled_for: "2024-01-15T09:30:00Z",
730+
session_id: "session_id",
728731
status: "scheduled",
729732
triggered_at: "2024-01-15T09:30:00Z",
730733
updated_at: "2024-01-15T09:30:00Z",
@@ -755,6 +758,7 @@ describe("SchedulesClient", () => {
755758
reason: "reason",
756759
scheduleId: "schedule_id",
757760
scheduledFor: new Date("2024-01-15T09:30:00.000Z"),
761+
sessionId: "session_id",
758762
status: "scheduled",
759763
triggeredAt: new Date("2024-01-15T09:30:00.000Z"),
760764
updatedAt: new Date("2024-01-15T09:30:00.000Z"),

‎packages/trueforge/src/apis/schedules.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export async function startScheduleRunOnRequest<TTransaction>(params: {
106106
item,
107107
sessions: deps.sessions,
108108
agentStore: deps.agentStore,
109+
scheduleStore: deps.scheduleStore,
109110
});
110111
if (prepared === undefined) {
111112
return;
@@ -152,6 +153,7 @@ function toWireScheduleRun(record: ScheduleRunRecord): ScheduleRun {
152153
created_by_subject: record.created_by_subject,
153154
triggered_at: record.triggered_at,
154155
reason: record.reason,
156+
session_id: record.session_id,
155157
created_at: record.created_at,
156158
updated_at: record.updated_at,
157159
};
@@ -349,7 +351,7 @@ export function createSchedulesRouter<TTransaction>(deps: SchedulesRouterDeps<TT
349351
try {
350352
await startScheduleRunOnRequest({ c, item: { run, schedule }, deps });
351353
} catch (error) {
352-
await deps.scheduleStore.updateRunStatus({
354+
await deps.scheduleStore.updateRun({
353355
tenant_id: requestContext.tenant_id,
354356
id: run.id,
355357
status: 'failed',

‎packages/trueforge/src/controller/scheduleDispatch.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,17 @@ export interface PreparedScheduleTurn {
8686
agent: AgentRecord;
8787
}
8888

89-
export async function startScheduleRun(params: {
89+
export async function startScheduleRun<TTransaction>(params: {
9090
item: ScheduleDispatchItem;
9191
sessions: Sessions;
9292
agentStore: IAgentStore;
93+
scheduleStore: Pick<IScheduleStore<TTransaction>, 'updateRun'>;
9394
}): Promise<PreparedScheduleTurn | undefined> {
9495
const {
9596
item: { run, schedule },
9697
sessions,
9798
agentStore,
99+
scheduleStore,
98100
} = params;
99101

100102
const named = await agentStore.getAgent({ tenant_id: schedule.tenant_id, name: schedule.agent_name });
@@ -110,6 +112,14 @@ export async function startScheduleRun(params: {
110112
source: { type: 'schedule', id: schedule.id, run_id: run.id },
111113
});
112114

115+
// This is not atomic with session creation so a failure here leaves the run unlinked.
116+
// Runs before the idempotency check so retries relink it, at the cost of redundant writes on re-delivery.
117+
await scheduleStore.updateRun({
118+
tenant_id: schedule.tenant_id,
119+
id: run.id,
120+
session_id: session.session_id,
121+
});
122+
113123
// idempotency check
114124
const { data: turns } = await session.listTurns({ limit: 1 });
115125
if (turns.length > 0) {
@@ -164,7 +174,7 @@ async function finishScheduledRun<TTransaction>(params: {
164174
await withTransaction(async txn => {
165175
const latest = await store.getScheduleForUpdate({ tenant_id: run.tenant_id, id: run.schedule_id }, txn);
166176

167-
const updated = await store.updateRunStatus(
177+
const updated = await store.updateRun(
168178
{ tenant_id: run.tenant_id, id: run.id, status, reason: reason ?? null },
169179
txn,
170180
);
Lines changed: 22 additions & 0 deletions

0 commit comments

Comments
 (0)