@backburner/cli 0.1.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.
- package/LICENSE +21 -0
- package/README.md +329 -0
- package/dist/src/agents/antigravity-launch.js +162 -0
- package/dist/src/agents/antigravity.js +251 -0
- package/dist/src/agents/claude-launch.js +117 -0
- package/dist/src/agents/claude.js +271 -0
- package/dist/src/agents/codex-launch.js +82 -0
- package/dist/src/agents/codex.js +261 -0
- package/dist/src/agents/composite-runner.js +13 -0
- package/dist/src/agents/extraction.js +442 -0
- package/dist/src/agents/failure-feedback.js +132 -0
- package/dist/src/agents/gemini-launch.js +96 -0
- package/dist/src/agents/gemini.js +392 -0
- package/dist/src/agents/job-agent-assignments.js +180 -0
- package/dist/src/agents/lifecycle-feedback.js +147 -0
- package/dist/src/agents/limit-detector.js +83 -0
- package/dist/src/agents/opencode-launch.js +138 -0
- package/dist/src/agents/opencode.js +156 -0
- package/dist/src/agents/plan-breakdown-feedback.js +97 -0
- package/dist/src/agents/pool.js +168 -0
- package/dist/src/agents/prompt.js +462 -0
- package/dist/src/agents/provider-sessions.js +105 -0
- package/dist/src/agents/review-feedback.js +255 -0
- package/dist/src/agents/selection.js +113 -0
- package/dist/src/agents/service.js +1087 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/agents/wave-assessment-feedback.js +69 -0
- package/dist/src/attention/derive.js +337 -0
- package/dist/src/capabilities/index.js +2 -0
- package/dist/src/capabilities/projection.js +183 -0
- package/dist/src/capabilities/types.js +1 -0
- package/dist/src/capabilities/validator.js +87 -0
- package/dist/src/cli/commands/broker-smoke.js +96 -0
- package/dist/src/cli/commands/broker.js +56 -0
- package/dist/src/cli/commands/init.js +4 -0
- package/dist/src/cli/commands/journal.js +43 -0
- package/dist/src/cli/commands/run-loop.js +56 -0
- package/dist/src/cli/commands/run.js +1241 -0
- package/dist/src/cli/commands/tui.js +187 -0
- package/dist/src/cli/dispatcher.js +51 -0
- package/dist/src/cli/errors.js +2 -0
- package/dist/src/cli/init.js +92 -0
- package/dist/src/cli/options.js +86 -0
- package/dist/src/cli/output.js +231 -0
- package/dist/src/cli/run-journal.js +342 -0
- package/dist/src/cli/run-types.js +1 -0
- package/dist/src/cli/run.js +32 -0
- package/dist/src/cli/runtime/dispatch-lifecycle.js +772 -0
- package/dist/src/cli/runtime/provider-runtime-types.js +1 -0
- package/dist/src/cli/runtime/provider-runtime.js +152 -0
- package/dist/src/cli/runtime/provider-tools.js +73 -0
- package/dist/src/cli/runtime/providers/antigravity.js +26 -0
- package/dist/src/cli/runtime/providers/claude.js +20 -0
- package/dist/src/cli/runtime/providers/codex.js +20 -0
- package/dist/src/cli/runtime/providers/gemini.js +20 -0
- package/dist/src/cli/runtime/providers/opencode.js +20 -0
- package/dist/src/cli/runtime/run-context.js +131 -0
- package/dist/src/cli/tui/index.js +3 -0
- package/dist/src/cli/tui/screen.js +388 -0
- package/dist/src/cli/tui/tracker.js +558 -0
- package/dist/src/cli/tui/types.js +1 -0
- package/dist/src/cli/tui/views.js +1006 -0
- package/dist/src/config/loader.js +136 -0
- package/dist/src/config/schemas.js +403 -0
- package/dist/src/config/types.js +1 -0
- package/dist/src/context/types.js +331 -0
- package/dist/src/domain/entities.js +1 -0
- package/dist/src/git/backburner-git-tool-service.js +680 -0
- package/dist/src/git/gitops-lifecycle.js +56 -0
- package/dist/src/git/merge-prep-workspace.js +357 -0
- package/dist/src/git/types.js +484 -0
- package/dist/src/github/broker-model-content.js +215 -0
- package/dist/src/github/broker-server.js +237 -0
- package/dist/src/github/broker.js +638 -0
- package/dist/src/github/composed.js +53 -0
- package/dist/src/github/gateway.js +478 -0
- package/dist/src/github/normalize.js +222 -0
- package/dist/src/github/pr-classification.js +73 -0
- package/dist/src/github/scoped-broker.js +35 -0
- package/dist/src/github/security.js +126 -0
- package/dist/src/github/service.js +332 -0
- package/dist/src/github/types.js +1 -0
- package/dist/src/github/utils.js +16 -0
- package/dist/src/journal/index.js +4 -0
- package/dist/src/journal/reader.js +62 -0
- package/dist/src/journal/renderer.js +50 -0
- package/dist/src/journal/service.js +72 -0
- package/dist/src/journal/types.js +1 -0
- package/dist/src/journal/writer.js +19 -0
- package/dist/src/mcps/bridge.js +166 -0
- package/dist/src/mcps/index.js +5 -0
- package/dist/src/mcps/reconcile.js +113 -0
- package/dist/src/mcps/smithery-client.js +186 -0
- package/dist/src/mcps/smithery.js +164 -0
- package/dist/src/mcps/types.js +1 -0
- package/dist/src/memory/json-store.js +71 -0
- package/dist/src/memory/prompt.js +16 -0
- package/dist/src/memory/scope.js +31 -0
- package/dist/src/memory/store.js +1 -0
- package/dist/src/memory/tool-service.js +142 -0
- package/dist/src/memory/types.js +12 -0
- package/dist/src/onboarding/config-writer.js +96 -0
- package/dist/src/onboarding/engine.js +86 -0
- package/dist/src/onboarding/prompt.js +102 -0
- package/dist/src/onboarding/services/provider-discovery.js +58 -0
- package/dist/src/onboarding/services/repo-discovery.js +246 -0
- package/dist/src/onboarding/steps/check-github-auth.js +32 -0
- package/dist/src/onboarding/steps/check-requirements.js +34 -0
- package/dist/src/onboarding/steps/configure-models.js +105 -0
- package/dist/src/onboarding/steps/configure-paths.js +33 -0
- package/dist/src/onboarding/steps/discover-providers.js +38 -0
- package/dist/src/onboarding/steps/discover-repos.js +39 -0
- package/dist/src/onboarding/steps/generate-config.js +106 -0
- package/dist/src/onboarding/steps/readiness-validation.js +158 -0
- package/dist/src/onboarding/steps/select-repos.js +76 -0
- package/dist/src/onboarding/steps/show-summary.js +97 -0
- package/dist/src/onboarding/steps/welcome.js +27 -0
- package/dist/src/onboarding/types.js +1 -0
- package/dist/src/onboarding/validators/git-access.js +63 -0
- package/dist/src/onboarding/validators/github-auth.js +85 -0
- package/dist/src/onboarding/validators/tool-checker.js +153 -0
- package/dist/src/onboarding/validators/workspace.js +83 -0
- package/dist/src/prompts/resolver.js +106 -0
- package/dist/src/retrospectives/candidates.js +224 -0
- package/dist/src/retrospectives/derive.js +169 -0
- package/dist/src/retrospectives/ingest.js +321 -0
- package/dist/src/retrospectives/management-prs.js +380 -0
- package/dist/src/retrospectives/proposals.js +199 -0
- package/dist/src/retrospectives/scopes.js +272 -0
- package/dist/src/retrospectives/scoring.js +171 -0
- package/dist/src/retrospectives/types.js +1 -0
- package/dist/src/state/loader.js +473 -0
- package/dist/src/state/types.js +15 -0
- package/dist/src/tasks/derivation/builders.js +70 -0
- package/dist/src/tasks/derivation/handlers/comment-response.js +169 -0
- package/dist/src/tasks/derivation/handlers/implement-plan.js +103 -0
- package/dist/src/tasks/derivation/handlers/index.js +54 -0
- package/dist/src/tasks/derivation/handlers/packet-pr-control.js +92 -0
- package/dist/src/tasks/derivation/handlers/packet-worktree.js +29 -0
- package/dist/src/tasks/derivation/handlers/plan-breakdown.js +45 -0
- package/dist/src/tasks/derivation/handlers/prepare-for-merge.js +91 -0
- package/dist/src/tasks/derivation/handlers/product-discovery.js +36 -0
- package/dist/src/tasks/derivation/handlers/retry-failed-task.js +125 -0
- package/dist/src/tasks/derivation/handlers/review-pr.js +28 -0
- package/dist/src/tasks/derivation/handlers/shared.js +24 -0
- package/dist/src/tasks/derivation/handlers/sync-parent-branch.js +100 -0
- package/dist/src/tasks/derivation/handlers/wave-assessment.js +223 -0
- package/dist/src/tasks/derivation/handlers/write-plan.js +101 -0
- package/dist/src/tasks/derivation/handlers/write-product-spec.js +49 -0
- package/dist/src/tasks/derivation/runner.js +18 -0
- package/dist/src/tasks/derivation/types.js +1 -0
- package/dist/src/tasks/derive.js +810 -0
- package/dist/src/tasks/mcp-skip.js +10 -0
- package/dist/src/tasks/retry.js +10 -0
- package/dist/src/tasks/task.js +184 -0
- package/dist/src/tasks/types.js +1 -0
- package/dist/src/utils/command-runner.js +192 -0
- package/dist/src/utils/json.js +38 -0
- package/dist/src/utils/logger.js +43 -0
- package/dist/src/utils/paths.js +38 -0
- package/dist/src/utils/slug.js +10 -0
- package/dist/src/workflows/registry.js +174 -0
- package/dist/src/workflows/triage.js +90 -0
- package/dist/src/workflows/workstream-machine/derive-events.js +121 -0
- package/dist/src/workflows/workstream-machine/evaluate.js +24 -0
- package/dist/src/workflows/workstream-machine/events.js +1 -0
- package/dist/src/workflows/workstream-machine/machine.js +355 -0
- package/dist/src/workflows/workstream-machine/selectors.js +19 -0
- package/dist/src/workflows/workstream-machine/types.js +1 -0
- package/dist/src/workstreams/branch-sync.js +87 -0
- package/dist/src/workstreams/derive.js +629 -0
- package/dist/src/workstreams/mcp-blockers.js +420 -0
- package/dist/src/workstreams/packet-candidates.js +42 -0
- package/dist/src/workstreams/packet-lifecycle-feedback.js +60 -0
- package/dist/src/workstreams/packet-lifecycle-report.js +199 -0
- package/dist/src/workstreams/packet-plan.js +597 -0
- package/dist/src/workstreams/packet-projections.js +190 -0
- package/dist/src/workstreams/parent-branch-sync-action.js +312 -0
- package/dist/src/workstreams/parent-branch-sync.js +148 -0
- package/dist/src/workstreams/phases.js +4 -0
- package/dist/src/workstreams/plan-breakdown-generator/export.js +19 -0
- package/dist/src/workstreams/plan-breakdown-generator/index.js +6 -0
- package/dist/src/workstreams/plan-breakdown-generator/inspect.js +50 -0
- package/dist/src/workstreams/plan-breakdown-generator/patch.js +139 -0
- package/dist/src/workstreams/plan-breakdown-generator/session.js +19 -0
- package/dist/src/workstreams/plan-breakdown-generator/store.js +27 -0
- package/dist/src/workstreams/plan-breakdown-generator/tools.js +407 -0
- package/dist/src/workstreams/plan-breakdown-generator/types.js +6 -0
- package/dist/src/workstreams/plan-breakdown-generator/validate.js +212 -0
- package/dist/src/workstreams/plan-breakdown-schema.js +175 -0
- package/dist/src/workstreams/utils.js +15 -0
- package/dist/src/workstreams/wave-assessment/checkpoint.js +172 -0
- package/dist/src/workstreams/wave-assessment/index.js +3 -0
- package/dist/src/workstreams/wave-assessment/session.js +84 -0
- package/dist/src/workstreams/wave-assessment/store.js +51 -0
- package/dist/src/workstreams/wave-assessment/tools.js +419 -0
- package/dist/src/workstreams/wave-assessment/types.js +1 -0
- package/dist/src/workstreams/wave-assessment/validate.js +78 -0
- package/dist/src/worktrees/service.js +839 -0
- package/dist/src/worktrees/workspace-ownership.js +34 -0
- package/dist/src/worktrees/workspace-recovery.js +177 -0
- package/package.json +61 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
const MAX_COMMENT_BODY_LENGTH = 65_536;
|
|
2
|
+
const MAX_PR_BODY_LENGTH = 65_536;
|
|
3
|
+
const MAX_SEARCH_LIMIT = 25;
|
|
4
|
+
export class GitHubBrokerService {
|
|
5
|
+
gitHubGateway;
|
|
6
|
+
logger;
|
|
7
|
+
managedRepos;
|
|
8
|
+
allowedToolNames;
|
|
9
|
+
constructor(gitHubGateway, repos, logger, allowedToolNames = TOOL_DEFINITIONS.map((tool) => tool.name)) {
|
|
10
|
+
this.gitHubGateway = gitHubGateway;
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
this.allowedToolNames = new Set(allowedToolNames);
|
|
13
|
+
this.managedRepos = new Map(repos
|
|
14
|
+
.filter((repo) => repo.enabled)
|
|
15
|
+
.map((repo) => {
|
|
16
|
+
const slug = `${repo.owner}/${repo.name}`;
|
|
17
|
+
return [
|
|
18
|
+
slug,
|
|
19
|
+
{
|
|
20
|
+
id: repo.id,
|
|
21
|
+
owner: repo.owner,
|
|
22
|
+
name: repo.name,
|
|
23
|
+
slug
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
listTools() {
|
|
29
|
+
return TOOL_DEFINITIONS.filter((tool) => this.allowedToolNames.has(tool.name));
|
|
30
|
+
}
|
|
31
|
+
async callTool(name, rawArguments) {
|
|
32
|
+
const startedAt = new Date().toISOString();
|
|
33
|
+
const sanitizedArguments = sanitizeArguments(name, rawArguments);
|
|
34
|
+
if (!this.allowedToolNames.has(name)) {
|
|
35
|
+
const repo = extractRepoSlug(rawArguments);
|
|
36
|
+
return {
|
|
37
|
+
ok: false,
|
|
38
|
+
tool: name,
|
|
39
|
+
error: {
|
|
40
|
+
code: "tool_not_found",
|
|
41
|
+
message: `GitHub broker tool "${name}" is not enabled for this run`
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
switch (name) {
|
|
47
|
+
case "get_issue":
|
|
48
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
49
|
+
const input = validateIssueLookup(rawArguments);
|
|
50
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
51
|
+
const issue = await this.gitHubGateway.getIssue(repo.owner, repo.name, input.issueNumber);
|
|
52
|
+
return {
|
|
53
|
+
ok: true,
|
|
54
|
+
tool: name,
|
|
55
|
+
repo: repo.slug,
|
|
56
|
+
summary: `get_issue succeeded for ${repo.slug}`,
|
|
57
|
+
result: {
|
|
58
|
+
repo: repo.slug,
|
|
59
|
+
issue
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
case "get_issue_comments":
|
|
64
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
65
|
+
const input = validateIssueLookup(rawArguments);
|
|
66
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
67
|
+
const comments = await this.gitHubGateway.listIssueComments(repo.owner, repo.name, input.issueNumber);
|
|
68
|
+
return {
|
|
69
|
+
ok: true,
|
|
70
|
+
tool: name,
|
|
71
|
+
repo: repo.slug,
|
|
72
|
+
summary: `get_issue_comments succeeded for ${repo.slug}`,
|
|
73
|
+
result: {
|
|
74
|
+
repo: repo.slug,
|
|
75
|
+
issueNumber: input.issueNumber,
|
|
76
|
+
comments
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
case "get_pr":
|
|
81
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
82
|
+
const input = validatePullRequestLookup(rawArguments);
|
|
83
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
84
|
+
const pullRequest = await this.gitHubGateway.getPullRequest(repo.owner, repo.name, input.pullRequestNumber);
|
|
85
|
+
return {
|
|
86
|
+
ok: true,
|
|
87
|
+
tool: name,
|
|
88
|
+
repo: repo.slug,
|
|
89
|
+
summary: `get_pr succeeded for ${repo.slug}`,
|
|
90
|
+
result: {
|
|
91
|
+
repo: repo.slug,
|
|
92
|
+
pullRequest
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
case "get_pr_comments":
|
|
97
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
98
|
+
const input = validatePullRequestLookup(rawArguments);
|
|
99
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
100
|
+
const [comments, reviews, reviewComments] = await Promise.all([
|
|
101
|
+
this.gitHubGateway.listPullRequestComments(repo.owner, repo.name, input.pullRequestNumber),
|
|
102
|
+
this.gitHubGateway.listPullRequestReviews(repo.owner, repo.name, input.pullRequestNumber),
|
|
103
|
+
this.gitHubGateway.listPullRequestReviewComments(repo.owner, repo.name, input.pullRequestNumber)
|
|
104
|
+
]);
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
tool: name,
|
|
108
|
+
repo: repo.slug,
|
|
109
|
+
summary: `get_pr_comments succeeded for ${repo.slug}`,
|
|
110
|
+
result: {
|
|
111
|
+
repo: repo.slug,
|
|
112
|
+
pullRequestNumber: input.pullRequestNumber,
|
|
113
|
+
comments,
|
|
114
|
+
reviews,
|
|
115
|
+
reviewComments
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
case "get_pr_checks":
|
|
120
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
121
|
+
const input = validatePullRequestLookup(rawArguments);
|
|
122
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
123
|
+
const pullRequest = await this.gitHubGateway.getPullRequest(repo.owner, repo.name, input.pullRequestNumber);
|
|
124
|
+
if (!pullRequest.headSha) {
|
|
125
|
+
return {
|
|
126
|
+
ok: true,
|
|
127
|
+
tool: name,
|
|
128
|
+
repo: repo.slug,
|
|
129
|
+
summary: `get_pr_checks: head SHA unavailable for PR #${input.pullRequestNumber}`,
|
|
130
|
+
result: {
|
|
131
|
+
repo: repo.slug,
|
|
132
|
+
pullRequestNumber: input.pullRequestNumber,
|
|
133
|
+
headSha: "",
|
|
134
|
+
checkRuns: []
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const checkRuns = await this.gitHubGateway.listPullRequestCheckRuns(repo.owner, repo.name, pullRequest.headSha);
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
tool: name,
|
|
142
|
+
repo: repo.slug,
|
|
143
|
+
summary: `get_pr_checks succeeded for ${repo.slug}`,
|
|
144
|
+
result: {
|
|
145
|
+
repo: repo.slug,
|
|
146
|
+
pullRequestNumber: input.pullRequestNumber,
|
|
147
|
+
headSha: pullRequest.headSha,
|
|
148
|
+
checkRuns
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
case "search_code":
|
|
153
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
154
|
+
const input = validateCodeSearch(rawArguments);
|
|
155
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
156
|
+
const result = await this.gitHubGateway.searchCode(input.pathPrefix === undefined
|
|
157
|
+
? {
|
|
158
|
+
owner: repo.owner,
|
|
159
|
+
name: repo.name,
|
|
160
|
+
query: input.query,
|
|
161
|
+
limit: input.limit
|
|
162
|
+
}
|
|
163
|
+
: {
|
|
164
|
+
owner: repo.owner,
|
|
165
|
+
name: repo.name,
|
|
166
|
+
query: input.query,
|
|
167
|
+
pathPrefix: input.pathPrefix,
|
|
168
|
+
limit: input.limit
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
ok: true,
|
|
172
|
+
tool: name,
|
|
173
|
+
repo: repo.slug,
|
|
174
|
+
summary: `search_code succeeded for ${repo.slug}`,
|
|
175
|
+
result: {
|
|
176
|
+
repo: repo.slug,
|
|
177
|
+
query: input.query,
|
|
178
|
+
pathPrefix: input.pathPrefix ?? null,
|
|
179
|
+
totalCount: result.totalCount,
|
|
180
|
+
incompleteResults: result.incompleteResults,
|
|
181
|
+
items: result.items
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
case "post_issue_comment":
|
|
186
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
187
|
+
const input = validatePostIssueComment(rawArguments);
|
|
188
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
189
|
+
const comment = await this.gitHubGateway.postIssueComment({
|
|
190
|
+
owner: repo.owner,
|
|
191
|
+
name: repo.name,
|
|
192
|
+
issueNumber: input.issueNumber,
|
|
193
|
+
body: input.body
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
ok: true,
|
|
197
|
+
tool: name,
|
|
198
|
+
repo: repo.slug,
|
|
199
|
+
summary: `post_issue_comment succeeded for ${repo.slug}`,
|
|
200
|
+
result: {
|
|
201
|
+
repo: repo.slug,
|
|
202
|
+
issueNumber: input.issueNumber,
|
|
203
|
+
comment
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
case "post_pr_review_comment_reply":
|
|
208
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
209
|
+
const input = validatePostPullRequestReviewCommentReply(rawArguments);
|
|
210
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
211
|
+
const comment = await this.gitHubGateway.postPullRequestReviewCommentReply({
|
|
212
|
+
owner: repo.owner,
|
|
213
|
+
name: repo.name,
|
|
214
|
+
pullRequestNumber: input.pullRequestNumber,
|
|
215
|
+
commentId: input.commentId,
|
|
216
|
+
body: input.body
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
ok: true,
|
|
220
|
+
tool: name,
|
|
221
|
+
repo: repo.slug,
|
|
222
|
+
summary: `post_pr_review_comment_reply succeeded for ${repo.slug}`,
|
|
223
|
+
result: {
|
|
224
|
+
repo: repo.slug,
|
|
225
|
+
pullRequestNumber: input.pullRequestNumber,
|
|
226
|
+
commentId: input.commentId,
|
|
227
|
+
reply: comment
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
case "create_pr":
|
|
232
|
+
return await this.runReadTool(startedAt, name, rawArguments, sanitizedArguments, async () => {
|
|
233
|
+
const input = validateCreatePullRequest(rawArguments);
|
|
234
|
+
const repo = this.requireManagedRepo(input.repo);
|
|
235
|
+
const baseBranch = input.baseBranch ?? (await this.gitHubGateway.getRepositoryDefaultBranch(repo.owner, repo.name));
|
|
236
|
+
const pullRequest = await this.gitHubGateway.createPullRequest({
|
|
237
|
+
owner: repo.owner,
|
|
238
|
+
name: repo.name,
|
|
239
|
+
headBranch: input.headBranch,
|
|
240
|
+
baseBranch,
|
|
241
|
+
title: input.title,
|
|
242
|
+
body: input.body
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
ok: true,
|
|
246
|
+
tool: name,
|
|
247
|
+
repo: repo.slug,
|
|
248
|
+
summary: `create_pr succeeded for ${repo.slug}`,
|
|
249
|
+
result: {
|
|
250
|
+
repo: repo.slug,
|
|
251
|
+
pullRequest
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
default:
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
tool: name,
|
|
259
|
+
error: {
|
|
260
|
+
code: "tool_not_found",
|
|
261
|
+
message: `Unknown GitHub broker tool "${name}"`
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
const failure = toFailure(name, rawArguments, error);
|
|
268
|
+
const repo = extractRepoSlug(rawArguments);
|
|
269
|
+
this.logInvocation({
|
|
270
|
+
timestamp: startedAt,
|
|
271
|
+
toolName: name,
|
|
272
|
+
status: "error",
|
|
273
|
+
arguments: sanitizedArguments,
|
|
274
|
+
...(repo ? { repo } : {}),
|
|
275
|
+
message: failure.error.message
|
|
276
|
+
});
|
|
277
|
+
return failure;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
requireManagedRepo(repoSlug) {
|
|
281
|
+
const repo = this.managedRepos.get(repoSlug);
|
|
282
|
+
if (!repo) {
|
|
283
|
+
throw new RepoNotManagedError(`Repository "${repoSlug}" is not enabled in management/repos.json`);
|
|
284
|
+
}
|
|
285
|
+
return repo;
|
|
286
|
+
}
|
|
287
|
+
async runReadTool(timestamp, toolName, rawArguments, sanitizedArguments, runner) {
|
|
288
|
+
const result = await runner();
|
|
289
|
+
this.logInvocation({
|
|
290
|
+
timestamp,
|
|
291
|
+
toolName,
|
|
292
|
+
repo: result.repo,
|
|
293
|
+
status: "ok",
|
|
294
|
+
arguments: sanitizedArguments
|
|
295
|
+
});
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
logInvocation(entry) {
|
|
299
|
+
this.logger.info(JSON.stringify({
|
|
300
|
+
component: "github-broker-mcp",
|
|
301
|
+
timestamp: entry.timestamp,
|
|
302
|
+
toolName: entry.toolName,
|
|
303
|
+
repo: entry.repo ?? "unknown",
|
|
304
|
+
resultStatus: entry.status,
|
|
305
|
+
arguments: entry.arguments,
|
|
306
|
+
...(entry.message ? { message: entry.message } : {})
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
class BrokerValidationError extends Error {
|
|
311
|
+
}
|
|
312
|
+
class RepoNotManagedError extends Error {
|
|
313
|
+
}
|
|
314
|
+
function toFailure(name, rawArguments, error) {
|
|
315
|
+
const repo = extractRepoSlug(rawArguments);
|
|
316
|
+
if (error instanceof BrokerValidationError) {
|
|
317
|
+
return {
|
|
318
|
+
ok: false,
|
|
319
|
+
tool: name,
|
|
320
|
+
...(repo ? { repo } : {}),
|
|
321
|
+
error: {
|
|
322
|
+
code: "invalid_arguments",
|
|
323
|
+
message: error.message
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (error instanceof RepoNotManagedError) {
|
|
328
|
+
return {
|
|
329
|
+
ok: false,
|
|
330
|
+
tool: name,
|
|
331
|
+
...(repo ? { repo } : {}),
|
|
332
|
+
error: {
|
|
333
|
+
code: "repo_not_managed",
|
|
334
|
+
message: error.message
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
ok: false,
|
|
340
|
+
tool: name,
|
|
341
|
+
...(repo ? { repo } : {}),
|
|
342
|
+
error: {
|
|
343
|
+
code: "github_error",
|
|
344
|
+
message: error instanceof Error ? error.message : String(error)
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function validateIssueLookup(value) {
|
|
349
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
350
|
+
return {
|
|
351
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
352
|
+
issueNumber: expectPositiveInteger(object.issueNumber, '"issueNumber" must be a positive integer')
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function validatePullRequestLookup(value) {
|
|
356
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
357
|
+
return {
|
|
358
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
359
|
+
pullRequestNumber: expectPositiveInteger(object.pullRequestNumber, '"pullRequestNumber" must be a positive integer')
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function validateCodeSearch(value) {
|
|
363
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
364
|
+
const limitValue = object.limit === undefined ? 10 : object.limit;
|
|
365
|
+
const pathPrefix = optionalTrimmedString(object.pathPrefix, '"pathPrefix" must be a non-empty string when provided');
|
|
366
|
+
return {
|
|
367
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
368
|
+
query: expectNonEmptyString(object.query, '"query" must be a non-empty string'),
|
|
369
|
+
...(pathPrefix ? { pathPrefix } : {}),
|
|
370
|
+
limit: expectBoundedPositiveInteger(limitValue, 1, MAX_SEARCH_LIMIT, `"limit" must be between 1 and ${MAX_SEARCH_LIMIT}`)
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function validatePostIssueComment(value) {
|
|
374
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
375
|
+
return {
|
|
376
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
377
|
+
issueNumber: expectPositiveInteger(object.issueNumber, '"issueNumber" must be a positive integer'),
|
|
378
|
+
body: expectBoundedString(object.body, MAX_COMMENT_BODY_LENGTH, `"body" must be a non-empty string up to ${MAX_COMMENT_BODY_LENGTH} characters`)
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function validateCreatePullRequest(value) {
|
|
382
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
383
|
+
const baseBranch = optionalTrimmedString(object.baseBranch, '"baseBranch" must be a non-empty string when provided');
|
|
384
|
+
return {
|
|
385
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
386
|
+
headBranch: expectNonEmptyString(object.headBranch, '"headBranch" must be a non-empty string'),
|
|
387
|
+
...(baseBranch ? { baseBranch } : {}),
|
|
388
|
+
title: expectNonEmptyString(object.title, '"title" must be a non-empty string'),
|
|
389
|
+
body: expectBoundedString(object.body, MAX_PR_BODY_LENGTH, `"body" must be a non-empty string up to ${MAX_PR_BODY_LENGTH} characters`)
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function validatePostPullRequestReviewCommentReply(value) {
|
|
393
|
+
const object = expectObject(value, "tool arguments must be an object");
|
|
394
|
+
return {
|
|
395
|
+
repo: expectRepoSlug(object.repo, '"repo" must be an "owner/name" string'),
|
|
396
|
+
pullRequestNumber: expectPositiveInteger(object.pullRequestNumber, '"pullRequestNumber" must be a positive integer'),
|
|
397
|
+
commentId: expectPositiveInteger(object.commentId, '"commentId" must be a positive integer'),
|
|
398
|
+
body: expectBoundedString(object.body, MAX_COMMENT_BODY_LENGTH, `"body" must be a non-empty string up to ${MAX_COMMENT_BODY_LENGTH} characters`)
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function sanitizeArguments(name, value) {
|
|
402
|
+
const object = isRecord(value) ? value : {};
|
|
403
|
+
switch (name) {
|
|
404
|
+
case "post_issue_comment":
|
|
405
|
+
case "post_pr_review_comment_reply":
|
|
406
|
+
return {
|
|
407
|
+
repo: safeString(object.repo),
|
|
408
|
+
...(name === "post_issue_comment"
|
|
409
|
+
? { issueNumber: safeNumber(object.issueNumber) }
|
|
410
|
+
: {
|
|
411
|
+
pullRequestNumber: safeNumber(object.pullRequestNumber),
|
|
412
|
+
commentId: safeNumber(object.commentId)
|
|
413
|
+
}),
|
|
414
|
+
bodyPreview: previewBody(object.body),
|
|
415
|
+
bodyLength: safeLength(object.body)
|
|
416
|
+
};
|
|
417
|
+
case "create_pr":
|
|
418
|
+
return {
|
|
419
|
+
repo: safeString(object.repo),
|
|
420
|
+
headBranch: safeString(object.headBranch),
|
|
421
|
+
baseBranch: safeString(object.baseBranch),
|
|
422
|
+
title: safeString(object.title),
|
|
423
|
+
bodyPreview: previewBody(object.body),
|
|
424
|
+
bodyLength: safeLength(object.body)
|
|
425
|
+
};
|
|
426
|
+
default:
|
|
427
|
+
return Object.fromEntries(Object.entries(object).map(([key, entry]) => [key, typeof entry === "string" ? entry : entry ?? null]));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function previewBody(value) {
|
|
431
|
+
if (typeof value !== "string") {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const trimmed = value.trim();
|
|
435
|
+
if (trimmed === "") {
|
|
436
|
+
return "";
|
|
437
|
+
}
|
|
438
|
+
return trimmed.slice(0, 120);
|
|
439
|
+
}
|
|
440
|
+
function safeLength(value) {
|
|
441
|
+
return typeof value === "string" ? value.length : null;
|
|
442
|
+
}
|
|
443
|
+
function safeString(value) {
|
|
444
|
+
return typeof value === "string" ? value : null;
|
|
445
|
+
}
|
|
446
|
+
function safeNumber(value) {
|
|
447
|
+
return typeof value === "number" ? value : null;
|
|
448
|
+
}
|
|
449
|
+
function extractRepoSlug(value) {
|
|
450
|
+
if (!isRecord(value) || typeof value.repo !== "string") {
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
return value.repo;
|
|
454
|
+
}
|
|
455
|
+
function expectObject(value, message) {
|
|
456
|
+
if (!isRecord(value)) {
|
|
457
|
+
throw new BrokerValidationError(message);
|
|
458
|
+
}
|
|
459
|
+
return value;
|
|
460
|
+
}
|
|
461
|
+
function expectRepoSlug(value, message) {
|
|
462
|
+
const repo = expectNonEmptyString(value, message);
|
|
463
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) {
|
|
464
|
+
throw new BrokerValidationError(message);
|
|
465
|
+
}
|
|
466
|
+
return repo;
|
|
467
|
+
}
|
|
468
|
+
function expectNonEmptyString(value, message) {
|
|
469
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
470
|
+
throw new BrokerValidationError(message);
|
|
471
|
+
}
|
|
472
|
+
return value.trim();
|
|
473
|
+
}
|
|
474
|
+
function optionalTrimmedString(value, message) {
|
|
475
|
+
if (value === undefined) {
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
return expectNonEmptyString(value, message);
|
|
479
|
+
}
|
|
480
|
+
function expectPositiveInteger(value, message) {
|
|
481
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
482
|
+
throw new BrokerValidationError(message);
|
|
483
|
+
}
|
|
484
|
+
return value;
|
|
485
|
+
}
|
|
486
|
+
function expectBoundedPositiveInteger(value, minimum, maximum, message) {
|
|
487
|
+
const parsed = expectPositiveInteger(value, message);
|
|
488
|
+
if (parsed < minimum || parsed > maximum) {
|
|
489
|
+
throw new BrokerValidationError(message);
|
|
490
|
+
}
|
|
491
|
+
return parsed;
|
|
492
|
+
}
|
|
493
|
+
function expectBoundedString(value, maximumLength, message) {
|
|
494
|
+
const parsed = expectNonEmptyString(value, message);
|
|
495
|
+
if (parsed.length > maximumLength) {
|
|
496
|
+
throw new BrokerValidationError(message);
|
|
497
|
+
}
|
|
498
|
+
return parsed;
|
|
499
|
+
}
|
|
500
|
+
function isRecord(value) {
|
|
501
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
502
|
+
}
|
|
503
|
+
const TOOL_DEFINITIONS = [
|
|
504
|
+
{
|
|
505
|
+
name: "get_issue",
|
|
506
|
+
title: "Get GitHub Issue",
|
|
507
|
+
description: "Return a single managed GitHub issue with structured metadata.",
|
|
508
|
+
inputSchema: {
|
|
509
|
+
type: "object",
|
|
510
|
+
additionalProperties: false,
|
|
511
|
+
required: ["repo", "issueNumber"],
|
|
512
|
+
properties: {
|
|
513
|
+
repo: { type: "string", description: 'Managed repository slug in "owner/name" form.' },
|
|
514
|
+
issueNumber: { type: "integer", minimum: 1 }
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: "get_issue_comments",
|
|
520
|
+
title: "Get GitHub Issue Comments",
|
|
521
|
+
description: "Return top-level comments for a managed GitHub issue.",
|
|
522
|
+
inputSchema: {
|
|
523
|
+
type: "object",
|
|
524
|
+
additionalProperties: false,
|
|
525
|
+
required: ["repo", "issueNumber"],
|
|
526
|
+
properties: {
|
|
527
|
+
repo: { type: "string" },
|
|
528
|
+
issueNumber: { type: "integer", minimum: 1 }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
name: "get_pr",
|
|
534
|
+
title: "Get GitHub Pull Request",
|
|
535
|
+
description: "Return a single managed GitHub pull request with structured metadata.",
|
|
536
|
+
inputSchema: {
|
|
537
|
+
type: "object",
|
|
538
|
+
additionalProperties: false,
|
|
539
|
+
required: ["repo", "pullRequestNumber"],
|
|
540
|
+
properties: {
|
|
541
|
+
repo: { type: "string" },
|
|
542
|
+
pullRequestNumber: { type: "integer", minimum: 1 }
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: "get_pr_comments",
|
|
548
|
+
title: "Get Pull Request Discussion",
|
|
549
|
+
description: "Return top-level PR comments, reviews, and inline review comments for a managed pull request.",
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: "object",
|
|
552
|
+
additionalProperties: false,
|
|
553
|
+
required: ["repo", "pullRequestNumber"],
|
|
554
|
+
properties: {
|
|
555
|
+
repo: { type: "string" },
|
|
556
|
+
pullRequestNumber: { type: "integer", minimum: 1 }
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
name: "get_pr_checks",
|
|
562
|
+
title: "Get Pull Request CI Check Results",
|
|
563
|
+
description: "Return CI check run results for the head commit of a managed pull request. Reports status (queued/in_progress/completed) and conclusion (success/failure/etc.) for each check.",
|
|
564
|
+
inputSchema: {
|
|
565
|
+
type: "object",
|
|
566
|
+
additionalProperties: false,
|
|
567
|
+
required: ["repo", "pullRequestNumber"],
|
|
568
|
+
properties: {
|
|
569
|
+
repo: { type: "string" },
|
|
570
|
+
pullRequestNumber: { type: "integer", minimum: 1 }
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "search_code",
|
|
576
|
+
title: "Search Managed Repository Code",
|
|
577
|
+
description: "Run a bounded code search in a managed repository.",
|
|
578
|
+
inputSchema: {
|
|
579
|
+
type: "object",
|
|
580
|
+
additionalProperties: false,
|
|
581
|
+
required: ["repo", "query"],
|
|
582
|
+
properties: {
|
|
583
|
+
repo: { type: "string" },
|
|
584
|
+
query: { type: "string" },
|
|
585
|
+
pathPrefix: { type: "string" },
|
|
586
|
+
limit: { type: "integer", minimum: 1, maximum: MAX_SEARCH_LIMIT }
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: "post_issue_comment",
|
|
592
|
+
title: "Post GitHub Issue Comment",
|
|
593
|
+
description: "Post a comment on a managed issue or pull request thread.",
|
|
594
|
+
inputSchema: {
|
|
595
|
+
type: "object",
|
|
596
|
+
additionalProperties: false,
|
|
597
|
+
required: ["repo", "issueNumber", "body"],
|
|
598
|
+
properties: {
|
|
599
|
+
repo: { type: "string" },
|
|
600
|
+
issueNumber: { type: "integer", minimum: 1 },
|
|
601
|
+
body: { type: "string", minLength: 1, maxLength: MAX_COMMENT_BODY_LENGTH }
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: "post_pr_review_comment_reply",
|
|
607
|
+
title: "Reply To Inline Review Comment",
|
|
608
|
+
description: "Post a reply directly to an inline pull request review comment in a managed repository.",
|
|
609
|
+
inputSchema: {
|
|
610
|
+
type: "object",
|
|
611
|
+
additionalProperties: false,
|
|
612
|
+
required: ["repo", "pullRequestNumber", "commentId", "body"],
|
|
613
|
+
properties: {
|
|
614
|
+
repo: { type: "string" },
|
|
615
|
+
pullRequestNumber: { type: "integer", minimum: 1 },
|
|
616
|
+
commentId: { type: "integer", minimum: 1 },
|
|
617
|
+
body: { type: "string", minLength: 1, maxLength: MAX_COMMENT_BODY_LENGTH }
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: "create_pr",
|
|
623
|
+
title: "Create Pull Request",
|
|
624
|
+
description: "Create a pull request in a managed repository from an existing head branch.",
|
|
625
|
+
inputSchema: {
|
|
626
|
+
type: "object",
|
|
627
|
+
additionalProperties: false,
|
|
628
|
+
required: ["repo", "headBranch", "title", "body"],
|
|
629
|
+
properties: {
|
|
630
|
+
repo: { type: "string" },
|
|
631
|
+
headBranch: { type: "string", minLength: 1 },
|
|
632
|
+
baseBranch: { type: "string", minLength: 1 },
|
|
633
|
+
title: { type: "string", minLength: 1 },
|
|
634
|
+
body: { type: "string", minLength: 1, maxLength: MAX_PR_BODY_LENGTH }
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class ComposedBrokerService {
|
|
2
|
+
services;
|
|
3
|
+
routes = new Map();
|
|
4
|
+
constructor(services) {
|
|
5
|
+
this.services = services;
|
|
6
|
+
for (const service of services) {
|
|
7
|
+
const namespace = service.namespace;
|
|
8
|
+
if (namespace !== undefined && (namespace === "" || namespace.includes("."))) {
|
|
9
|
+
throw new Error(`Invalid namespace "${namespace}". Namespaces must be non-empty and must not contain "."`);
|
|
10
|
+
}
|
|
11
|
+
for (const tool of service.listTools()) {
|
|
12
|
+
if (tool.name === "") {
|
|
13
|
+
throw new Error("Tool local name must be non-empty");
|
|
14
|
+
}
|
|
15
|
+
const publicName = namespace ? `${namespace}.${tool.name}` : tool.name;
|
|
16
|
+
if (this.routes.has(publicName)) {
|
|
17
|
+
throw new Error(`Duplicate tool registration for public name "${publicName}"`);
|
|
18
|
+
}
|
|
19
|
+
this.routes.set(publicName, {
|
|
20
|
+
publicName,
|
|
21
|
+
serviceToolName: tool.name,
|
|
22
|
+
service,
|
|
23
|
+
tool
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
listTools() {
|
|
29
|
+
return Array.from(this.routes.values()).map((route) => ({
|
|
30
|
+
...route.tool,
|
|
31
|
+
name: route.publicName
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
async callTool(name, rawArguments) {
|
|
35
|
+
const route = this.routes.get(name);
|
|
36
|
+
if (!route) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
tool: name,
|
|
40
|
+
error: {
|
|
41
|
+
code: "tool_not_found",
|
|
42
|
+
message: `Tool "${name}" not found in any registered service`
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const result = await route.service.callTool(route.serviceToolName, rawArguments);
|
|
47
|
+
// Ensure the returned tool name matches the public name used for the call
|
|
48
|
+
return {
|
|
49
|
+
...result,
|
|
50
|
+
tool: name
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|