@ricsam/r5d-api 0.0.60 → 0.0.62

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,22 +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.agents.start(projectRef, { source, agentType, prompt, worker, requestId })`
62
- - `agents.status/sendPrompt/merge/stop/finishMerge(...)`
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? })`
63
64
  - `sessions.describe(sessionId)`
64
65
  - `sessions.update(sessionId, { name })`
65
66
  - `sessions.delete(sessionId)`
66
67
  - `sessions.conversation(sessionId)`
68
+ - `sessions.events(sessionId, { signal? })`
67
69
  - `sessions.conversationOverview(sessionId)`
68
70
  - `sessions.inspectConversationNode(sessionId, nodeId)`
69
71
  - `sessions.inspectConversationWork(sessionId, workId, { detail? })`
70
- - `sessions.prompt(sessionId, { message, mode, model })`
71
- - `sessions.answerQuestions(sessionId, { answers })`
72
- - `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 })`
73
77
 
74
- `agents.sendPrompt(...)` returns `promptDisposition: "queued" | "started"`, so callers can distinguish delivery to an active generation from a newly started generation. Agent commands never rebind a session to another worker implicitly.
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.
75
79
 
76
- `projects.agents.start(...)` requires `source: { type: "worktree", branchName }`. The source is the named branch worktree, including its committed and uncommitted files.
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.
77
81
 
78
82
  ## Errors
79
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,26 @@ class R5dctlClient {
294
361
  method: "POST",
295
362
  path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions`,
296
363
  body: input
364
+ }),
365
+ start: (projectRef, input) => this.request({
366
+ method: "POST",
367
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions/start`,
368
+ body: input
297
369
  })
298
370
  },
