@ricsam/r5d-api 0.0.59 → 0.0.61

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.
package/README.md CHANGED
@@ -58,16 +58,26 @@ Trusted server-side callers that authenticate through custom headers, such as a
58
58
  - `projects.env.get/update/remove(projectId, ...)`
59
59
  - `projects.sessions.list(projectRef, { branch? })`
60
60
  - `projects.sessions.create(projectRef, { branchName, name? })`
61
+ - `projects.sessions.start(projectRef, { worker, model, prompt, requestId, worktree?, newWorktree?, source? })`
62
+ - `projects.worktrees.merge(projectRef, sourceWorktree, { worker?, conflictMode })`
63
+ - `projects.worktrees.finishMerge(projectRef, conflictWorktree, { worker, summary? })`
61
64
  - `sessions.describe(sessionId)`
62
65
  - `sessions.update(sessionId, { name })`
63
66
  - `sessions.delete(sessionId)`
64
67
  - `sessions.conversation(sessionId)`
68
+ - `sessions.events(sessionId, { signal? })`
65
69
  - `sessions.conversationOverview(sessionId)`
66
70
  - `sessions.inspectConversationNode(sessionId, nodeId)`
67
71
  - `sessions.inspectConversationWork(sessionId, workId, { detail? })`
68
- - `sessions.prompt(sessionId, { message, mode, model })`
69
- - `sessions.answerQuestions(sessionId, { answers })`
70
- - `sessions.answerEnvRequest(sessionId, { envs?, additionalContext? })`
72
+ - `sessions.status(sessionId)`
73
+ - `sessions.prompt(sessionId, { prompt, worker, mode, model, requestId })`
74
+ - `sessions.stop(sessionId)`
75
+ - `sessions.answerQuestions(sessionId, { answers, worker, mode, model, requestId })`
76
+ - `sessions.answerEnvRequest(sessionId, { envs?, additionalContext?, worker, mode, model, requestId })`
77
+
78
+ `sessions.prompt(...)` returns `promptDisposition: "queued" | "started"`, so callers can distinguish delivery to an active generation from a newly started generation. Session commands never rebind a session to another worker implicitly.
79
+
80
+ `projects.sessions.start(...)` selects either an existing `worktree`, or a `newWorktree` plus `source: { type: "worktree", branchName }`. Session events are exposed as an authenticated async iterable over the raw `/api/sessions/:sessionId/events` SSE stream.
71
81
 
72
82
  ## Errors
73
83
 
@@ -49,6 +49,56 @@ function withQuery(path, query) {
49
49
  const queryString = params.toString();
50
50
  return queryString.length > 0 ? `${path}?${queryString}` : path;
51
51
  }
