@loopstack/github-oauth-example 0.3.0 → 0.4.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.
@@ -1,7 +1,7 @@
1
1
  import { TestingModule } from '@nestjs/testing';
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
  import { z } from 'zod';
4
- import { RunContext, WorkflowEntity, getBlockArgsSchema, getBlockConfig, getBlockTools } from '@loopstack/common';
4
+ import { RunContext, WorkflowEntity, getBlockArgsSchema, getBlockConfig } from '@loopstack/common';
5
5
  import { WorkflowProcessorService } from '@loopstack/core';
6
6
  import {
7
7
  GitHubCreateIssueCommentTool,
@@ -35,7 +35,7 @@ import { ToolMock, createStatelessContext, createWorkflowTest } from '@loopstack
35
35
  import { GitHubReposOverviewWorkflow } from '../github-repos-overview.workflow';
36
36
 
37
37
  const mockOAuthWorkflow = {
38
- run: vi.fn(),
38
+ run: vi.fn().mockResolvedValue({ workflowId: 'sub-1' }),
39
39
  };
40
40
 
41
41
  function applyAllGitHubToolMocks(builder: ReturnType<typeof createWorkflowTest>) {
@@ -72,7 +72,7 @@ function buildWorkflowTest() {
72
72
  createWorkflowTest()
73
73
  .forWorkflow(GitHubReposOverviewWorkflow)
74
74
  .withImports(OAuthModule)
75
- .withMock(OAuthWorkflow, mockOAuthWorkflow),
75
+ .withOverride(OAuthWorkflow, mockOAuthWorkflow),
76
76
  );
77
77
  }
78
78
 
@@ -130,20 +130,16 @@ describe('GitHubReposOverviewWorkflow', () => {
130
130
  expect(getBlockConfig(workflow)).toBeDefined();
131
131
  });
132
132
 
133
- it('should have GitHub tools available', () => {
134
- const tools = getBlockTools(workflow);
135
- expect(tools).toBeDefined();
136
-
137
- expect(tools).toContain('gitHubGetAuthenticatedUser');
138
- expect(tools).toContain('gitHubListUserOrgs');
139
- expect(tools).toContain('gitHubGetRepo');
140
-
141
- expect(tools).toContain('gitHubListBranches');
142
- expect(tools).toContain('gitHubListIssues');
143
- expect(tools).toContain('gitHubListPullRequests');
144
- expect(tools).toContain('gitHubListDirectory');
145
- expect(tools).toContain('gitHubListWorkflowRuns');
146
- expect(tools).toContain('gitHubSearchCode');
133
+ it('should have GitHub tools available via constructor injection', () => {
134
+ expect(mockGetAuthenticatedUser).toBeDefined();
135
+ expect(mockListUserOrgs).toBeDefined();
136
+ expect(mockGetRepo).toBeDefined();
137
+ expect(mockListBranches).toBeDefined();
138
+ expect(mockListIssues).toBeDefined();
139
+ expect(mockListPullRequests).toBeDefined();
140
+ expect(mockListDirectory).toBeDefined();
141
+ expect(mockListWorkflowRuns).toBeDefined();
142
+ expect(mockSearchCode).toBeDefined();
147
143
  });
148
144
  });
149
145
 
@@ -330,9 +326,7 @@ describe('GitHubReposOverviewWorkflow', () => {
330
326
  expect(result.place).toBe('end');
331
327
 
332
328
  // Verify markdown document was created
333
- expect(result.documents).toEqual(
334
- expect.arrayContaining([expect.objectContaining({ className: 'MarkdownDocument' })]),
335
- );
329
+ expect(result.documents).toEqual(expect.arrayContaining([expect.objectContaining({ documentName: 'markdown' })]));
336
330
 
337
331
  // Verify all read tools were called
338
332
  expect(mockGetAuthenticatedUser.call).toHaveBeenCalledTimes(1);
@@ -352,10 +346,7 @@ describe('GitHubReposOverviewWorkflow', () => {
352
346
 
353
347
  await processor.process(workflow, args, context);
354
348
 
355
- expect(mockGetRepo.call).toHaveBeenCalledWith(
356
- expect.objectContaining({ owner: 'octocat', repo: 'Hello-World' }),
357
- undefined,
358
- );
349
+ expect(mockGetRepo.call).toHaveBeenCalledWith(expect.objectContaining({ owner: 'octocat', repo: 'Hello-World' }));
359
350
  });
360
351
 
361
352
  it('should pass owner and repo to gitHubListBranches', async () => {
@@ -366,7 +357,6 @@ describe('GitHubReposOverviewWorkflow', () => {
366
357
 
367
358
  expect(mockListBranches.call).toHaveBeenCalledWith(
368
359
  expect.objectContaining({ owner: 'octocat', repo: 'Hello-World' }),
369
- undefined,
370
360
  );
371
361
  });
372
362
 
@@ -378,7 +368,6 @@ describe('GitHubReposOverviewWorkflow', () => {
378
368
 
379
369
  expect(mockListIssues.call).toHaveBeenCalledWith(
380
370
  expect.objectContaining({ owner: 'octocat', repo: 'Hello-World', state: 'open' }),
381
- undefined,
382
371
  );
383
372
  });
384
373
 
@@ -390,7 +379,6 @@ describe('GitHubReposOverviewWorkflow', () => {
390
379
 
391
380
  expect(mockListPullRequests.call).toHaveBeenCalledWith(
392
381
  expect.objectContaining({ owner: 'octocat', repo: 'Hello-World', state: 'open' }),
393
- undefined,
394
382
  );
395
383
  });
396
384
 
@@ -402,7 +390,6 @@ describe('GitHubReposOverviewWorkflow', () => {
402
390
 
403
391
  expect(mockListDirectory.call).toHaveBeenCalledWith(
404
392
  expect.objectContaining({ owner: 'octocat', repo: 'Hello-World' }),
405
- undefined,
406
393
  );
407
394
  });
408
395
 
@@ -414,7 +401,6 @@ describe('GitHubReposOverviewWorkflow', () => {
414
401
 
415
402
  expect(mockListWorkflowRuns.call).toHaveBeenCalledWith(
416
403
  expect.objectContaining({ owner: 'octocat', repo: 'Hello-World' }),
417
- undefined,
418
404
  );
419
405
  });
420
406
 
@@ -424,10 +410,7 @@ describe('GitHubReposOverviewWorkflow', () => {
424
410
 
425
411
  await processor.process(workflow, args, context);
426
412
 
427
- expect(mockSearchCode.call).toHaveBeenCalledWith(
428
- expect.objectContaining({ query: 'repo:octocat/Hello-World' }),
429
- undefined,
430
- );
413
+ expect(mockSearchCode.call).toHaveBeenCalledWith(expect.objectContaining({ query: 'repo:octocat/Hello-World' }));
431
414
  });
432
415
  });
433
416
 
@@ -442,10 +425,6 @@ describe('GitHubReposOverviewWorkflow', () => {
442
425
  },
443
426
  });
444
427
 
445
- mockOAuthWorkflow.run.mockResolvedValue({
446
- workflowId: 'test-workflow-id',
447
- });
448
-
449
428
  const result = await processor.process(workflow, { owner: 'octocat', repo: 'Hello-World' }, context);
450
429
 
451
430
  expect(result).toBeDefined();
@@ -457,10 +436,7 @@ describe('GitHubReposOverviewWorkflow', () => {
457
436
  expect(mockOAuthWorkflow.run).toHaveBeenCalledTimes(1);
458
437
  expect(mockOAuthWorkflow.run).toHaveBeenCalledWith(
459
438
  { provider: 'github', scopes: ['repo', 'read:org', 'workflow'] },
460
- expect.objectContaining({
461
- alias: 'oAuth',
462
- callback: { transition: 'authCompleted' },
463
- }),
439
+ { callback: { transition: 'authCompleted' } },
464
440
  );
465
441
 
466
442
  // Subsequent tools should NOT be called
@@ -570,9 +546,7 @@ describe('GitHubReposOverviewWorkflow with existing entity', () => {
570
546
  expect(result.place).toBe('end');
571
547
 
572
548
  // Verify markdown document was created after auth resume
573
- expect(result.documents).toEqual(
574
- expect.arrayContaining([expect.objectContaining({ className: 'MarkdownDocument' })]),
575
- );
549
+ expect(result.documents).toEqual(expect.arrayContaining([expect.objectContaining({ documentName: 'markdown' })]));
576
550
 
577
551
  expect(mockGetAuthenticatedUser.call).toHaveBeenCalledTimes(1);
578
552
  expect(mockGetRepo.call).toHaveBeenCalledTimes(1);
@@ -1,16 +1,6 @@
1
- title: 'GitHub Agent'
2
-
3
- description: |
4
- An interactive chat agent with access to GitHub.
5
- Ask it to manage repositories, issues, pull requests, browse code, check CI/CD status,
6
- search across GitHub, and more. Handles OAuth authentication automatically when needed —
7
- the agent detects unauthorized errors and launches authentication on its own.
8
-
9
- ui:
10
- widgets:
11
- - widget: prompt-input
12
- enabledWhen:
13
- - waiting_for_user
14
- options:
15
- transition: userMessage
16
- label: Send Message
1
+ widget: prompt-input
2
+ enabledWhen:
3
+ - waiting_for_user
4
+ options:
5
+ transition: userMessage
6
+ label: Send Message
@@ -1,14 +1,7 @@
1
+ import { Inject } from '@nestjs/common';
1
2
  import { z } from 'zod';
2
- import {
3
- BaseWorkflow,
4
- Guard,
5
- Initial,
6
- InjectTool,
7
- InjectWorkflow,
8
- ToolResult,
9
- Transition,
10
- Workflow,
11
- } from '@loopstack/common';
3
+ import { BaseWorkflow, Guard, TEMPLATE_RENDERER, Transition, Workflow } from '@loopstack/common';
4
+ import type { TemplateRenderFn } from '@loopstack/common';
12
5
  import {
13
6
  GitHubCreateIssueCommentTool,
14
7
  GitHubCreateIssueTool,
@@ -46,98 +39,66 @@ import {
46
39
  import { OAuthWorkflow } from '@loopstack/oauth-module';
47
40
  import { AuthenticateGitHubTask } from '../tools/authenticate-github-task.tool';
48
41
 
49
- @Workflow({
50
- uiConfig: __dirname + '/github-agent.ui.yaml',
51
- })
52
- export class GitHubAgentWorkflow extends BaseWorkflow {
53
- @InjectTool({
54
- provider: 'claude',
55
- model: 'claude-sonnet-4-6',
56
- system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
57
- and search tools. When a tool returns an unauthorized error, use authenticateGitHub
58
- to let the user sign in, then retry. Be concise and format results using markdown.`,
59
- tools: [
60
- 'gitHubListRepos',
61
- 'gitHubGetRepo',
62
- 'gitHubCreateRepo',
63
- 'gitHubListBranches',
64
- 'gitHubListIssues',
65
- 'gitHubGetIssue',
66
- 'gitHubCreateIssue',
67
- 'gitHubCreateIssueComment',
68
- 'gitHubListPullRequests',
69
- 'gitHubGetPullRequest',
70
- 'gitHubCreatePullRequest',
71
- 'gitHubMergePullRequest',
72
- 'gitHubListPrReviews',
73
- 'gitHubGetFileContent',
74
- 'gitHubCreateOrUpdateFile',
75
- 'gitHubListDirectory',
76
- 'gitHubGetCommit',
77
- 'gitHubListWorkflowRuns',
78
- 'gitHubTriggerWorkflow',
79
- 'gitHubGetWorkflowRun',
80
- 'gitHubSearchCode',
81
- 'gitHubSearchRepos',
82
- 'gitHubSearchIssues',
83
- 'gitHubGetAuthenticatedUser',
84
- 'gitHubListUserOrgs',
85
- 'authenticateGitHub',
86
- ],
87
- })
88
- llmGenerateText: LlmGenerateTextTool;
89
- @InjectTool({ provider: 'claude' }) llmDelegateToolCalls: LlmDelegateToolCallsTool;
90
- @InjectTool({ provider: 'claude' }) llmUpdateToolResult: LlmUpdateToolResultTool;
91
- @InjectTool() authenticateGitHub: AuthenticateGitHubTask;
92
-
93
- // GitHub Repos tools
94
- @InjectTool() gitHubListRepos: GitHubListReposTool;
95
- @InjectTool() gitHubGetRepo: GitHubGetRepoTool;
96
- @InjectTool() gitHubCreateRepo: GitHubCreateRepoTool;
97
- @InjectTool() gitHubListBranches: GitHubListBranchesTool;
98
-
99
- // GitHub Issues tools
100
- @InjectTool() gitHubListIssues: GitHubListIssuesTool;
101
- @InjectTool() gitHubGetIssue: GitHubGetIssueTool;
102
- @InjectTool() gitHubCreateIssue: GitHubCreateIssueTool;
103
- @InjectTool() gitHubCreateIssueComment: GitHubCreateIssueCommentTool;
104
-
105
- // GitHub Pull Requests tools
106
- @InjectTool() gitHubListPullRequests: GitHubListPullRequestsTool;
107
- @InjectTool() gitHubGetPullRequest: GitHubGetPullRequestTool;
108
- @InjectTool() gitHubCreatePullRequest: GitHubCreatePullRequestTool;
109
- @InjectTool() gitHubMergePullRequest: GitHubMergePullRequestTool;
110
- @InjectTool() gitHubListPrReviews: GitHubListPrReviewsTool;
111
-
112
- // GitHub Content / Git Ops tools
113
- @InjectTool() gitHubGetFileContent: GitHubGetFileContentTool;
114
- @InjectTool() gitHubCreateOrUpdateFile: GitHubCreateOrUpdateFileTool;
115
- @InjectTool() gitHubListDirectory: GitHubListDirectoryTool;
116
- @InjectTool() gitHubGetCommit: GitHubGetCommitTool;
117
-
118
- // GitHub Actions tools
119
- @InjectTool() gitHubListWorkflowRuns: GitHubListWorkflowRunsTool;
120
- @InjectTool() gitHubTriggerWorkflow: GitHubTriggerWorkflowTool;
121
- @InjectTool() gitHubGetWorkflowRun: GitHubGetWorkflowRunTool;
122
-
123
- // GitHub Search tools
124
- @InjectTool() gitHubSearchCode: GitHubSearchCodeTool;
125
- @InjectTool() gitHubSearchRepos: GitHubSearchReposTool;
126
- @InjectTool() gitHubSearchIssues: GitHubSearchIssuesTool;
127
-
128
- // GitHub Users & Orgs tools
129
- @InjectTool() gitHubGetAuthenticatedUser: GitHubGetAuthenticatedUserTool;
130
- @InjectTool() gitHubListUserOrgs: GitHubListUserOrgsTool;
131
-
132
- @InjectWorkflow() oAuth: OAuthWorkflow;
133
-
42
+ interface GitHubAgentState {
134
43
  llmResult?: LlmGenerateTextResult;
135
44
  llmMeta?: LlmResultMeta;
136
45
  delegateResult?: LlmDelegateResult;
46
+ }
47
+
48
+ @Workflow({
49
+ title: 'GitHub Agent',
50
+ description:
51
+ 'An interactive chat agent with access to GitHub.\nAsk it to manage repositories, issues, pull requests, browse code, check CI/CD status,\nsearch across GitHub, and more. Handles OAuth authentication automatically when needed —\nthe agent detects unauthorized errors and launches authentication on its own.',
52
+ name: 'github_agent',
53
+ widget: __dirname + '/github-agent.ui.yaml',
54
+ })
55
+ export class GitHubAgentWorkflow extends BaseWorkflow<Record<string, unknown>, GitHubAgentState> {
56
+ constructor(
57
+ private readonly llmGenerateText: LlmGenerateTextTool,
58
+ private readonly llmDelegateToolCalls: LlmDelegateToolCallsTool,
59
+ private readonly llmUpdateToolResult: LlmUpdateToolResultTool,
60
+ private readonly authenticateGitHub: AuthenticateGitHubTask,
61
+ // GitHub Repos tools
62
+ private readonly gitHubListRepos: GitHubListReposTool,
63
+ private readonly gitHubGetRepo: GitHubGetRepoTool,
64
+ private readonly gitHubCreateRepo: GitHubCreateRepoTool,
65
+ private readonly gitHubListBranches: GitHubListBranchesTool,
66
+ // GitHub Issues tools
67
+ private readonly gitHubListIssues: GitHubListIssuesTool,
68
+ private readonly gitHubGetIssue: GitHubGetIssueTool,
69
+ private readonly gitHubCreateIssue: GitHubCreateIssueTool,
70
+ private readonly gitHubCreateIssueComment: GitHubCreateIssueCommentTool,
71
+ // GitHub Pull Requests tools
72
+ private readonly gitHubListPullRequests: GitHubListPullRequestsTool,
73
+ private readonly gitHubGetPullRequest: GitHubGetPullRequestTool,
74
+ private readonly gitHubCreatePullRequest: GitHubCreatePullRequestTool,
75
+ private readonly gitHubMergePullRequest: GitHubMergePullRequestTool,
76
+ private readonly gitHubListPrReviews: GitHubListPrReviewsTool,
77
+ // GitHub Content / Git Ops tools
78
+ private readonly gitHubGetFileContent: GitHubGetFileContentTool,
79
+ private readonly gitHubCreateOrUpdateFile: GitHubCreateOrUpdateFileTool,
80
+ private readonly gitHubListDirectory: GitHubListDirectoryTool,
81
+ private readonly gitHubGetCommit: GitHubGetCommitTool,
82
+ // GitHub Actions tools
83
+ private readonly gitHubListWorkflowRuns: GitHubListWorkflowRunsTool,
84
+ private readonly gitHubTriggerWorkflow: GitHubTriggerWorkflowTool,
85
+ private readonly gitHubGetWorkflowRun: GitHubGetWorkflowRunTool,
86
+ // GitHub Search tools
87
+ private readonly gitHubSearchCode: GitHubSearchCodeTool,
88
+ private readonly gitHubSearchRepos: GitHubSearchReposTool,
89
+ private readonly gitHubSearchIssues: GitHubSearchIssuesTool,
90
+ // GitHub Users & Orgs tools
91
+ private readonly gitHubGetAuthenticatedUser: GitHubGetAuthenticatedUserTool,
92
+ private readonly gitHubListUserOrgs: GitHubListUserOrgsTool,
93
+ private readonly oAuth: OAuthWorkflow,
94
+ @Inject(TEMPLATE_RENDERER) private readonly render: TemplateRenderFn,
95
+ ) {
96
+ super();
97
+ }
137
98
 
138
- @Initial({ to: 'waiting_for_user' })
139
- async setup() {
140
- await this.repository.save(
99
+ @Transition({ to: 'waiting_for_user' })
100
+ async setup(state: GitHubAgentState): Promise<GitHubAgentState> {
101
+ await this.documentStore.save(
141
102
  LlmMessageDocument,
142
103
  {
143
104
  role: 'user',
@@ -145,71 +106,119 @@ to let the user sign in, then retry. Be concise and format results using markdow
145
106
  },
146
107
  { meta: { hidden: true } },
147
108
  );
109
+ return state;
148
110
  }
149
111
 
150
112
  @Transition({ from: 'waiting_for_user', to: 'ready', wait: true, schema: z.string() })
151
- async userMessage(payload: string) {
152
- await this.repository.save(LlmMessageDocument, {
113
+ async userMessage(state: GitHubAgentState, payload: string): Promise<GitHubAgentState> {
114
+ await this.documentStore.save(LlmMessageDocument, {
153
115
  role: 'user',
154
116
  content: payload,
155
117
  });
118
+ return state;
156
119
  }
157
120
 
158
121
  @Transition({ from: 'ready', to: 'prompt_executed' })
159
- async llmTurn() {
160
- const result: ToolResult<LlmGenerateTextResult, LlmResultMeta> = await this.llmGenerateText.call();
161
- this.llmResult = result.data;
162
- this.llmMeta = result.metadata;
122
+ async llmTurn(state: GitHubAgentState): Promise<GitHubAgentState> {
123
+ const result = await this.llmGenerateText.call(
124
+ {},
125
+ {
126
+ config: {
127
+ provider: 'claude',
128
+ model: 'claude-sonnet-4-6',
129
+ system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
130
+ and search tools. When a tool returns an unauthorized error, use authenticateGitHub
131
+ to let the user sign in, then retry. Be concise and format results using markdown.`,
132
+ tools: [
133
+ 'github_list_repos',
134
+ 'github_get_repo',
135
+ 'github_create_repo',
136
+ 'github_list_branches',
137
+ 'github_list_issues',
138
+ 'github_get_issue',
139
+ 'github_create_issue',
140
+ 'github_create_issue_comment',
141
+ 'github_list_pull_requests',
142
+ 'github_get_pull_request',
143
+ 'github_create_pull_request',
144
+ 'github_merge_pull_request',
145
+ 'github_list_pr_reviews',
146
+ 'github_get_file_content',
147
+ 'github_create_or_update_file',
148
+ 'github_list_directory',
149
+ 'github_get_commit',
150
+ 'github_list_workflow_runs',
151
+ 'github_trigger_workflow',
152
+ 'github_get_workflow_run',
153
+ 'github_search_code',
154
+ 'github_search_repos',
155
+ 'github_search_issues',
156
+ 'github_get_authenticated_user',
157
+ 'github_list_user_orgs',
158
+ 'authenticate_github',
159
+ ],
160
+ },
161
+ },
162
+ );
163
+ return { ...state, llmResult: result.data, llmMeta: result.metadata as LlmResultMeta | undefined };
163
164
  }
164
165
 
165
166
  @Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
166
167
  @Guard('hasToolCalls')
167
- async executeToolCalls() {
168
- await this.repository.save(LlmMessageDocument, this.llmResult!.message, {
169
- meta: { response: this.llmResult!.response, provider: this.llmMeta!.provider },
168
+ async executeToolCalls(state: GitHubAgentState): Promise<GitHubAgentState> {
169
+ await this.documentStore.save(LlmMessageDocument, state.llmResult!.message, {
170
+ meta: { response: state.llmResult!.response, provider: state.llmMeta!.provider },
170
171
  });
171
- const result: ToolResult<LlmDelegateResult> = await this.llmDelegateToolCalls.call({
172
- message: this.llmResult!.message,
173
- callback: { transition: 'toolResultReceived' },
174
- });
175
- this.delegateResult = result.data;
172
+ const result = await this.llmDelegateToolCalls.call(
173
+ {
174
+ message: state.llmResult!.message,
175
+ callback: { transition: 'toolResultReceived' },
176
+ },
177
+ { config: { provider: 'claude' } },
178
+ );
179
+ return { ...state, delegateResult: result.data };
176
180
  }
177
181
 
178
- hasToolCalls(): boolean {
179
- return this.llmResult?.message.stopReason === 'tool_use';
182
+ hasToolCalls(state: GitHubAgentState): boolean {
183
+ return state.llmResult?.message.stopReason === 'tool_use';
180
184
  }
181
185
 
182
186
  @Transition({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true, schema: z.record(z.string(), z.unknown()) })
183
- async toolResultReceived(payload: Record<string, unknown>) {
184
- const result: ToolResult<LlmDelegateResult> = await this.llmUpdateToolResult.call({
185
- delegateResult: this.delegateResult!,
186
- completedTool: payload,
187
- });
188
- this.delegateResult = result.data;
187
+ async toolResultReceived(state: GitHubAgentState, payload: Record<string, unknown>): Promise<GitHubAgentState> {
188
+ const result = await this.llmUpdateToolResult.call(
189
+ {
190
+ delegateResult: state.delegateResult!,
191
+ completedTool: payload,
192
+ },
193
+ { config: { provider: 'claude' } },
194
+ );
195
+ return { ...state, delegateResult: result.data };
189
196
  }
190
197
 
191
198
  @Transition({ from: 'awaiting_tools', to: 'ready' })
192
199
  @Guard('allToolsComplete')
193
- async allToolsCompleteTransition() {
194
- await this.repository.save(LlmMessageDocument, {
200
+ async allToolsCompleteTransition(state: GitHubAgentState): Promise<GitHubAgentState> {
201
+ await this.documentStore.save(LlmMessageDocument, {
195
202
  role: 'user',
196
- content: this.delegateResult!.toolResults.map((tr) => ({
203
+ content: state.delegateResult!.toolResults.map((tr) => ({
197
204
  type: 'tool_result' as const,
198
205
  toolCallId: tr.toolCallId,
199
206
  content: tr.content ?? '',
200
207
  isError: tr.isError ?? false,
201
208
  })),
202
209
  });
210
+ return state;
203
211
  }
204
212
 
205
- allToolsComplete(): boolean {
206
- return this.delegateResult?.allCompleted ?? false;
213
+ allToolsComplete(state: GitHubAgentState): boolean {
214
+ return state.delegateResult?.allCompleted ?? false;
207
215
  }
208
216
 
209
217
  @Transition({ from: 'prompt_executed', to: 'waiting_for_user' })
210
- async respond() {
211
- await this.repository.save(LlmMessageDocument, this.llmResult!.message, {
212
- meta: { response: this.llmResult!.response, provider: this.llmMeta!.provider },
218
+ async respond(state: GitHubAgentState): Promise<GitHubAgentState> {
219
+ await this.documentStore.save(LlmMessageDocument, state.llmResult!.message, {
220
+ meta: { response: state.llmResult!.response, provider: state.llmMeta!.provider },
213
221
  });
222
+ return state;
214
223
  }
215
224
  }