@pstdio/sdk 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +27 -188
  2. package/dist/api/extensions.d.ts +1 -1
  3. package/dist/api/index.d.ts +2 -2
  4. package/dist/api/settings.d.ts +1 -0
  5. package/dist/api/statuses.d.ts +1 -0
  6. package/dist/api/tags.d.ts +1 -0
  7. package/dist/api/tickets.d.ts +3 -0
  8. package/dist/api/workspaces.d.ts +10 -1
  9. package/dist/client/client.d.ts +7 -2
  10. package/dist/client/index.d.ts +5 -3
  11. package/dist/client/index.js +285 -31
  12. package/dist/client/projects.d.ts +0 -11
  13. package/dist/client/request.d.ts +9 -0
  14. package/dist/client/sessions.d.ts +25 -3
  15. package/dist/client/settings.d.ts +7 -0
  16. package/dist/client/sse.d.ts +15 -0
  17. package/dist/client/statuses.d.ts +2 -0
  18. package/dist/client/sync.d.ts +35 -0
  19. package/dist/client/tags.d.ts +2 -0
  20. package/dist/client/tickets.d.ts +6 -1
  21. package/dist/client/workspaces.d.ts +2 -0
  22. package/dist/extensions/define-command.d.ts +1 -1
  23. package/dist/extensions/define-extension.d.ts +50 -7
  24. package/dist/extensions/index.d.ts +3 -2
  25. package/dist/extensions/index.js +151 -11
  26. package/dist/extensions/kernel-slots.d.ts +131 -10
  27. package/dist/extensions/params.d.ts +19 -12
  28. package/dist/extensions/refs.d.ts +3 -3
  29. package/dist/extensions/slots.d.ts +1 -9
  30. package/dist/extensions/types/commands.d.ts +8 -0
  31. package/dist/extensions/types/context.d.ts +225 -18
  32. package/dist/extensions/types/contributions.d.ts +183 -10
  33. package/dist/extensions/types/extension.d.ts +16 -10
  34. package/dist/extensions/types/params.d.ts +43 -29
  35. package/dist/extensions/types/slots.d.ts +1 -1
  36. package/dist/extensions/types/webview-capabilities.d.ts +12 -2
  37. package/dist/extensions/workbench-targets.d.ts +134 -0
  38. package/dist/hooks/attempt.d.ts +2 -0
  39. package/dist/hooks/entities.d.ts +2 -0
  40. package/dist/hooks/ticket.d.ts +3 -0
  41. package/dist/resources/index.d.ts +3 -0
  42. package/dist/resources/index.js +26 -0
  43. package/dist/resources/known-agents.d.ts +12 -0
  44. package/dist/resources/settings.d.ts +1 -0
  45. package/dist/resources/status.d.ts +1 -0
  46. package/dist/resources/tag.d.ts +1 -0
  47. package/dist/resources/ticket.d.ts +1 -0
  48. package/dist/resources/workspace.d.ts +4 -1
  49. package/package.json +2 -6
  50. package/dist/api/actions.d.ts +0 -13
  51. package/dist/client/actions.d.ts +0 -8
  52. package/dist/plugins/define-plugin.d.ts +0 -2
  53. package/dist/plugins/helpers/context.d.ts +0 -24
  54. package/dist/plugins/helpers/create-attempt.d.ts +0 -5
  55. package/dist/plugins/helpers/create-session.d.ts +0 -21
  56. package/dist/plugins/helpers/create-workspace.d.ts +0 -5
  57. package/dist/plugins/helpers/find-ticket-by-ref.d.ts +0 -6
  58. package/dist/plugins/helpers/find-workspace-by-ref.d.ts +0 -5
  59. package/dist/plugins/helpers/followup-session.d.ts +0 -29
  60. package/dist/plugins/helpers/get-attempts-for-ticket.d.ts +0 -17
  61. package/dist/plugins/helpers/index.d.ts +0 -16
  62. package/dist/plugins/helpers/remove-all-worktrees-for-ticket.d.ts +0 -2
  63. package/dist/plugins/helpers/run-command.d.ts +0 -10
  64. package/dist/plugins/helpers/save-ticket.d.ts +0 -14
  65. package/dist/plugins/helpers/set-ticket-status.d.ts +0 -7
  66. package/dist/plugins/helpers/set-workspace-attempt-status.d.ts +0 -7
  67. package/dist/plugins/helpers/ticket-pull.d.ts +0 -18
  68. package/dist/plugins/helpers/update-ticket-when-all-attempts-match.d.ts +0 -7
  69. package/dist/plugins/helpers/workspaces-for-ticket.d.ts +0 -17
  70. package/dist/plugins/helpers/worktree-bootstrap.d.ts +0 -8
  71. package/dist/plugins/hooks.d.ts +0 -42
  72. package/dist/plugins/index.d.ts +0 -5
  73. package/dist/plugins/index.js +0 -739
  74. package/dist/plugins/types.d.ts +0 -112