299
- agents: {
300
- start: (projectRef, input) => this.request({
371
+ worktrees: {
372
+ merge: (projectRef, sourceWorktree, input) => this.request({
301
373
  method: "POST",
302
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/agents`,
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`,
303
380
  body: input
304
381
  })
305
382
  }
306
383
  };
307
- agents = {
308
- status: (sessionId) => this.request({
309
- method: "GET",
310
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}`
311
- }),
312
- sendPrompt: (sessionId, input) => this.request({
313
- method: "POST",
314
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/prompt`,
315
- body: input
316
- }),
317
- merge: (sessionId, input) => this.request({
318
- method: "POST",
319
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/merge`,
320
- body: input
321
- }),
322
- stop: (sessionId) => this.request({
323
- method: "POST",
324
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/stop`
325
- }),
326
- finishMerge: (sessionId, input) => this.request({
327
- method: "POST",
328
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/finish-merge`,
329
- body: input
330
- })
331
- };
332
384
  sessions = {
333
385
  recent: (input = {}) => this.request({
334
386
  method: "GET",
@@ -348,6 +400,11 @@ class R5dctlClient {
348
400
  method: "DELETE",
349
401
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}`
350
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),
351
408
  conversation: (sessionId, input = {}) => this.request({
352
409
  method: "GET",
353
410
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/conversation`,
@@ -377,6 +434,11 @@ class R5dctlClient {
377
434
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/prompt`,
378
435
  body: input
379
436
  }),
437
+ stop: (sessionId) => this.request({
438
+ method: "POST",
439
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/stop`,
440
+ body: {}
441
+ }),
380
442
  answerQuestions: (sessionId, input) => this.request({
381
443
  method: "POST",
382
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.60",
3
+ "version": "0.0.62",
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,26 @@ class R5dctlClient {
270
337
  method: "POST",
271
338
  path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions`,
272
339
  body: input
340
+ }),
341
+ start: (projectRef, input) => this.request({
342
+ method: "POST",
343
+ path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/sessions/start`,
344
+ body: input
273
345
  })
274
346
  },
275
- agents: {
276
- start: (projectRef, input) => this.request({
347
+ worktrees: {
348
+ merge: (projectRef, sourceWorktree, input) => this.request({
277
349
  method: "POST",
278
- path: `/api/r5dctl/projects/${encodeURIComponent(projectRef)}/agents`,
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`,
279
356
  body: input
280
357
  })
281
358
  }
282
359
  };
283
- agents = {
284
- status: (sessionId) => this.request({
285
- method: "GET",
286
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}`
287
- }),
288
- sendPrompt: (sessionId, input) => this.request({
289
- method: "POST",
290
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/prompt`,
291
- body: input
292
- }),
293
- merge: (sessionId, input) => this.request({
294
- method: "POST",
295
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/merge`,
296
- body: input
297
- }),
298
- stop: (sessionId) => this.request({
299
- method: "POST",
300
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/stop`
301
- }),
302
- finishMerge: (sessionId, input) => this.request({
303
- method: "POST",
304
- path: `/api/r5dctl/agents/${encodeURIComponent(sessionId)}/finish-merge`,
305
- body: input
306
- })
307
- };
308
360
  sessions = {
309
361
  recent: (input = {}) => this.request({
310
362
  method: "GET",
@@ -324,6 +376,11 @@ class R5dctlClient {
324
376
  method: "DELETE",
325
377
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}`
326
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),
327
384
  conversation: (sessionId, input = {}) => this.request({
328
385
  method: "GET",
329
386
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/conversation`,
@@ -353,6 +410,11 @@ class R5dctlClient {
353
410
  path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/prompt`,
354
411
  body: input
355
412
  }),
413
+ stop: (sessionId) => this.request({
414
+ method: "POST",
415
+ path: `/api/r5dctl/sessions/${encodeURIComponent(sessionId)}/stop`,
416
+ body: {}
417
+ }),
356
418
  answerQuestions: (sessionId, input) => this.request({
357
419
  method: "POST",
358
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.60",
3
+ "version": "0.0.62",
4
4
  "type": "module"
5
5
  }
@@ -1,7 +1,7 @@
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 R5dctlAgentSource = {
4
+ export type R5dctlWorktreeSource = {
5
5
  type: "worktree";
6
6
  branchName: string;
7
7
  };
@@ -69,6 +69,81 @@ export type R5dctlConversationResponse = {
69
69
  pendingQuestions: R5dctlPendingQuestion[];
70
70
  requestedEnvs: R5dctlRequestedEnv[];
71
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: "fresh_context_failed";
135
+ message: string;
136
+ } | {
137
+ type: "shell_result";
138
+ stdout: string;
139
+ stderr: string;
140
+ exitCode: number;
141
+ fileChanges: unknown[];
142
+ cwd: string;
143
+ } | {
144
+ type: string;
145
+ [key: string]: unknown;
146
+ };
72
147
  export type R5dctlConversationWorkDetail = "compact" | "summary" | "full";
73
148
  export type R5dctlConversationWorkMetadata = {
74
149
  id: string;
@@ -98,31 +173,31 @@ export type R5dctlConversationWorkResponse = {
98
173
  detail: R5dctlConversationWorkDetail;
99
174
  workText: string;
100
175
  };
101
- export type R5dctlAgentRunStatus = "provisioning" | "queued" | "running" | "completed" | "failed" | "stopped";
102
- export type R5dctlAgentMergeStatus = {
103
- status: "merged" | "up_to_date" | "conflicts" | "resolving" | "failed" | "stale";
176
+ export type R5dctlSessionRunStatus = "idle" | "provisioning" | "queued" | "running" | "completed" | "failed" | "stopped";
177
+ export type R5dctlWorktreeMergeResponse = {
178
+ status: "merged" | "up_to_date" | "conflicts" | "conflict_worktree_created" | "resolving" | "failed" | "stale";
104
179
  attemptId?: string;
105
- sourceBranch: string;
106
- targetBranch: string;
180
+ sourceWorktree: string;
181
+ targetWorktree: string;
182
+ conflictWorktree?: string;
107
183
  commitHash?: string;
108
184
  resolverSessionId?: string;
109
- resolverBranch?: string;
110
185
  folderPath?: string;
111
186
  worker?: string;
112
187
  conflictedFiles?: string[];
113
188
  failureReason?: string;
114
189
  message?: string;
115
190
  };
116
- export type R5dctlAgentStatusResponse = {
191
+ export type R5dctlMergeConflictMode = "none" | "worktree" | "agent";
192
+ export type R5dctlSessionRunResponse = {
117
193
  sessionId: string;
118
194
  branchName: string;
119
- source: R5dctlAgentSource;
120
195
  baselineCommit: string | null;
121
196
  workspaceHead: string | null;
122
197
  worker: string;
123
- status: R5dctlAgentRunStatus;
124
- runStatus: R5dctlAgentRunStatus;
125
- kind: R5dctlAgentType;
198
+ status: R5dctlSessionRunStatus;
199
+ runStatus: R5dctlSessionRunStatus;
200
+ generation: number;
126
201
  createdAt: string;
127
202
  updatedAt: string;
128
203
  queuedAt?: string;
@@ -137,12 +212,31 @@ export type R5dctlAgentStatusResponse = {
137
212
  diffSummary: string;
138
213
  sessionPath: string;
139
214
  sessionUrl?: string;
140
- merge?: R5dctlAgentMergeStatus;
141
215
  };
142
- export type R5dctlAgentStartResponse = R5dctlAgentStatusResponse;
143
- export type R5dctlAgentPromptResponse = R5dctlAgentStatusResponse & {
216
+ export type R5dctlSessionStartResponse = R5dctlSessionRunResponse;
217
+ export type R5dctlSessionStatusResponse = R5dctlSessionRunResponse;
218
+ export type R5dctlSessionPromptResponse = R5dctlSessionRunResponse & {
144
219
  promptDisposition: "queued" | "started";
145
220
  };
221
+ export type R5dctlSessionStopResponse = {
222
+ sessionId: string;
223
+ status: "stopped";
224
+ };
225
+ type R5dctlSessionStartBaseInput = {
226
+ worker: string;
227
+ model: ModelTier;
228
+ prompt: string;
229
+ requestId: string;
230
+ };
231
+ export type R5dctlSessionStartInput = R5dctlSessionStartBaseInput & ({
232
+ worktree: string;
233
+ newWorktree?: never;
234
+ source?: never;
235
+ } | {
236
+ worktree?: never;
237
+ newWorktree: string;
238
+ source: R5dctlWorktreeSource;
239
+ });
146
240
  export type DeviceAuthorizationStartResponse = {
147
241
  requestId: string;
148
242
  deviceCode: string;
@@ -432,7 +526,9 @@ export declare class R5dctlClient {
432
526
  setToken(token?: string): void;
433
527
  setApiKey(apiKey?: string): void;
434
528
  private resolveBearerToken;
529
+ private authHeaders;
435
530
  private request;
531
+ private events;
436
532
  readonly auth: {
437
533
  deviceStart: (input: {
438
534
  deviceName: string;
@@ -535,32 +631,19 @@ export declare class R5dctlClient {
535
631
  branch?: string;
536
632
  }) => Promise<R5dctlSessionSummary[]>;
537
633
  create: (projectRef: string, input: R5dctlSessionCreateInput) => Promise<R5dctlSessionDescription>;
634
+ start: (projectRef: string, input: R5dctlSessionStartInput) => Promise<R5dctlSessionRunResponse>;
538
635
  };
539
- agents: {
540
- start: (projectRef: string, input: {
541
- source: R5dctlAgentSource;
542
- agentType: R5dctlAgentType;
543
- prompt: string;
636
+ worktrees: {
637
+ merge: (projectRef: string, sourceWorktree: string, input: {
638
+ worker?: string;
639
+ conflictMode: R5dctlMergeConflictMode;
640
+ }) => Promise<R5dctlWorktreeMergeResponse>;
641
+ finishMerge: (projectRef: string, conflictWorktree: string, input: {
544
642
  worker: string;
545
- requestId: string;
546
- }) => Promise<R5dctlAgentStatusResponse>;
643
+ summary?: string;
644
+ }) => Promise<R5dctlWorktreeMergeResponse>;
547
645
  };
548
646
  };
549
- readonly agents: {
550
- status: (sessionId: string) => Promise<R5dctlAgentStatusResponse>;
551
- sendPrompt: (sessionId: string, input: {
552
- prompt: string;
553
- worker: string;
554
- }) => Promise<R5dctlAgentPromptResponse>;
555
- merge: (sessionId: string, input: {
556
- worker: string;
557
- }) => Promise<R5dctlAgentStatusResponse>;
558
- stop: (sessionId: string) => Promise<R5dctlAgentStatusResponse>;
559
- finishMerge: (sessionId: string, input: {
560
- worker: string;
561
- summary: string;
562
- }) => Promise<R5dctlAgentStatusResponse>;
563
- };
564
647
  readonly sessions: {
565
648
  recent: (input?: {
566
649
  limit?: number;
@@ -570,6 +653,10 @@ export declare class R5dctlClient {
570
653
  delete: (sessionId: string) => Promise<{
571
654
  success: boolean;
572
655
  }>;
656
+ status: (sessionId: string) => Promise<R5dctlSessionRunResponse>;
657
+ events: (sessionId: string, input?: {
658
+ signal?: AbortSignal;
659
+ }) => AsyncGenerator<R5dctlSessionEvent, any, any>;
573
660
  conversation: (sessionId: string, input?: R5dctlConversationRenderOptions) => Promise<R5dctlConversationResponse>;
574
661
  conversationOverview: (sessionId: string) => Promise<R5dctlConversationOverviewResponse>;
575
662
  inspectConversationNode: (sessionId: string, nodeId: string) => Promise<R5dctlConversationNodeResponse>;
@@ -577,16 +664,28 @@ export declare class R5dctlClient {
577
664
  detail?: R5dctlConversationWorkDetail;
578
665
  }) => Promise<R5dctlConversationWorkResponse>;
579
666
  prompt: (sessionId: string, input: {
580
- message: string;
581
- mode: ChatMode;
667
+ prompt: string;
668
+ worker: string;
669
+ mode: R5dctlSessionMode;
582
670
  model: ModelTier;
583
- }) => Promise<R5dctlConversationResponse>;
671
+ requestId: string;
672
+ }) => Promise<R5dctlSessionPromptResponse>;
673
+ stop: (sessionId: string) => Promise<R5dctlSessionStopResponse>;
584
674
  answerQuestions: (sessionId: string, input: {
585
675
  answers: string[];
586
- }) => Promise<R5dctlConversationResponse>;
676
+ worker: string;
677
+ mode: ChatMode;
678
+ model: ModelTier;
679
+ requestId: string;
680
+ }) => Promise<R5dctlSessionPromptResponse>;
587
681
  answerEnvRequest: (sessionId: string, input: {
588
682
  envs?: string[];
589
683
  additionalContext?: string;
590
- }) => Promise<R5dctlConversationResponse>;
684
+ worker: string;
685
+ mode: ChatMode;
686
+ model: ModelTier;
687
+ requestId: string;
688
+ }) => Promise<R5dctlSessionPromptResponse>;
591
689
  };
592
690
  }
691
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-api",
3
- "version": "0.0.60",
3
+ "version": "0.0.62",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/index.cjs",
6
6
  "module": "./dist/mjs/index.mjs",