Skip to content

Commit ff584ef

Browse files
committed
feat(tools): add incremental job sync and draft postings to Ashby reads
list_jobs accepts Ashby's syncToken and returns it as nextSyncCursor, so a scheduled sync costs O(changed reqs) instead of rescanning every req. Ashby only returns the token once the last page is drained, which the param description states. The output is named as a cursor deliberately. It is an opaque resumption marker, not a credential, so it belongs with nextCursor - and a field literally named syncToken matches the /^.*token$/i deny-list in redaction and renders as [REDACTED], which makes an incremental sync unusable since the operator cannot read the value the next run needs. The wire name stays syncToken. list_job_postings gains includeUnpublishedJobPostings, plus the posting status field - without status a caller cannot tell a returned draft from a published posting, which makes the flag useless. Also widens the custom field valueLabel type, which MultiValueSelect returns as an array, for the write operations that follow.
1 parent a7115e8 commit ff584ef

3 files changed

Lines changed: 38 additions & 2 deletions

File tree

apps/sim/tools/ashby/list_job_postings.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface AshbyListJobPostingsParams {
66
location?: string
77
department?: string
88
listedOnly?: boolean
9+
includeUnpublishedJobPostings?: boolean
910
jobBoardId?: string
1011
}
1112

@@ -22,6 +23,7 @@ interface AshbyJobPostingSummary {
2223
} | null
2324
workplaceType: string | null
2425
employmentType: string | null
26+
status: string | null
2527
isListed: boolean
2628
publishedDate: string | null
2729
applicationDeadline: string | null
@@ -72,6 +74,13 @@ export const listJobPostingsTool: ToolConfig<
7274
visibility: 'user-or-llm',
7375
description: 'When true, only returns listed (publicly visible) job postings (default false)',
7476
},
77+
includeUnpublishedJobPostings: {
78+
type: 'boolean',
79+
required: false,
80+
visibility: 'user-or-llm',
81+
description:
82+
'When true, also returns unpublished (Draft) job postings. The endpoint already returns both listed and unlisted published postings by default, so this only adds drafts.',
83+
},
7584
jobBoardId: {
7685
type: 'string',
7786
required: false,
@@ -90,6 +99,9 @@ export const listJobPostingsTool: ToolConfig<
9099
if (params.location) body.location = params.location
91100
if (params.department) body.department = params.department
92101
if (params.listedOnly !== undefined) body.listedOnly = params.listedOnly
102+
if (params.includeUnpublishedJobPostings !== undefined) {
103+
body.includeUnpublishedJobPostings = params.includeUnpublishedJobPostings
104+
}
93105
if (params.jobBoardId) body.jobBoardId = params.jobBoardId.trim()
94106
return body
95107
},
@@ -127,6 +139,7 @@ export const listJobPostingsTool: ToolConfig<
127139
: null,
128140
workplaceType: (jp.workplaceType as string) ?? null,
129141
employmentType: (jp.employmentType as string) ?? null,
142+
status: (jp.status as string) ?? null,
130143
isListed: (jp.isListed as boolean) ?? false,
131144
publishedDate: (jp.publishedDate as string) ?? null,
132145
applicationDeadline: (jp.applicationDeadline as string) ?? null,
@@ -186,6 +199,11 @@ export const listJobPostingsTool: ToolConfig<
186199
description: 'Employment type (FullTime, PartTime, Intern, Contract, Temporary)',
187200
optional: true,
188201
},
202+
status: {
203+
type: 'string',
204+
description: 'Posting status (Draft or Published)',
205+
optional: true,
206+
},
189207
isListed: { type: 'boolean', description: 'Whether the posting is publicly listed' },
190208
publishedDate: {
191209
type: 'string',

apps/sim/tools/ashby/list_jobs.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,15 @@ export const listJobsTool: ToolConfig<AshbyListJobsParams, AshbyListJobsResponse
2626
type: 'number',
2727
required: false,
2828
visibility: 'user-or-llm',
29-
description: 'Number of results per page (default 100)',
29+
description:
30+
'Number of results per page (default and max 100). Ashby silently caps larger values rather than erroring.',
31+
},
32+
syncToken: {
33+
type: 'string',
34+
required: false,
35+
visibility: 'user-or-llm',
36+
description:
37+
'Opaque token from a prior sync to fetch only jobs changed since then. Ashby only returns a new syncToken on the last page, so drain moreDataAvailable/nextCursor before persisting it.',
3038
},
3139
status: {
3240
type: 'string',
@@ -75,6 +83,7 @@ export const listJobsTool: ToolConfig<AshbyListJobsParams, AshbyListJobsResponse
7583
const body: Record<string, unknown> = { expand: ['openings', 'location'] }
7684
if (params.cursor) body.cursor = params.cursor
7785
if (params.perPage) body.limit = params.perPage
86+
if (params.syncToken) body.syncToken = params.syncToken
7887
if (params.status) body.status = [params.status]
7988
const isoToMs = (iso: string): number | null => {
8089
const ms = new Date(iso).getTime()
@@ -117,6 +126,7 @@ export const listJobsTool: ToolConfig<AshbyListJobsParams, AshbyListJobsResponse
117126
jobs: (data.results ?? []).map(mapJob),
118127
moreDataAvailable: data.moreDataAvailable ?? false,
119128
nextCursor: data.nextCursor ?? null,
129+
nextSyncCursor: data.syncToken ?? null,
120130
},
121131
}
122132
},
@@ -139,5 +149,11 @@ export const listJobsTool: ToolConfig<AshbyListJobsParams, AshbyListJobsResponse
139149
description: 'Opaque cursor for fetching the next page',
140150
optional: true,
141151
},
152+
nextSyncCursor: {
153+
type: 'string',
154+
description:
155+
"Ashby's syncToken for the next incremental run, returned only once the last page is drained. Named as a cursor because that is what it is - an opaque resumption marker, not a credential - so it stays readable in block output alongside nextCursor.",
156+
optional: true,
157+
},
142158
},
143159
}

apps/sim/tools/ashby/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface AshbyCustomField {
3131
id: string | null
3232
title: string
3333
isPrivate: boolean
34-
valueLabel: string | null
34+
valueLabel: string | string[] | null
3535
value: unknown
3636
}
3737

@@ -122,6 +122,7 @@ export interface AshbySearchCandidatesParams extends AshbyBaseParams {
122122
export interface AshbyListJobsParams extends AshbyBaseParams {
123123
cursor?: string
124124
perPage?: number
125+
syncToken?: string
125126
status?: string
126127
createdAfter?: string
127128
openedAfter?: string
@@ -266,6 +267,7 @@ export interface AshbyListJobsResponse extends ToolResponse {
266267
jobs: AshbyJob[]
267268
moreDataAvailable: boolean
268269
nextCursor: string | null
270+
nextSyncCursor: string | null
269271
}
270272
}
271273

0 commit comments

Comments
 (0)