52
+ function parseSseFrame(frame) {
53
+ const dataLines = [];
54
+ let eventType;
55
+ for (const line of frame.split(/\r?\n/)) {
56
+ if (line.length === 0 || line.startsWith(":")) continue;
57
+ const separator = line.indexOf(":");
58
+ const field = separator < 0 ? line : line.slice(0, separator);
59
+ const value = separator < 0 ? "" : line.slice(separator + 1).replace(/^ /, "");
60
+ if (field === "event") eventType = value;
61
+ if (field === "data") dataLines.push(value);
62
+ }
63
+ if (dataLines.length === 0) return null;
64
+ const parsed = safeJsonParse(dataLines.join("\n"));
65
+ if (typeof parsed === "object" && parsed !== null) {
66
+ const record = parsed;
67
+ if (typeof record.type !== "string" && eventType) record.type = eventType;
68
+ if (typeof record.type === "string") return record;
69
+ }
70
+ return eventType ? { type: eventType, data: parsed } : null;
71
+ }
72
+ async function* parseSseStream(stream) {
73
+ const reader = stream.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffer = "";
76
+ let completed = false;
77
+ try {
78
+ while (true) {
79
+ const { done, value } = await reader.read();
80
+ buffer += decoder.decode(value, { stream: !done });
81
+ let boundary = buffer.search(/\r?\n\r?\n/);
82
+ while (boundary >= 0) {
83
+ const frame = buffer.slice(0, boundary);
84
+ const separator = buffer.slice(boundary).match(/^\r?\n\r?\n/)?.[0] ?? "\n\n";
85
+ buffer = buffer.slice(boundary + separator.length);
86
+ const event2 = parseSseFrame(frame);
87
+ if (event2) yield event2;
88
+ boundary = buffer.search(/\r?\n\r?\n/);
89
+ }
90
+ if (done) {
91
+ completed = true;
92
+ break;
93
+ }
94
+ }
95
+ const event = parseSseFrame(buffer);
96
+ if (event) yield event;
97
+ } finally {
98
+ if (!completed) await reader.cancel().catch(() => void 0);
99
+ reader.releaseLock();
100
+ }
101
+ }
52
102
  class R5dctlClient {
53
103
  fetchImpl;
54
104
  baseUrl;
@@ -76,26 +126,25 @@ class R5dctlClient {
76
126
  resolveBearerToken() {
77
127
  return this.token ?? this.apiKey;
78
128
  }
79
- async request(input) {
80
- const authMode = input.auth ?? "auto";
129
+ authHeaders(path, accept, auth = "auto") {
130
+ const headers = { ...this.extraHeaders, Accept: accept };
131
+ if (auth === "none") return headers;
81
132
  const bearerToken = this.resolveBearerToken();
82
- const headers = {
83
- ...this.extraHeaders,
84
- Accept: "application/json"
85
- };
86
- if (authMode === "auto") {
87
- if (!bearerToken) {
88
- if (this.authMode !== "headers") {
89
- throw new R5dctlApiError("Authentication required. Set token or apiKey in R5dctlClient.", {
90
- status: 401,
91
- path: input.path,
92
- body: { error: "Missing credentials" }
93
- });
94
- }
95
- } else {
96
- headers.Authorization = `Bearer ${bearerToken}`;
133
+ if (!bearerToken) {
134
+ if (this.authMode !== "headers") {
135
+ throw new R5dctlApiError("Authentication required. Set token or apiKey in R5dctlClient.", {
136
+ status: 401,
137
+ path,
138
+ body: { error: "Missing credentials" }
139
+ });
97
140
  }
141
+ } else {
142
+ headers.Authorization = `Bearer ${bearerToken}`;
98
143
  }
144
+ return headers;
145
+ }
146
+ async request(input) {
147
+ const headers = this.authHeaders(input.path, "application/json", input.auth ?? "auto");
99
148
  const hasBody = input.body !== void 0;
100
149
  if (hasBody) {
101
150
  headers["Content-Type"] = "application/json";
@@ -118,6 +167,24 @@ class R5dctlClient {
118
167
  }
119
168
  return payload;
120
169
  }
170
+ async *events(sessionId, input = {}) {
171
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/events`;
172
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
173
+ method: "GET",
174
+ headers: this.authHeaders(path, "text/event-stream"),
175
+ signal: input.signal
176
+ });
177
+ if (!response.ok) {
178
+ const text = await response.text();
179
+ const payload = text.length > 0 ? safeJsonParse(text) : null;
180
+ const message = typeof payload === "object" && payload && "error" in payload && typeof payload.error === "string" ? payload.error : `Request failed (${response.status})`;
181
+ throw new R5dctlApiError(message, { status: response.status, path, body: payload });
182
+ }
183
+ if (!response.body) {
184
+ throw new R5dctlApiError("Session event stream returned no body", { status: response.status, path, body: null });
185
+ }
186
+ yield* parseSseStream(response.body);
187
+ }
121
188
  auth = {
122
189
  deviceStart: (input) => this.request({
123
190
  method: "POST",
@@ -294,41 +361,25 @@ class R5dctlClient {
294
361
  method: "POST",
295
362
  path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions`,
296
363
  body: input
297
- })
298
- },
299
- agents: {
364
+ }),
300
365
  start: (projectRef, input) => this.request({
301
366
  method: "POST",
302
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/agents`,
367
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions/start`,
303
368
  body: input
304
369
  })
305
370
  },
306
- mergeChanges: (projectRef, input) => this.request({
307
- method: "POST",
308
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/merge-changes`,
309
- body: input
310
- }),
311
- continueMerge: (projectRef, input) => this.request({
312
- method: "POST",
313
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/continue-merge`,
314
- body: input
315
- }),
316
- abortMerge: (projectRef, input) => this.request({
317
- method: "POST",
318
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/abort-merge`,
319
- body: input
320
- })
321
- };
322
- agents = {
323
- status: (sessionId) => this.request({
324
- method: "GET",
325
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}`
326
- }),
327
- sendPrompt: (sessionId, input) => this.request({
328
- method: "POST",
329
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/prompt`,
330
- body: input
331
- })
371
+ worktrees: {
372
+ merge: (projectRef, sourceWorktree, input) => this.request({
373
+ method: "POST",
374
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/worktrees/${encodeURIComponent(sourceWorktree)}/merge`,
375
+ body: input
376
+ }),
377
+ finishMerge: (projectRef, conflictWorktree, input) => this.request({
378
+ method: "POST",
379
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/worktrees/${encodeURIComponent(conflictWorktree)}/merge/finish`,
380
+ body: input
381
+ })
382
+ }
332
383
  };
333
384
  sessions = {
334
385
  recent: (input = {}) => this.request({
@@ -349,6 +400,11 @@ class R5dctlClient {
349
400
  method: "DELETE",
350
401
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}`
351
402
  }),
403
+ status: (sessionId) => this.request({
404
+ method: "GET",
405
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/status`
406
+ }),
407
+ events: (sessionId, input = {}) => this.events(sessionId, input),
352
408
  conversation: (sessionId, input = {}) => this.request({
353
409
  method: "GET",
354
410
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/conversation`,
@@ -378,6 +434,11 @@ class R5dctlClient {
378
434
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/prompt`,
379
435
  body: input
380
436
  }),
437
+ stop: (sessionId) => this.request({
438
+ method: "POST",
439
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/stop`,
440
+ body: {}
441
+ }),
381
442
  answerQuestions: (sessionId, input) => this.request({
382
443
  method: "POST",
383
444
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/answer-questions`,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-api",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "type": "commonjs"
5
5
  }
@@ -25,6 +25,56 @@ function withQuery(path, query) {
25
25
  const queryString = params.toString();
26
26
  return queryString.length > 0 ? `${path}?${queryString}` : path;
27
27
  }
28
+ function parseSseFrame(frame) {
29
+ const dataLines = [];
30
+ let eventType;
31
+ for (const line of frame.split(/\r?\n/)) {
32
+ if (line.length === 0 || line.startsWith(":")) continue;
33
+ const separator = line.indexOf(":");
34
+ const field = separator < 0 ? line : line.slice(0, separator);
35
+ const value = separator < 0 ? "" : line.slice(separator + 1).replace(/^ /, "");
36
+ if (field === "event") eventType = value;
37
+ if (field === "data") dataLines.push(value);
38
+ }
39
+ if (dataLines.length === 0) return null;
40
+ const parsed = safeJsonParse(dataLines.join("\n"));
41
+ if (typeof parsed === "object" && parsed !== null) {
42
+ const record = parsed;
43
+ if (typeof record.type !== "string" && eventType) record.type = eventType;
44
+ if (typeof record.type === "string") return record;
45
+ }
46
+ return eventType ? { type: eventType, data: parsed } : null;
47
+ }
48
+ async function* parseSseStream(stream) {
49
+ const reader = stream.getReader();
50
+ const decoder = new TextDecoder();
51
+ let buffer = "";
52
+ let completed = false;
53
+ try {
54
+ while (true) {
55
+ const { done, value } = await reader.read();
56
+ buffer += decoder.decode(value, { stream: !done });
57
+ let boundary = buffer.search(/\r?\n\r?\n/);
58
+ while (boundary >= 0) {
59
+ const frame = buffer.slice(0, boundary);
60
+ const separator = buffer.slice(boundary).match(/^\r?\n\r?\n/)?.[0] ?? "\n\n";
61
+ buffer = buffer.slice(boundary + separator.length);
62
+ const event2 = parseSseFrame(frame);
63
+ if (event2) yield event2;
64
+ boundary = buffer.search(/\r?\n\r?\n/);
65
+ }
66
+ if (done) {
67
+ completed = true;
68
+ break;
69
+ }
70
+ }
71
+ const event = parseSseFrame(buffer);
72
+ if (event) yield event;
73
+ } finally {
74
+ if (!completed) await reader.cancel().catch(() => void 0);
75
+ reader.releaseLock();
76
+ }
77
+ }
28
78
  class R5dctlClient {
29
79
  fetchImpl;
30
80
  baseUrl;
@@ -52,26 +102,25 @@ class R5dctlClient {
52
102
  resolveBearerToken() {
53
103
  return this.token ?? this.apiKey;
54
104
  }
55
- async request(input) {
56
- const authMode = input.auth ?? "auto";
105
+ authHeaders(path, accept, auth = "auto") {
106
+ const headers = { ...this.extraHeaders, Accept: accept };
107
+ if (auth === "none") return headers;
57
108
  const bearerToken = this.resolveBearerToken();
58
- const headers = {
59
- ...this.extraHeaders,
60
- Accept: "application/json"
61
- };
62
- if (authMode === "auto") {
63
- if (!bearerToken) {
64
- if (this.authMode !== "headers") {
65
- throw new R5dctlApiError("Authentication required. Set token or apiKey in R5dctlClient.", {
66
- status: 401,
67
- path: input.path,
68
- body: { error: "Missing credentials" }
69
- });
70
- }
71
- } else {
72
- headers.Authorization = `Bearer ${bearerToken}`;
109
+ if (!bearerToken) {
110
+ if (this.authMode !== "headers") {
111
+ throw new R5dctlApiError("Authentication required. Set token or apiKey in R5dctlClient.", {
112
+ status: 401,
113
+ path,
114
+ body: { error: "Missing credentials" }
115
+ });
73
116
  }
117
+ } else {
118
+ headers.Authorization = `Bearer ${bearerToken}`;
74
119
  }
120
+ return headers;
121
+ }
122
+ async request(input) {
123
+ const headers = this.authHeaders(input.path, "application/json", input.auth ?? "auto");
75
124
  const hasBody = input.body !== void 0;
76
125
  if (hasBody) {
77
126
  headers["Content-Type"] = "application/json";
@@ -94,6 +143,24 @@ class R5dctlClient {
94
143
  }
95
144
  return payload;
96
145
  }
146
+ async *events(sessionId, input = {}) {
147
+ const path = `/api/sessions/${encodeURIComponent(sessionId)}/events`;
148
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
149
+ method: "GET",
150
+ headers: this.authHeaders(path, "text/event-stream"),
151
+ signal: input.signal
152
+ });
153
+ if (!response.ok) {
154
+ const text = await response.text();
155
+ const payload = text.length > 0 ? safeJsonParse(text) : null;
156
+ const message = typeof payload === "object" && payload && "error" in payload && typeof payload.error === "string" ? payload.error : `Request failed (${response.status})`;
157
+ throw new R5dctlApiError(message, { status: response.status, path, body: payload });
158
+ }
159
+ if (!response.body) {
160
+ throw new R5dctlApiError("Session event stream returned no body", { status: response.status, path, body: null });
161
+ }
162
+ yield* parseSseStream(response.body);
163
+ }
97
164
  auth = {
98
165
  deviceStart: (input) => this.request({
99
166
  method: "POST",
@@ -270,41 +337,25 @@ class R5dctlClient {
270
337
  method: "POST",
271
338
  path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions`,
272
339
  body: input
273
- })
274
- },
275
- agents: {
340
+ }),
276
341
  start: (projectRef, input) => this.request({
277
342
  method: "POST",
278
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/agents`,
343
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions/start`,
279
344
  body: input
280
345
  })
281
346
  },