@@ -1,12 +1,3 @@
1
- // src/client/actions.ts
2
- var createActionClient = (request) => ({
3
- list: (projectId, targetType) => request(targetType ? `/v1/projects/${projectId}/actions?targetType=${targetType}` : `/v1/projects/${projectId}/actions`),
4
- execute: (projectId, actionKey, input) => request(`/v1/projects/${projectId}/actions/${encodeURIComponent(actionKey)}/execute`, {
5
- method: "POST",
6
- body: input
7
- })
8
- });
9
-
10
1
  // src/client/agents.ts
11
2
  var createAgentClient = (request) => ({
12
3
  list: () => request("/v1/agents"),
@@ -62,8 +53,6 @@ var createProjectClient = (request) => ({
62
53
  const query = params.toString();
63
54
  return request(`/v1/projects/${projectId}/activity${query ? `?${query}` : ""}`);
64
55
  },
65
- listPlugins: (projectId) => request(`/v1/projects/${projectId}/plugins`),
66
- registerPlugins: (projectId) => request(`/v1/projects/${projectId}/plugins/register`, { method: "POST" }),
67
56
  listRepos: (projectId) => request(`/v1/projects/${projectId}/repos`),
68
57
  registerRepo: (projectId, input) => request(`/v1/projects/${projectId}/repos`, { method: "POST", body: input }),
69
58
  removeRepo: (projectId, repoId) => request(`/v1/projects/${projectId}/repos/${repoId}`, { method: "DELETE" })
@@ -107,20 +96,32 @@ ${hookOutput}` : message;
107
96
  }
108
97
  return `Request failed: ${status}`;
109
98
  };
99
+ var resolveBaseUrl = (options) => options.baseUrl ?? (typeof process !== "undefined" ? process.env.PSTDIO_API_URL : undefined) ?? "http://localhost:19840";
100
+ var resolveClientUrl = (baseUrl, path) => path.startsWith("http://") || path.startsWith("https://") ? path : `${baseUrl}${path}`;
101
+ var resolveFetch = (options) => options.fetch ?? globalThis.fetch;
102
+ var createRequestHeaders = (options, reqOpts = {}) => {
103
+ const headers = new Headers(reqOpts.headers);
104
+ if (reqOpts.hasJsonBody && !headers.has("content-type"))
105
+ headers.set("content-type", "application/json");
106
+ if (options.token)
107
+ headers.set("authorization", `Bearer ${options.token}`);
108
+ return headers;
109
+ };
110
110
  var createRequest = (options) => {
111
- const baseUrl = options.baseUrl ?? (typeof process !== "undefined" ? process.env.PSTDIO_API_URL : undefined) ?? "http://localhost:19840";
112
- const fetchFn = options.fetch ?? globalThis.fetch;
111
+ const baseUrl = resolveBaseUrl(options);
112
+ const fetchFn = resolveFetch(options);
113
113
  return async (path, reqOpts = {}) => {
114
- const headers = {};
115
- if (reqOpts.body !== undefined)
116
- headers["content-type"] = "application/json";
117
- if (options.token)
118
- headers.authorization = `Bearer ${options.token}`;
119
- const response = await fetchFn(`${baseUrl}${path}`, {
114
+ const headers = createRequestHeaders(options, {
115
+ headers: reqOpts.headers,
116
+ hasJsonBody: reqOpts.body !== undefined
117
+ });
118
+ const url = resolveClientUrl(baseUrl, path);
119
+ const response = await fetchFn(url, {
120
120
  method: reqOpts.method ?? "GET",
121
- headers,
121
+ headers: Object.fromEntries(headers.entries()),
122
122
  body: reqOpts.body !== undefined ? JSON.stringify(reqOpts.body) : undefined,
123
- signal: reqOpts.signal
123
+ signal: reqOpts.signal,
124
+ cache: reqOpts.cache
124
125
  });
125
126
  if (!response.ok) {
126
127
  const errorBody = await response.json().catch(() => null);
@@ -133,8 +134,105 @@ var createRequest = (options) => {
133
134
  };
134
135
  };
135
136
 
137
+ // src/client/sse.ts
138
+ var parseSseEvent = (part) => {
139
+ let event = "message";
140
+ const data = [];
141
+ for (const line of part.split(`
142
+ `)) {
143
+ if (line.startsWith("event: ")) {
144
+ event = line.slice(7);
145
+ continue;
146
+ }
147
+ if (line.startsWith("data: ")) {
148
+ data.push(line.slice(6));
149
+ }
150
+ }
151
+ if (!event || data.length === 0)
152
+ return null;
153
+ return { event, data: data.join(`
154
+ `) };
155
+ };
156
+ var consumeSseBuffer = (buffer) => {
157
+ const parts = buffer.split(`
158
+
159
+ `);
160
+ const remainder = parts.pop() ?? "";
161
+ const events = parts.map((part) => parseSseEvent(part)).filter((event) => event !== null);
162
+ return { remainder, events };
163
+ };
164
+ var readSseStream = async (body, onEvent, options = {}) => {
165
+ const reader = body.getReader();
166
+ const decoder = new TextDecoder;
167
+ let buffer = "";
168
+ const abort = () => {
169
+ reader.cancel();
170
+ };
171
+ options.signal?.addEventListener("abort", abort, { once: true });
172
+ try {
173
+ while (!options.signal?.aborted) {
174
+ const { done, value } = await reader.read();
175
+ if (done)
176
+ break;
177
+ buffer += decoder.decode(value, { stream: true });
178
+ const { remainder, events } = consumeSseBuffer(buffer);
179
+ buffer = remainder;
180
+ for (const event of events) {
181
+ onEvent(event);
182
+ }
183
+ }
184
+ } finally {
185
+ options.signal?.removeEventListener("abort", abort);
186
+ }
187
+ };
188
+
136
189
  // src/client/sessions.ts
137
- var createSessionClient = (request) => ({
190
+ var buildSessionsQuery = (projectId, input = {}) => {
191
+ const params = new URLSearchParams({ project_id: projectId });
192
+ if (input.status)
193
+ params.append("status", input.status);
194
+ if (input.agent)
195
+ params.append("agent", input.agent);
196
+ if (input.archived)
197
+ params.append("archived", "true");
198
+ return params.toString();
199
+ };
200
+ var buildSessionStreamPath = (sessionId, attempt) => {
201
+ const path = `/v1/sessions/${sessionId}/stream`;
202
+ return attempt === undefined ? path : `${path}?attempt=${attempt}`;
203
+ };
204
+ var parseEventData = (data) => {
205
+ if (data.length === 0)
206
+ return null;
207
+ return JSON.parse(data);
208
+ };
209
+ var streamSessionEvents = async (clientOptions, path, onEvent, options = {}) => {
210
+ const response = await resolveFetch(clientOptions)(resolveClientUrl(resolveBaseUrl(clientOptions), path), {
211
+ headers: Object.fromEntries(createRequestHeaders(clientOptions).entries()),
212
+ signal: options.signal
213
+ });
214
+ if (!response.ok || !response.body)
215
+ throw new Error(`Connection failed: ${response.status}`);
216
+ await readSseStream(response.body, onEvent, { signal: options.signal });
217
+ };
218
+ var dispatchSessionStreamEvent = (event, handlers) => {
219
+ if (event.event === "ready") {
220
+ handlers.onReady?.(parseEventData(event.data));
221
+ return;
222
+ }
223
+ if (event.event === "patch") {
224
+ handlers.onPatch?.(parseEventData(event.data));
225
+ return;
226
+ }
227
+ if (event.event === "approval_request") {
228
+ handlers.onApprovalRequest?.(parseEventData(event.data));
229
+ return;
230
+ }
231
+ if (event.event === "end") {
232
+ handlers.onEnd?.(parseEventData(event.data));
233
+ }
234
+ };
235
+ var createSessionClient = (request, clientOptions) => ({
138
236
  listActivity: (sessionId, input = {}) => {
139
237
  const params = new URLSearchParams;
140
238
  if (input.event_type)
@@ -150,7 +248,7 @@ var createSessionClient = (request) => ({
150
248
  const query = params.toString();
151
249
  return request(`/v1/sessions/${sessionId}/activity${query ? `?${query}` : ""}`);
152
250
  },
153
- list: (projectId) => request(`/v1/sessions?project_id=${projectId}`),
251
+ list: (projectId, input) => request(`/v1/sessions?${buildSessionsQuery(projectId, input)}`),
154
252
  get: (sessionId) => request(`/v1/sessions/${sessionId}`),
155
253
  create: (input) => request("/v1/sessions", { method: "POST", body: input }),
156
254
  archive: (sessionId) => request(`/v1/sessions/${sessionId}/archive`, { method: "POST" }),
@@ -158,7 +256,29 @@ var createSessionClient = (request) => ({
158
256
  approve: (sessionId, input) => request(`/v1/sessions/${sessionId}/approve`, { method: "POST", body: input }),
159
257
  getConversation: (sessionId) => request(`/v1/sessions/${sessionId}/conversation`),
160
258
  resolveSessionId: (input) => request("/v1/sessions/resolve-session-id", { method: "POST", body: input }),
161
- updateStatus: (sessionId, status) => request(`/v1/sessions/${sessionId}/status`, { method: "PATCH", body: { status } })
259
+ updateStatus: (sessionId, status) => request(`/v1/sessions/${sessionId}/status`, { method: "PATCH", body: { status } }),
260
+ stream: (sessionId, onEvent, options = {}) => streamSessionEvents(clientOptions, buildSessionStreamPath(sessionId), onEvent, options),
261
+ connectStream: (sessionId, handlers, options = {}) => {
262
+ const abortController = new AbortController;
263
+ let closed = false;
264
+ streamSessionEvents(clientOptions, buildSessionStreamPath(sessionId, options.attempt), (event) => dispatchSessionStreamEvent(event, handlers), { signal: abortController.signal }).catch((error) => {
265
+ if (closed || error.name === "AbortError")
266
+ return;
267
+ handlers.onError?.(error);
268
+ });
269
+ return {
270
+ close: () => {
271
+ closed = true;
272
+ abortController.abort();
273
+ }
274
+ };
275
+ }
276
+ });
277
+
278
+ // src/client/settings.ts
279
+ var createSettingsClient = (request) => ({
280
+ get: () => request("/v1/settings"),
281
+ update: (input) => request("/v1/settings", { method: "PATCH", body: input })
162
282
  });
163
283
 
164
284
  // src/client/skills.ts
@@ -181,6 +301,137 @@ var createStatusClient = (request) => ({
181
301
  deleteAttemptStatus: (projectId, statusId) => request(`/v1/projects/${projectId}/attempt-statuses/${statusId}`, { method: "DELETE" })
182
302
  });
183
303
 
304
+ // src/client/sync.ts
305
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
306
+ var DEFAULT_HEARTBEAT_THRESHOLD = 3;
307
+ var DEFAULT_RECONNECT_DELAY_MS = 1000;
308
+ var parseSyncDeleteEvent = (data) => {
309
+ const parsed = JSON.parse(data);
310
+ const id = parsed.id ?? parsed.data?.id;
311
+ if (!id) {
312
+ throw new Error("Missing id in sync:delete event.");
313
+ }
314
+ return {
315
+ table: parsed.table,
316
+ id,
317
+ seq: parsed.seq
318
+ };
319
+ };
320
+ var applySyncEvent = (event, data, writers) => {
321
+ if (event === "init") {
322
+ const parsed = JSON.parse(data);
323
+ for (const [table, rows] of Object.entries(parsed.tables)) {
324
+ writers.getWriter(table)?.truncateAndWrite(rows);
325
+ }
326
+ return parsed.seq;
327
+ }
328
+ if (event === "sync:set") {
329
+ const parsed = JSON.parse(data);
330
+ writers.getWriter(parsed.table)?.upsert(parsed.data);
331
+ return parsed.seq;
332
+ }
333
+ if (event === "sync:delete") {
334
+ const parsed = parseSyncDeleteEvent(data);
335
+ writers.getWriter(parsed.table)?.remove(parsed.id);
336
+ return parsed.seq;
337
+ }
338
+ if (event === "heartbeat") {
339
+ return JSON.parse(data).seq;
340
+ }
341
+ return null;
342
+ };
343
+ var createHeartbeatMonitor = (input) => {
344
+ let missedHeartbeats = 0;
345
+ let timer = null;
346
+ return {
347
+ recordHeartbeat: () => {
348
+ missedHeartbeats = 0;
349
+ },
350
+ start: () => {
351
+ timer = setInterval(() => {
352
+ missedHeartbeats += 1;
353
+ if (missedHeartbeats >= input.threshold) {
354
+ input.onConnectionLost?.();
355
+ }
356
+ }, input.intervalMs);
357
+ },
358
+ stop: () => {
359
+ if (timer)
360
+ clearInterval(timer);
361
+ timer = null;
362
+ }
363
+ };
364
+ };
365
+ var buildSyncStreamPath = (lastSeq) => lastSeq > 0 ? `/v1/sync/stream?since=${lastSeq}` : "/v1/sync/stream";
366
+ var createSyncClient = (clientOptions) => ({
367
+ start: (input) => {
368
+ let lastSeq = 0;
369
+ let abortController = null;
370
+ let reconnectTimer = null;
371
+ let closed = false;
372
+ const state = { close: () => {}, connected: false };
373
+ const heartbeatMonitor = createHeartbeatMonitor({
374
+ intervalMs: input.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS,
375
+ threshold: input.heartbeatThreshold ?? DEFAULT_HEARTBEAT_THRESHOLD,
376
+ onConnectionLost: input.onConnectionLost
377
+ });
378
+ const setConnected = () => {
379
+ const wasConnected = state.connected;
380
+ state.connected = true;
381
+ if (!wasConnected)
382
+ input.onConnected?.();
383
+ };
384
+ const setDisconnected = () => {
385
+ const wasConnected = state.connected;
386
+ state.connected = false;
387
+ if (wasConnected)
388
+ input.onDisconnected?.();
389
+ };
390
+ const handleEvent = (event, data) => {
391
+ heartbeatMonitor.recordHeartbeat();
392
+ const nextSeq = applySyncEvent(event, data, input);
393
+ if (nextSeq !== null)
394
+ lastSeq = Math.max(lastSeq, nextSeq);
395
+ if (event === "init")
396
+ setConnected();
397
+ };
398
+ const connect = async () => {
399
+ if (closed)
400
+ return;
401
+ abortController = new AbortController;
402
+ try {
403
+ const response = await resolveFetch(clientOptions)(resolveClientUrl(resolveBaseUrl(clientOptions), buildSyncStreamPath(lastSeq)), {
404
+ headers: Object.fromEntries(createRequestHeaders(clientOptions).entries()),
405
+ signal: abortController.signal
406
+ });
407
+ if (!response.ok || !response.body)
408
+ throw new Error("SSE connection failed");
409
+ await readSseStream(response.body, ({ event, data }) => handleEvent(event, data), {
410
+ signal: abortController.signal
411
+ });
412
+ } catch (err) {
413
+ if (closed || err.name === "AbortError")
414
+ return;
415
+ }
416
+ if (closed)
417
+ return;
418
+ setDisconnected();
419
+ reconnectTimer = setTimeout(connect, input.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS);
420
+ };
421
+ heartbeatMonitor.start();
422
+ connect();
423
+ state.close = () => {
424
+ closed = true;
425
+ state.connected = false;
426
+ heartbeatMonitor.stop();
427
+ abortController?.abort();
428
+ if (reconnectTimer)
429
+ clearTimeout(reconnectTimer);
430
+ };
431
+ return state;
432
+ }
433
+ });
434
+
184
435
  // src/client/tags.ts
185
436
  var createTagClient = (request) => ({
186
437
  list: (projectId) => request(`/v1/projects/${projectId}/ticket-tags`),
@@ -202,7 +453,7 @@ var createTemplateClient = (request) => ({
202
453
  });
203
454
 
204
455
  // src/client/tickets.ts
205
- var resolveBaseUrl = (options) => options.baseUrl ?? (typeof process !== "undefined" ? process.env.PSTDIO_API_URL : undefined) ?? "http://localhost:19840";
456
+ var resolveBaseUrl2 = (options) => options.baseUrl ?? (typeof process !== "undefined" ? process.env.PSTDIO_API_URL : undefined) ?? "http://localhost:19840";
206
457
  var readErrorMessage2 = async (response) => {
207
458
  const errorBody = await response.json().catch(() => null);
208
459
  if (errorBody && typeof errorBody === "object" && errorBody !== null && "error" in errorBody) {
@@ -265,13 +516,13 @@ var createTicketClient = (request, options = {}) => ({
265
516
  },
266
517
  updateWhenAttemptStatus: (ticketId, input) => request(`/v1/tickets/${ticketId}/update-when-attempt-status`, { method: "POST", body: input }),
267
518
  listFiles: (ticketId) => request(`/v1/tickets/${ticketId}/files`),
268
- getFileContent: async (ticketId, fileId) => {
269
- const baseUrl = resolveBaseUrl(options);
519
+ getFileContent: async (ticketId, fileId, requestOptions = {}) => {
520
+ const baseUrl = resolveBaseUrl2(options);
270
521
  const fetchFn = options.fetch ?? globalThis.fetch;
271
522
  const headers = {};
272
523
  if (options.token)
273
524
  headers.authorization = `Bearer ${options.token}`;
274
- const response = await fetchFn(`${baseUrl}/v1/tickets/${encodeURIComponent(ticketId)}/files/${encodeURIComponent(fileId)}/content`, { headers });
525
+ const response = await fetchFn(`${baseUrl}/v1/tickets/${encodeURIComponent(ticketId)}/files/${encodeURIComponent(fileId)}/content`, { cache: requestOptions.cache, headers, signal: requestOptions.signal });
275
526
  if (!response.ok) {
276
527
  throw new PstdioApiError(await readErrorMessage2(response), response.status);
277
528
  }
@@ -313,18 +564,21 @@ var createClient = (options = {}) => {
313
564
  projects: createProjectClient(request),
314
565
  tickets: createTicketClient(request, options),
315
566
  workspaces: createWorkspaceClient(request),
316
- sessions: createSessionClient(request),
567
+ sessions: createSessionClient(request, options),
317
568
  statuses: createStatusClient(request),
318
569
  tags: createTagClient(request),
319
570
  templates: createTemplateClient(request),
320
571
  skills: createSkillClient(request),
321
572
  agents: createAgentClient(request),
322
- actions: createActionClient(request),
323
- extensions: createExtensionClient(request)
573
+ extensions: createExtensionClient(request),
574
+ settings: createSettingsClient(request),
575
+ sync: createSyncClient(options)
324
576
  };
325
577
  };
326
578
  export {
579
+ parseSyncDeleteEvent,
327
580
  createRequest,
328
581
  createClient,
582
+ applySyncEvent,
329
583
  PstdioApiError
330
584
  };
@@ -1,25 +1,14 @@
1
1
  import type { CreateProjectInput, ListProjectActivityForTicketsInput, ListTicketActivityResponse, RegisterRepoInput, Repo } from "pstdio-api-contracts";
2
2
  import type { Project } from "../resources";
3
3
  import type { RequestFn } from "./request";
4
- type RegisteredPlugin = {
5
- identity: string;
6
- filePath: string;
7
- };
8
- type RegisteredPluginsResponse = {
9
- plugins: RegisteredPlugin[];
10
- pluginsDir: string | null;
11
- };
12
4
  export type ProjectClient = {
13
5
  list(): Promise<Project[]>;
14
6
  get(projectId: string): Promise<Project>;
15
7
  create(input: CreateProjectInput): Promise<Project>;
16
8
  delete(projectId: string): Promise<void>;
17
- listPlugins(projectId: string): Promise<RegisteredPluginsResponse>;
18
- registerPlugins(projectId: string): Promise<RegisteredPluginsResponse>;
19
9
  listActivity(projectId: string, input?: ListProjectActivityForTicketsInput): Promise<ListTicketActivityResponse>;
20
10
  listRepos(projectId: string): Promise<Repo[]>;
21
11
  registerRepo(projectId: string, input: RegisterRepoInput): Promise<Repo>;
22
12
  removeRepo(projectId: string, repoId: string): Promise<void>;
23
13
  };
24
14
  export declare const createProjectClient: (request: RequestFn) => ProjectClient;
25
- export {};
@@ -11,6 +11,15 @@ export type RequestOptions = {
11
11
  method?: string;
12
12
  body?: unknown;
13
13
  signal?: AbortSignal;
14
+ headers?: HeadersInit;
15
+ cache?: RequestCache;
14
16
  };
15
17
  export type RequestFn = <T>(path: string, options?: RequestOptions) => Promise<T>;
18
+ export declare const resolveBaseUrl: (options: ClientOptions) => string;
19
+ export declare const resolveClientUrl: (baseUrl: string, path: string) => string;
20
+ export declare const resolveFetch: (options: ClientOptions) => typeof fetch;
21
+ export declare const createRequestHeaders: (options: ClientOptions, reqOpts?: {
22
+ headers?: HeadersInit;
23
+ hasJsonBody?: boolean;
24
+ }) => Headers;
16
25
  export declare const createRequest: (options: ClientOptions) => RequestFn;
@@ -1,8 +1,14 @@
1
1
  import type { ApprovalInput, CreateSessionInput, FollowUpInput, FollowUpResponse, ListSessionActivityInput, ListSessionActivityResponse, ResolveSessionIdInput, ResolveSessionIdResponse, SessionConversationResponse } from "pstdio-api-contracts";
2
2
  import type { Session } from "../resources";
3
- import type { RequestFn } from "./request";
3
+ import { type ClientOptions, type RequestFn } from "./request";
4
+ import { type SseEvent } from "./sse";
5
+ export type ListSessionsInput = {
6
+ status?: string;
7
+ agent?: string;
8
+ archived?: boolean;
9
+ };
4
10
  export type SessionClient = {
5
- list(projectId: string): Promise<Session[]>;
11
+ list(projectId: string, input?: ListSessionsInput): Promise<Session[]>;
6
12
  get(sessionId: string): Promise<Session>;
7
13
  create(input: CreateSessionInput): Promise<Session>;
8
14
  archive(sessionId: string): Promise<void>;
@@ -12,5 +18,21 @@ export type SessionClient = {
12
18
  resolveSessionId(input: ResolveSessionIdInput): Promise<ResolveSessionIdResponse>;
13
19
  updateStatus(sessionId: string, status: string): Promise<Session>;
14
20
  listActivity(sessionId: string, input?: ListSessionActivityInput): Promise<ListSessionActivityResponse>;
21
+ stream(sessionId: string, onEvent: (event: SseEvent) => void, options?: {
22
+ signal?: AbortSignal;
23
+ }): Promise<void>;
24
+ connectStream(sessionId: string, handlers: SessionStreamHandlers, options?: {
25
+ attempt?: number;
26
+ }): SessionStreamConnection;
27
+ };
28
+ export type SessionStreamHandlers = {
29
+ onReady?: (data: unknown) => void;
30
+ onPatch?: (data: unknown) => void;
31
+ onApprovalRequest?: (data: unknown) => void;
32
+ onEnd?: (data: unknown) => void;
33
+ onError?: (error: unknown) => void;
34
+ };
35
+ export type SessionStreamConnection = {
36
+ close: () => void;
15
37
  };
16
- export declare const createSessionClient: (request: RequestFn) => SessionClient;
38
+ export declare const createSessionClient: (request: RequestFn, clientOptions: ClientOptions) => SessionClient;
@@ -0,0 +1,7 @@
1
+ import type { Settings, UpdateSettingsInput } from "../api/settings";
2
+ import type { RequestFn } from "./request";
3
+ export type SettingsClient = {
4
+ get(): Promise<Settings>;
5
+ update(input: UpdateSettingsInput): Promise<Settings>;
6
+ };
7
+ export declare const createSettingsClient: (request: RequestFn) => SettingsClient;
@@ -0,0 +1,15 @@
1
+ export type SseEvent = {
2
+ event: string;
3
+ data: string;
4
+ };
5
+ export declare const parseSseEvent: (part: string) => {
6
+ event: string;
7
+ data: string;
8
+ } | null;
9
+ export declare const consumeSseBuffer: (buffer: string) => {
10
+ remainder: string;
11
+ events: SseEvent[];
12
+ };
13
+ export declare const readSseStream: (body: ReadableStream<Uint8Array>, onEvent: (event: SseEvent) => void, options?: {
14
+ signal?: AbortSignal;
15
+ }) => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { AttemptStatus, CreateAttemptStatusInput, CreateStatusInput } from "pstdio-api-contracts";
2
2
  import type { Status } from "../resources";
3
3
  import type { RequestFn } from "./request";
4
+ /** @deprecated Legacy core ticket status client. Ticket statuses are owned by the pstdio tickets extension. */
4
5
  export type StatusClient = {
5
6
  list(projectId: string): Promise<Status[]>;
6
7
  create(projectId: string, input: CreateStatusInput): Promise<Status>;
@@ -19,4 +20,5 @@ export type StatusClient = {
19
20
  }): Promise<AttemptStatus>;
20
21
  deleteAttemptStatus(projectId: string, statusId: string): Promise<void>;
21
22
  };
23
+ /** @deprecated Legacy core ticket status client. Ticket statuses are owned by the pstdio tickets extension. */
22
24
  export declare const createStatusClient: (request: RequestFn) => StatusClient;
@@ -0,0 +1,35 @@
1
+ import { type ClientOptions } from "./request";
2
+ export type SyncRow = {
3
+ id: string;
4
+ [key: string]: unknown;
5
+ };
6
+ export type SyncWriter = {
7
+ truncateAndWrite: (rows: SyncRow[]) => void;
8
+ upsert: (row: SyncRow) => void;
9
+ remove: (id: string) => void;
10
+ };
11
+ export type SyncWriterProvider = {
12
+ getWriter: (table: string) => SyncWriter | undefined;
13
+ };
14
+ export type SyncConnection = {
15
+ close: () => void;
16
+ connected: boolean;
17
+ };
18
+ export type StartSyncInput = SyncWriterProvider & {
19
+ onConnected?: () => void;
20
+ onDisconnected?: () => void;
21
+ onConnectionLost?: () => void;
22
+ heartbeatIntervalMs?: number;
23
+ heartbeatThreshold?: number;
24
+ reconnectDelayMs?: number;
25
+ };
26
+ export type SyncClient = {
27
+ start: (input: StartSyncInput) => SyncConnection;
28
+ };
29
+ export declare const parseSyncDeleteEvent: (data: string) => {
30
+ table: string;
31
+ id: string;
32
+ seq: number;
33
+ };
34
+ export declare const applySyncEvent: (event: string, data: string, writers: SyncWriterProvider) => number | null;
35
+ export declare const createSyncClient: (clientOptions: ClientOptions) => SyncClient;
@@ -1,6 +1,7 @@
1
1
  import type { CreateTagInput, CreateTagOptionInput, UpdateTagInput, UpdateTagOptionInput } from "../api/tags";
2
2
  import type { Tag, TagOption } from "../resources";
3
3
  import type { RequestFn } from "./request";
4
+ /** @deprecated Legacy core ticket tag client. Ticket tags are owned by the pstdio tickets extension. */
4
5
  export type TagClient = {
5
6
  list(projectId: string): Promise<Tag[]>;
6
7
  create(projectId: string, input: CreateTagInput): Promise<Tag>;
@@ -10,4 +11,5 @@ export type TagClient = {
10
11
  updateOption(projectId: string, tagId: string, optionId: string, input: UpdateTagOptionInput): Promise<TagOption>;
11
12
  deleteOption(projectId: string, tagId: string, optionId: string): Promise<void>;
12
13
  };
14
+ /** @deprecated Legacy core ticket tag client. Ticket tags are owned by the pstdio tickets extension. */
13
15
  export declare const createTagClient: (request: RequestFn) => TagClient;
@@ -1,6 +1,7 @@
1
1
  import type { CreateTicketAttemptInput, CreateTicketInput, ListProjectActivityForTicketsInput, ListTicketActivityInput, ListTicketActivityResponse, ListTicketsInput, TicketAttemptResponse, UpdateTicketInput, UpdateWhenAttemptStatusInput, UpdateWhenAttemptStatusResponse, UploadTicketFileInput } from "../api/tickets";
2
2
  import type { Ticket, TicketDetail, TicketFile, TicketListItem } from "../resources";
3
3
  import { type ClientOptions, type RequestFn } from "./request";
4
+ /** @deprecated Legacy core ticket client. Ticket data is owned by the pstdio tickets extension. */
4
5
  export type TicketClient = {
5
6
  list(projectId: string, input?: ListTicketsInput): Promise<TicketListItem[]>;
6
7
  get(ticketId: string): Promise<TicketDetail>;
@@ -12,8 +13,12 @@ export type TicketClient = {
12
13
  listProjectActivity(projectId: string, input?: ListProjectActivityForTicketsInput): Promise<ListTicketActivityResponse>;
13
14
  updateWhenAttemptStatus(ticketId: string, input: UpdateWhenAttemptStatusInput): Promise<UpdateWhenAttemptStatusResponse>;
14
15
  listFiles(ticketId: string): Promise<TicketFile[]>;
15
- getFileContent(ticketId: string, fileId: string): Promise<Uint8Array>;
16
+ getFileContent(ticketId: string, fileId: string, options?: {
17
+ cache?: RequestCache;
18
+ signal?: AbortSignal;
19
+ }): Promise<Uint8Array>;
16
20
  uploadFile(ticketId: string, input: UploadTicketFileInput): Promise<TicketFile>;
17
21
  deleteFile(ticketId: string, fileId: string): Promise<void>;
18
22
  };
23
+ /** @deprecated Legacy core ticket client. Ticket data is owned by the pstdio tickets extension. */
19
24
  export declare const createTicketClient: (request: RequestFn, options?: ClientOptions) => TicketClient;
@@ -4,7 +4,9 @@ import type { RequestFn } from "./request";
4
4
  export type WorkspaceClient = {
5
5
  list(projectId: string): Promise<WorkspaceListItem[]>;
6
6
  getByShorthand(projectId: string, shorthand: string): Promise<Workspace>;
7
+ /** @deprecated Requires legacy ticket-workspace linkage. Ticket ownership is moving to the pstdio tickets extension. */
7
8
  create(input: CreateWorkspaceInput): Promise<Workspace>;
9
+ /** @deprecated Legacy ticket attempt status mutation. Workspace status automation is extension-owned. */
8
10
  updateAttemptStatus(workspaceId: string, input: UpdateAttemptStatusInput): Promise<UpdateAttemptStatusResponse>;
9
11
  listActivity(workspaceId: string, input?: ListWorkspaceActivityInput): Promise<ListWorkspaceActivityResponse>;
10
12
  removeWorktree(workspaceId: string): Promise<RemoveWorktreeResponse>;
@@ -9,7 +9,7 @@ import type { ParamObjectSchema } from "./types/params";
9
9
  * @example
10
10
  * export const sayHello = defineCommand({
11
11
  * title: "Say hello",
12
- * params: { name: params.text() },
12
+ * params: { name: params.text({ required: true }) },
13
13
  * async run(ctx) {
14
14
  * ctx.params.name; // string
15
15
  * },