@ricsam/r5d-api 0.0.60 → 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,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.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,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.61",
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,86 @@ 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: "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
+ };
72
152
  export type R5dctlConversationWorkDetail = "compact" | "summary" | "full";
73
153
  export type R5dctlConversationWorkMetadata = {
74
154
  id: string;
@@ -98,31 +178,31 @@ export type R5dctlConversationWorkResponse = {
98
178
  detail: R5dctlConversationWorkDetail;
99
179
  workText: string;
100
180
  };
101
- export type R5dctlAgentRunStatus = "provisioning" | "queued" | "running" | "completed" | "failed" | "stopped";
102
- export type R5dctlAgentMergeStatus = {
103
- status: "merged" | "up_to_date" | "conflicts" | "resolving" | "failed" | "stale";
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";
104
184
  attemptId?: string;
105
- sourceBranch: string;
106
- targetBranch: string;
185
+ sourceWorktree: string;
186
+ targetWorktree: string;
187
+ conflictWorktree?: string;
107
188
  commitHash?: string;
108
189
  resolverSessionId?: string;
109
- resolverBranch?: string;
110
190
  folderPath?: string;
111
191
  worker?: string;
112
192
  conflictedFiles?: string[];
113
193
  failureReason?: string;
114
194
  message?: string;
115
195
  };
116
- export type R5dctlAgentStatusResponse = {
196
+ export type R5dctlMergeConflictMode = "none" | "worktree" | "agent";
197
+ export type R5dctlSessionRunResponse = {
117
198
  sessionId: string;
118
199
  branchName: string;
119
- source: R5dctlAgentSource;
120
200
  baselineCommit: string | null;
121
201
  workspaceHead: string | null;
122
202
  worker: string;
123
- status: R5dctlAgentRunStatus;
124
- runStatus: R5dctlAgentRunStatus;
125
- kind: R5dctlAgentType;
203
+ status: R5dctlSessionRunStatus;
204
+ runStatus: R5dctlSessionRunStatus;
205
+ generation: number;
126
206
  createdAt: string;
127
207
  updatedAt: string;
128
208
  queuedAt?: string;
@@ -137,12 +217,31 @@ export type R5dctlAgentStatusResponse = {
137
217
  diffSummary: string;
138
218
  sessionPath: string;
139
219
  sessionUrl?: string;
140
- merge?: R5dctlAgentMergeStatus;
141
220
  };
142
- export type R5dctlAgentStartResponse = R5dctlAgentStatusResponse;
143
- export type R5dctlAgentPromptResponse = R5dctlAgentStatusResponse & {
221
+ export type R5dctlSessionStartResponse = R5dctlSessionRunResponse;
222
+ export type R5dctlSessionStatusResponse = R5dctlSessionRunResponse;
223
+ export type R5dctlSessionPromptResponse = R5dctlSessionRunResponse & {
144
224
  promptDisposition: "queued" | "started";
145
225
  };
226
+ export type R5dctlSessionStopResponse = {
227
+ sessionId: string;
228
+ status: "stopped";
229
+ };
230
+ type R5dctlSessionStartBaseInput = {
231
+ worker: string;
232
+ model: ModelTier;
233
+ prompt: string;
234
+ requestId: string;
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
+ });
146
245
  export type DeviceAuthorizationStartResponse = {
147
246
  requestId: string;
148
247
  deviceCode: string;
@@ -432,7 +531,9 @@ export declare class R5dctlClient {
432
531
  setToken(token?: string): void;
433
532
  setApiKey(apiKey?: string): void;
434
533
  private resolveBearerToken;
534
+ private authHeaders;
435
535
  private request;
536
+ private events;
436
537
  readonly auth: {
437
538
  deviceStart: (input: {
438
539
  deviceName: string;
@@ -535,32 +636,19 @@ export declare class R5dctlClient {
535
636
  branch?: string;
536
637
  }) => Promise<R5dctlSessionSummary[]>;
537
638
  create: (projectRef: string, input: R5dctlSessionCreateInput) => Promise<R5dctlSessionDescription>;
639
+ start: (projectRef: string, input: R5dctlSessionStartInput) => Promise<R5dctlSessionRunResponse>;
538
640
  };
539
- agents: {
540
- start: (projectRef: string, input: {
541
- source: R5dctlAgentSource;
542
- agentType: R5dctlAgentType;
543
- prompt: string;
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: {
544
647
  worker: string;
545
- requestId: string;
546
- }) => Promise<R5dctlAgentStatusResponse>;
648
+ summary?: string;
649
+ }) => Promise<R5dctlWorktreeMergeResponse>;
547
650
  };
548
651
  };
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
652
  readonly sessions: {
565
653
  recent: (input?: {
566
654
  limit?: number;
@@ -570,6 +658,10 @@ export declare class R5dctlClient {
570
658
  delete: (sessionId: string) => Promise<{
571
659
  success: boolean;
572
660
  }>;
661
+ status: (sessionId: string) => Promise<R5dctlSessionRunResponse>;
662
+ events: (sessionId: string, input?: {
663
+ signal?: AbortSignal;
664
+ }) => AsyncGenerator<R5dctlSessionEvent, any, any>;
573
665
  conversation: (sessionId: string, input?: R5dctlConversationRenderOptions) => Promise<R5dctlConversationResponse>;
574
666
  conversationOverview: (sessionId: string) => Promise<R5dctlConversationOverviewResponse>;
575
667
  inspectConversationNode: (sessionId: string, nodeId: string) => Promise<R5dctlConversationNodeResponse>;
@@ -577,16 +669,28 @@ export declare class R5dctlClient {
577
669
  detail?: R5dctlConversationWorkDetail;
578
670
  }) => Promise<R5dctlConversationWorkResponse>;
579
671
  prompt: (sessionId: string, input: {
580
- message: string;
581
- mode: ChatMode;
672
+ prompt: string;
673
+ worker: string;
674
+ mode: R5dctlSessionMode;
582
675
  model: ModelTier;
583
- }) => Promise<R5dctlConversationResponse>;
676
+ requestId: string;
677
+ }) => Promise<R5dctlSessionPromptResponse>;
678
+ stop: (sessionId: string) => Promise<R5dctlSessionStopResponse>;
584
679
  answerQuestions: (sessionId: string, input: {
585
680
  answers: string[];
586
- }) => Promise<R5dctlConversationResponse>;
681
+ worker: string;
682
+ mode: ChatMode;
683
+ model: ModelTier;
684
+ requestId: string;
685
+ }) => Promise<R5dctlSessionPromptResponse>;
587
686
  answerEnvRequest: (sessionId: string, input: {
588
687
  envs?: string[];
589
688
  additionalContext?: string;
590
- }) => Promise<R5dctlConversationResponse>;
689
+ worker: string;
690
+ mode: ChatMode;
691
+ model: ModelTier;
692
+ requestId: string;
693
+ }) => Promise<R5dctlSessionPromptResponse>;
591
694
  };
592
695
  }
696
+ 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.61",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/index.cjs",
6
6
  "module": "./dist/mjs/index.mjs",