282
- mergeChanges: (projectRef, input) => this.request({
283
- method: "POST",
284
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/merge-changes`,
285
- body: input
286
- }),
287
- continueMerge: (projectRef, input) => this.request({
288
- method: "POST",
289
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/continue-merge`,
290
- body: input
291
- }),
292
- abortMerge: (projectRef, input) => this.request({
293
- method: "POST",
294
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/abort-merge`,
295
- body: input
296
- })
297
- };
298
- agents = {
299
- status: (sessionId) => this.request({
300
- method: "GET",
301
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}`
302
- }),
303
- sendPrompt: (sessionId, input) => this.request({
304
- method: "POST",
305
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/prompt`,
306
- body: input
307
- })
347
+ worktrees: {
348
+ merge: (projectRef, sourceWorktree, input) => this.request({
349
+ method: "POST",
350
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/worktrees/${encodeURIComponent(sourceWorktree)}/merge`,
351
+ body: input
352
+ }),
353
+ finishMerge: (projectRef, conflictWorktree, input) => this.request({
354
+ method: "POST",
355
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/worktrees/${encodeURIComponent(conflictWorktree)}/merge/finish`,
356
+ body: input
357
+ })
358
+ }
308
359
  };
309
360
  sessions = {
310
361
  recent: (input = {}) => this.request({
@@ -325,6 +376,11 @@ class R5dctlClient {
325
376
  method: "DELETE",
326
377
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}`
327
378
  }),
379
+ status: (sessionId) => this.request({
380
+ method: "GET",
381
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/status`
382
+ }),
383
+ events: (sessionId, input = {}) => this.events(sessionId, input),
328
384
  conversation: (sessionId, input = {}) => this.request({
329
385
  method: "GET",
330
386
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/conversation`,
@@ -354,6 +410,11 @@ class R5dctlClient {
354
410
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/prompt`,
355
411
  body: input
356
412
  }),
413
+ stop: (sessionId) => this.request({
414
+ method: "POST",
415
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/stop`,
416
+ body: {}
417
+ }),
357
418
  answerQuestions: (sessionId, input) => this.request({
358
419
  method: "POST",
359
420
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/answer-questions`,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-api",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "type": "module"
5
5
  }
@@ -1,6 +1,10 @@
1
- export type ChatMode = "ask" | "plan" | "build" | "agent" | "explore" | "test" | "research" | "security_review" | "large_diff_remediation" | "merge_conflict_resolution";
1
+ export type ChatMode = "ask" | "plan" | "build" | "agent" | "security_review" | "large_diff_remediation" | "merge_conflict_resolution";
2
+ export type R5dctlSessionMode = "ask" | "plan" | "build" | "agent";
2
3
  export type ModelTier = "low" | "medium" | "high" | "max";
3
- export type R5dctlAgentType = "research" | "debug" | "test";
4
+ export type R5dctlWorktreeSource = {
5
+ type: "worktree";
6
+ branchName: string;
7
+ };
4
8
  export type R5dctlProject = {
5
9
  id: string;
6
10
  namespace: string;
@@ -65,6 +69,86 @@ export type R5dctlConversationResponse = {
65
69
  pendingQuestions: R5dctlPendingQuestion[];
66
70
  requestedEnvs: R5dctlRequestedEnv[];
67
71
  };
72
+ export type R5dctlSessionEvent = {
73
+ type: "text_delta";
74
+ text: string;
75
+ } | {
76
+ type: "thinking_delta";
77
+ text: string;
78
+ } | {
79
+ type: "tool_use_start";
80
+ toolCallId: string;
81
+ toolName: string;
82
+ } | {
83
+ type: "input_json_delta";
84
+ toolCallId: string;
85
+ partial_json: string;
86
+ } | {
87
+ type: "stream_reset";
88
+ attempt: number;
89
+ maxAttempts: number;
90
+ } | {
91
+ type: "tool_call";
92
+ data: unknown;
93
+ } | {
94
+ type: "tool_progress";
95
+ toolCallId: string;
96
+ progress: unknown;
97
+ } | {
98
+ type: "response";
99
+ data: {
100
+ node: unknown;
101
+ optimisticResponseId?: string;
102
+ conversationRevision: number;
103
+ };
104
+ } | {
105
+ type: "session";
106
+ data: unknown;
107
+ } | {
108
+ type: "status";
109
+ status: unknown;
110
+ statusVersion: number;
111
+ } | {
112
+ type: "files_updated";
113
+ files: unknown[];
114
+ } | {
115
+ type: "token_usage";
116
+ input: number;
117
+ output: number;
118
+ contextWindow?: number;
119
+ } | {
120
+ type: "queue_updated";
121
+ queue: unknown;
122
+ queueVersion: unknown;
123
+ } | {
124
+ type: "done";
125
+ } | {
126
+ type: "stopped";
127
+ } | {
128
+ type: "error";
129
+ message: string;
130
+ } | {
131
+ type: "agent_running";
132
+ running: boolean;
133
+ } | {
134
+ type: "context_continued";
135
+ sessionId: string;
136
+ target: unknown;
137
+ partNumber: number;
138
+ } | {
139
+ type: "context_handoff_failed";
140
+ message: string;
141
+ } | {
142
+ type: "shell_result";
143
+ stdout: string;
144
+ stderr: string;
145
+ exitCode: number;
146
+ fileChanges: unknown[];
147
+ cwd: string;
148
+ } | {
149
+ type: string;
150
+ [key: string]: unknown;
151
+ };
68
152
  export type R5dctlConversationWorkDetail = "compact" | "summary" | "full";
69
153
  export type R5dctlConversationWorkMetadata = {
70
154
  id: string;
@@ -94,54 +178,70 @@ export type R5dctlConversationWorkResponse = {
94
178
  detail: R5dctlConversationWorkDetail;
95
179
  workText: string;
96
180
  };
97
- export type R5dctlAgentStartResponse = {
98
- sessionId: string;
99
- branchName: string;
100
- baseBranch: string;
101
- baseCommit: string;
102
- status: "running";
181
+ export type R5dctlSessionRunStatus = "idle" | "provisioning" | "queued" | "running" | "completed" | "failed" | "stopped";
182
+ export type R5dctlWorktreeMergeResponse = {
183
+ status: "merged" | "up_to_date" | "conflicts" | "conflict_worktree_created" | "resolving" | "failed" | "stale";
184
+ attemptId?: string;
185
+ sourceWorktree: string;
186
+ targetWorktree: string;
187
+ conflictWorktree?: string;
188
+ commitHash?: string;
189
+ resolverSessionId?: string;
190
+ folderPath?: string;
191
+ worker?: string;
192
+ conflictedFiles?: string[];
193
+ failureReason?: string;
194
+ message?: string;
103
195
  };
104
- export type R5dctlAgentStatusResponse = {
196
+ export type R5dctlMergeConflictMode = "none" | "worktree" | "agent";
197
+ export type R5dctlSessionRunResponse = {
105
198
  sessionId: string;
106
199
  branchName: string;
107
- baseBranch: string | null;
108
- baseCommit: string | null;
109
- status: "running" | "completed" | "failed";
110
- startedAt: string;
200
+ baselineCommit: string | null;
201
+ workspaceHead: string | null;
202
+ worker: string;
203
+ status: R5dctlSessionRunStatus;
204
+ runStatus: R5dctlSessionRunStatus;
205
+ generation: number;
206
+ createdAt: string;
111
207
  updatedAt: string;
208
+ queuedAt?: string;
209
+ startedAt?: string;
112
210
  completedAt?: string;
113
211
  failedAt?: string;
212
+ stoppedAt?: string;
114
213
  error?: string;
115
- headCommit: string;
214
+ headCommit: string | null;
215
+ headCommitError?: string;
116
216
  summary: string;
117
217
  diffSummary: string;
218
+ sessionPath: string;
219
+ sessionUrl?: string;
118
220
  };
119
- export type R5dctlMergeResult = {
120
- type: "merge_branch";
121
- status: "merged";
122
- sourceBranch: string;
123
- targetBranch: string;
124
- commitHash: string;
125
- message: string;
126
- } | {
127
- type: "merge_branch";
128
- status: "conflicts";
129
- sourceBranch: string;
130
- targetBranch: string;
131
- conflictedFiles: string[];
132
- message: string;
221
+ export type R5dctlSessionStartResponse = R5dctlSessionRunResponse;
222
+ export type R5dctlSessionStatusResponse = R5dctlSessionRunResponse;
223
+ export type R5dctlSessionPromptResponse = R5dctlSessionRunResponse & {
224
+ promptDisposition: "queued" | "started";
133
225
  };
134
- export type R5dctlContinueMergeResult = {
135
- type: "continue_merge";
136
- status: "committed";
137
- commitHash: string;
138
- message: string;
226
+ export type R5dctlSessionStopResponse = {
227
+ sessionId: string;
228
+ status: "stopped";
139
229
  };
140
- export type R5dctlAbortMergeResult = {
141
- type: "abort_merge";
142
- status: "aborted";
143
- message: string;
230
+ type R5dctlSessionStartBaseInput = {
231
+ worker: string;
232
+ model: ModelTier;
233
+ prompt: string;
234
+ requestId: string;
144
235
  };
236
+ export type R5dctlSessionStartInput = R5dctlSessionStartBaseInput & ({
237
+ worktree: string;
238
+ newWorktree?: never;
239
+ source?: never;
240
+ } | {
241
+ worktree?: never;
242
+ newWorktree: string;
243
+ source: R5dctlWorktreeSource;
244
+ });
145
245
  export type DeviceAuthorizationStartResponse = {
146
246
  requestId: string;
147
247
  deviceCode: string;
@@ -256,7 +356,7 @@ export type R5dctlWorkspaceStatus = {
256
356
  desiredCanonicalHead: string | null;
257
357
  dirtyGeneration: number;
258
358
  publishedGeneration: number;
259
- checkpointState: "idle" | "scheduled" | "preparing" | "submitting" | "blocked";
359
+ publicationState: "idle" | "scheduled" | "preparing" | "submitting" | "blocked";
260
360
  casRetries: number;
261
361
  lastNotifiedHead: string | null;
262
362
  lastNotifiedAt: string | null;
@@ -431,7 +531,9 @@ export declare class R5dctlClient {
431
531
  setToken(token?: string): void;
432
532
  setApiKey(apiKey?: string): void;
433
533
  private resolveBearerToken;
534
+ private authHeaders;
434
535
  private request;
536
+ private events;
435
537
  readonly auth: {
436
538
  deviceStart: (input: {
437
539
  deviceName: string;
@@ -534,30 +636,18 @@ export declare class R5dctlClient {
534
636
  branch?: string;
535
637
  }) => Promise<R5dctlSessionSummary[]>;
536
638
  create: (projectRef: string, input: R5dctlSessionCreateInput) => Promise<R5dctlSessionDescription>;
639
+ start: (projectRef: string, input: R5dctlSessionStartInput) => Promise<R5dctlSessionRunResponse>;
537
640
  };
538
- agents: {
539
- start: (projectRef: string, input: {
540
- sourceBranch: string;
541
- agentType: R5dctlAgentType;
542
- prompt: string;
543
- }) => Promise<R5dctlAgentStartResponse>;
641
+ worktrees: {
642
+ merge: (projectRef: string, sourceWorktree: string, input: {
643
+ worker?: string;
644
+ conflictMode: R5dctlMergeConflictMode;
645
+ }) => Promise<R5dctlWorktreeMergeResponse>;
646
+ finishMerge: (projectRef: string, conflictWorktree: string, input: {
647
+ worker: string;
648
+ summary?: string;
649
+ }) => Promise<R5dctlWorktreeMergeResponse>;
544
650
  };
545
- mergeChanges: (projectRef: string, input: {
546
- targetBranch: string;
547
- sourceBranch: string;
548
- }) => Promise<R5dctlMergeResult>;
549
- continueMerge: (projectRef: string, input: {
550
- targetBranch: string;
551
- }) => Promise<R5dctlContinueMergeResult>;
552
- abortMerge: (projectRef: string, input: {
553
- targetBranch: string;
554
- }) => Promise<R5dctlAbortMergeResult>;
555
- };
556
- readonly agents: {
557
- status: (sessionId: string) => Promise<R5dctlAgentStatusResponse>;
558
- sendPrompt: (sessionId: string, input: {
559
- prompt: string;
560
- }) => Promise<R5dctlAgentStatusResponse>;
561
651
  };
562
652
  readonly sessions: {
563
653
  recent: (input?: {
@@ -568,6 +658,10 @@ export declare class R5dctlClient {
568
658
  delete: (sessionId: string) => Promise<{
569
659
  success: boolean;
570
660
  }>;
661
+ status: (sessionId: string) => Promise<R5dctlSessionRunResponse>;
662
+ events: (sessionId: string, input?: {
663
+ signal?: AbortSignal;
664
+ }) => AsyncGenerator<R5dctlSessionEvent, any, any>;
571
665
  conversation: (sessionId: string, input?: R5dctlConversationRenderOptions) => Promise<R5dctlConversationResponse>;
572
666
  conversationOverview: (sessionId: string) => Promise<R5dctlConversationOverviewResponse>;
573
667
  inspectConversationNode: (sessionId: string, nodeId: string) => Promise<R5dctlConversationNodeResponse>;
@@ -575,16 +669,28 @@ export declare class R5dctlClient {
575
669
  detail?: R5dctlConversationWorkDetail;
576
670
  }) => Promise<R5dctlConversationWorkResponse>;
577
671
  prompt: (sessionId: string, input: {
578
- message: string;
579
- mode: ChatMode;
672
+ prompt: string;
673
+ worker: string;
674
+ mode: R5dctlSessionMode;
580
675
  model: ModelTier;
581
- }) => Promise<R5dctlConversationResponse>;
676
+ requestId: string;
677
+ }) => Promise<R5dctlSessionPromptResponse>;
678
+ stop: (sessionId: string) => Promise<R5dctlSessionStopResponse>;
582
679
  answerQuestions: (sessionId: string, input: {
583
680
  answers: string[];
584
- }) => Promise<R5dctlConversationResponse>;
681
+ worker: string;
682
+ mode: ChatMode;
683
+ model: ModelTier;
684
+ requestId: string;
685
+ }) => Promise<R5dctlSessionPromptResponse>;
585
686
  answerEnvRequest: (sessionId: string, input: {
586
687
  envs?: string[];
587
688
  additionalContext?: string;
588
- }) => Promise<R5dctlConversationResponse>;
689
+ worker: string;
690
+ mode: ChatMode;
691
+ model: ModelTier;
692
+ requestId: string;
693
+ }) => Promise<R5dctlSessionPromptResponse>;
589
694
  };
590
695
  }
696
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-api",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/index.cjs",
6
6
  "module": "./dist/mjs/index.mjs",