@akira-tl/forgerelay 0.3.4 → 0.3.5
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/CHANGELOG.md +16 -0
- package/capabilities/artifacts-review/GUIDE.md +2 -4
- package/dist/artifact-tools.js +0 -128
- package/dist/capabilities.js +2 -5
- package/dist/hooks.js +12 -0
- package/dist/mcp/server-instructions.js +4 -9
- package/dist/review-checkpoints.js +4 -4
- package/dist/server.js +5 -263
- package/dist/ui/.vite/manifest.json +280 -280
- package/dist/ui/assets/{angular-html-CQmCUfkv.js → angular-html-BT2Z6jee.js} +1 -1
- package/dist/ui/assets/{angular-ts-BSbfxpMS.js → angular-ts-BwPE8YDt.js} +1 -1
- package/dist/ui/assets/{apl-CWCaH-E3.js → apl-CGbONVvU.js} +1 -1
- package/dist/ui/assets/{astro-R_p4G0xA.js → astro-DIAPy7cj.js} +1 -1
- package/dist/ui/assets/{blade-irHensDY.js → blade-Cgtd19Ky.js} +1 -1
- package/dist/ui/assets/{c-_XEQUoq6.js → c-acnvZ8tm.js} +1 -1
- package/dist/ui/assets/{cobol-Css6VQjJ.js → cobol-G-7HLVKW.js} +1 -1
- package/dist/ui/assets/{coffee-CpXUkyDm.js → coffee-DQFyKxEB.js} +1 -1
- package/dist/ui/assets/{cpp-BQHsPWwI.js → cpp-UyrmHXan.js} +1 -1
- package/dist/ui/assets/{crystal-Cbv8J9Fm.js → crystal-DuiWhEO3.js} +1 -1
- package/dist/ui/assets/{css-D8DkrS3U.js → css-D_WCbY6m.js} +1 -1
- package/dist/ui/assets/{edge-bBL_XDAG.js → edge-COkalhFi.js} +1 -1
- package/dist/ui/assets/{elixir-CQjsM9sA.js → elixir-cdlzbYR1.js} +1 -1
- package/dist/ui/assets/{elm-DLFl-jEZ.js → elm-ZwDYuYpb.js} +1 -1
- package/dist/ui/assets/{erb-BJjsdB_Q.js → erb-C8WG9TtC.js} +1 -1
- package/dist/ui/assets/{git-rebase-CqP5A2s0.js → git-rebase-CMlDVJBT.js} +1 -1
- package/dist/ui/assets/{glimmer-js-B5Hh_7xe.js → glimmer-js-DeLOQlol.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-oaeSAC8s.js → glimmer-ts-DTEM04TK.js} +1 -1
- package/dist/ui/assets/{glsl-BylkOpcF.js → glsl-IJV4SB-M.js} +1 -1
- package/dist/ui/assets/{graphql-DsVRvP6S.js → graphql-PM-WDnoB.js} +1 -1
- package/dist/ui/assets/{hack-D4N5abT_.js → hack-C14sh6Lh.js} +1 -1
- package/dist/ui/assets/{haml-CIAJQIox.js → haml-B_5nS6Kx.js} +1 -1
- package/dist/ui/assets/{handlebars-DDEZyNXR.js → handlebars-eS-J0O-M.js} +1 -1
- package/dist/ui/assets/{heavy-payload-DafM8yjB.js → heavy-payload-BKB0QDOI.js} +1 -1
- package/dist/ui/assets/{html-zDy-3lvy.js → html-C3TPjrC5.js} +1 -1
- package/dist/ui/assets/{html-derivative-CZ91Y2d5.js → html-derivative-BlVZU2id.js} +1 -1
- package/dist/ui/assets/{http-ezaodZRf.js → http-BmbZzA5x.js} +1 -1
- package/dist/ui/assets/{hurl-BmL0o7TO.js → hurl-Dnd_YpOD.js} +1 -1
- package/dist/ui/assets/{java-Q2C0vwbH.js → java-ne9HdIVn.js} +1 -1
- package/dist/ui/assets/{javascript-UDhBkHE3.js → javascript-DpHT58rr.js} +1 -1
- package/dist/ui/assets/{jinja-BWRskcYS.js → jinja-DTvkZdHA.js} +1 -1
- package/dist/ui/assets/{jison-B18BY9Ds.js → jison-DUJG-tmh.js} +1 -1
- package/dist/ui/assets/{json-DNj7MpRr.js → json-DJmnBdxW.js} +1 -1
- package/dist/ui/assets/{jsx-CVpHJ6cr.js → jsx-BclJnZnN.js} +1 -1
- package/dist/ui/assets/{julia-CJBNBS9c.js → julia-P5B4pHFa.js} +1 -1
- package/dist/ui/assets/{just-Bmu2JCQT.js → just-DYaoh7C6.js} +1 -1
- package/dist/ui/assets/{latex-BB7F93KY.js → latex-CI5lnkWV.js} +1 -1
- package/dist/ui/assets/{liquid-Btnomc6E.js → liquid-D3LnAs_z.js} +1 -1
- package/dist/ui/assets/{lua-D0FYT9Vo.js → lua-dZC7ZQIY.js} +1 -1
- package/dist/ui/assets/{marko-MHLw0ZlK.js → marko-CRDrjqfS.js} +1 -1
- package/dist/ui/assets/{mdc-CSr-suGy.js → mdc-ZzwgmJVn.js} +1 -1
- package/dist/ui/assets/{nginx-ur1l6E-g.js → nginx-BAvFXGSV.js} +1 -1
- package/dist/ui/assets/{nim-B2k0IugA.js → nim-BHIFawe9.js} +1 -1
- package/dist/ui/assets/{perl-BqOHxFDn.js → perl-C_f5AnLa.js} +1 -1
- package/dist/ui/assets/{php-DzPK6O1_.js → php-DDqpegL5.js} +1 -1
- package/dist/ui/assets/{pug-D169OZkr.js → pug-CSaAZXyd.js} +1 -1
- package/dist/ui/assets/{qml-BtemJTC4.js → qml-CNHvRs_z.js} +1 -1
- package/dist/ui/assets/{r-BGVHu0bd.js → r-CAfoQND2.js} +1 -1
- package/dist/ui/assets/{razor-Diisjg1d.js → razor-BDfAPy7I.js} +1 -1
- package/dist/ui/assets/{regexp-B2F5vMZ6.js → regexp-Mug5jltQ.js} +1 -1
- package/dist/ui/assets/{review-payload-B-3I3Oj_.js → review-payload-D7GIdYDj.js} +1 -1
- package/dist/ui/assets/{rst-BxpeZwuT.js → rst-BzPP2gle.js} +1 -1
- package/dist/ui/assets/{ruby-DrCq4riN.js → ruby-CQjzokzX.js} +1 -1
- package/dist/ui/assets/{sas-CI_2W-1n.js → sas-Bp37KGr7.js} +1 -1
- package/dist/ui/assets/{scrollbar-DHsDF5w-.js → scrollbar-HUMBdLLK.js} +3 -3
- package/dist/ui/assets/{scss-BxtyHZ1p.js → scss-C_Z1DYnw.js} +1 -1
- package/dist/ui/assets/{shellscript-BY_YYrHT.js → shellscript-CLfJpn3I.js} +1 -1
- package/dist/ui/assets/{shellsession-C6uBrLmA.js → shellsession-CNc62UhY.js} +1 -1
- package/dist/ui/assets/{soy-BHM9gdPh.js → soy-CF44SpBo.js} +1 -1
- package/dist/ui/assets/{sql-XmhfEGz_.js → sql-CYnzfiQ1.js} +1 -1
- package/dist/ui/assets/{stata-BbHY7NEo.js → stata-DVeMA68W.js} +1 -1
- package/dist/ui/assets/{surrealql-CD49DZ3E.js → surrealql-52sF96Yv.js} +1 -1
- package/dist/ui/assets/{svelte-CbYsEYJY.js → svelte-ydwT7UbF.js} +1 -1
- package/dist/ui/assets/{templ-B5JoUxEB.js → templ-BXcoVoGh.js} +1 -1
- package/dist/ui/assets/{tex-DZcOfTZK.js → tex-COsYFp7O.js} +1 -1
- package/dist/ui/assets/{ts-tags-BeU-xqc0.js → ts-tags-CF--LE1s.js} +1 -1
- package/dist/ui/assets/{tsx-V7W1xGl3.js → tsx-DLBt2-2f.js} +1 -1
- package/dist/ui/assets/{twig-MIaJo5BU.js → twig-YoM-DiI5.js} +1 -1
- package/dist/ui/assets/{typescript-C0WESuVc.js → typescript-Ce9JGkNI.js} +1 -1
- package/dist/ui/assets/{vue-eJHwc9ra.js → vue-B5LDEy4l.js} +1 -1
- package/dist/ui/assets/{vue-html-CLmBujTU.js → vue-html-CPdGGk4T.js} +1 -1
- package/dist/ui/assets/{vue-vine-BP5kjIGb.js → vue-vine-B5bjgk0a.js} +1 -1
- package/dist/ui/assets/{workspace-app-C1qR5cAY.js → workspace-app-Cm9EX5x4.js} +3 -3
- package/dist/ui/assets/{xml-j7pmyP48.js → xml-Cun8xb0T.js} +1 -1
- package/dist/ui/assets/{xsl-5VFHbg6o.js → xsl-wls_CEK_.js} +1 -1
- package/dist/ui/assets/{yaml-DxFAt-KU.js → yaml-BxuXn9OL.js} +1 -1
- package/dist/ui/workspace-app.html +1 -1
- package/docs/artifact-exchange.md +4 -3
- package/docs/chatgpt-coding-workflow.md +5 -3
- package/docs/configuration.md +12 -10
- package/docs/debugging.md +2 -2
- package/docs/gotchas.md +3 -3
- package/docs/security.md +3 -2
- package/package.json +1 -1
- package/scripts/debug/accept.mjs +53 -12
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable ForgeRelay changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.3.5] - 2026-08-10
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Regular `minimal` and `full` MCP modes now expose the same canonical 9-tool surface: `open_workspace`, `capability`, `close_workspace`, `read`, `write`, `edit`, `rename`, `delete`, and `bash`. Search and directory inspection use shell commands through `bash`; `full` remains only as a configuration-compatibility value.
|
|
12
|
+
- `review.changes` and `artifact.download` are now Capability Gateway-only workflows. Their dedicated top-level compatibility aliases have been removed, while review cards identify themselves as `capability` results with `capabilityName="review.changes"`.
|
|
13
|
+
- Capability fingerprints now report semantic runtime capabilities such as `review.changes` instead of implementation-shaped search/review tool names, and current Guides/docs/bootstrap instructions use the final Core Surface + Capability Gateway model.
|
|
14
|
+
|
|
15
|
+
### Removed
|
|
16
|
+
|
|
17
|
+
- Removed the regular dedicated `grep`, `glob`, `ls`, aggregate-review, and native-artifact MCP adapters after their canonical workflows moved to `bash` or the Capability Gateway.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Successful `report: true` lifecycle Hook reports are now mirrored into the model-readable structured result as well as MCP text content, so Hosts that surface only structured tool results still expose the Hook outcome to the Agent.
|
|
22
|
+
|
|
7
23
|
## [0.3.4] - 2026-08-10
|
|
8
24
|
|
|
9
25
|
### Changed
|
|
@@ -12,8 +12,6 @@ Native artifact download 默认关闭,且当前安全发布实现主要面向
|
|
|
12
12
|
2. 调用 `capability`,`name="artifact.download"`、`action="run"`,把 Host 原样提供的 native `file` 值放在 Gateway 顶层 `file`,并在 `arguments.path` 传 workspace-relative 目标路径;
|
|
13
13
|
3. 成功后使用返回的 normalized relative path 配合普通 `read` / `rename` / `delete` / shell 等工具继续处理。
|
|
14
14
|
|
|
15
|
-
0.3.3 仍保留 `download_artifact` 作为迁移期 compatibility alias,但新的 Agent workflow 不应把它视为长期 public surface。
|
|
16
|
-
|
|
17
15
|
`artifact.download` 会创建缺失的父目录,但拒绝覆盖已有目标。不要把 native file 值改造成:
|
|
18
16
|
|
|
19
17
|
- 任意 URL 字符串;
|
|
@@ -35,10 +33,10 @@ Native artifact download 默认关闭,且当前安全发布实现主要面向
|
|
|
35
33
|
|
|
36
34
|
Plain MCP Host 可以忽略这些 UI metadata;不要把 UI 是否显示当成文件操作是否成功的判据。
|
|
37
35
|
|
|
38
|
-
当 Capability catalog 广告 `review.changes` 时,本轮成功修改文件后,在最后一个相关 file mutation 之后、final response 之前通过 `capability` 调用一次,让用户看到聚合 diff;不要每改一个文件就调用一次。
|
|
36
|
+
当 Capability catalog 广告 `review.changes` 时,本轮成功修改文件后,在最后一个相关 file mutation 之后、final response 之前通过 `capability` 调用一次,让用户看到聚合 diff;不要每改一个文件就调用一次。
|
|
39
37
|
|
|
40
38
|
`review.changes` 使用 Git-backed review checkpoint。它按 workspace 跟踪 open/baseline 状态,展示自上次 review checkpoint 以来的 coherent diff,并在成功 review 后推进 baseline。当前版本要求可用的 Git workspace;checkpoint 缺失或 root 不匹配时会明确失败或使用受控 fallback,而不是凭空重建历史。
|
|
41
39
|
|
|
42
40
|
## Capability ownership
|
|
43
41
|
|
|
44
|
-
Artifact transfer 和 change review 都属于 ForgeRelay registered capability,而不是 Agent 自己的文件搬运协议。`tools/list`
|
|
42
|
+
Artifact transfer 和 change review 都属于 ForgeRelay registered capability,而不是 Agent 自己的文件搬运协议。`tools/list` 只暴露稳定 `capability` Gateway;真正可用的低频能力以当前 workspace 的 Capability catalog 为准。本指南提供流程和边界,不额外创造隐藏执行入口。
|
package/dist/artifact-tools.js
CHANGED
|
@@ -2,18 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import { link, lstat, mkdir, open, readdir, unlink, } from "node:fs/promises";
|
|
4
4
|
import { isAbsolute, join, normalize, sep } from "node:path";
|
|
5
|
-
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
6
|
-
import * as z from "zod/v4";
|
|
7
5
|
import { ArtifactError } from "./artifact-error.js";
|
|
8
|
-
import { runToolWithHooks } from "./hooks.js";
|
|
9
|
-
import { describeIncomingArtifactValue, IncomingArtifactAdapterRegistry, } from "./incoming-artifacts.js";
|
|
10
|
-
import { logEvent, workspaceLogLabel } from "./logger.js";
|
|
11
|
-
const ARTIFACT_WRITE_ANNOTATIONS = {
|
|
12
|
-
readOnlyHint: false,
|
|
13
|
-
destructiveHint: false,
|
|
14
|
-
idempotentHint: false,
|
|
15
|
-
openWorldHint: true,
|
|
16
|
-
};
|
|
17
6
|
const NO_FOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
18
7
|
const DIRECTORY_FLAGS = fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0) | NO_FOLLOW;
|
|
19
8
|
const PARTIAL_PREFIX = ".forgerelay-download-";
|
|
@@ -21,64 +10,9 @@ const PARTIAL_SUFFIX = ".partial";
|
|
|
21
10
|
const STALE_PARTIAL_AGE_MS = 24 * 60 * 60 * 1_000;
|
|
22
11
|
const MAX_STALE_PARTIAL_CLEANUP = 32;
|
|
23
12
|
const ARTIFACT_DOWNLOAD_PLATFORMS = new Set(["linux"]);
|
|
24
|
-
const openAIFileReferenceInputSchema = z.strictObject({
|
|
25
|
-
download_url: z.string(),
|
|
26
|
-
file_id: z.string(),
|
|
27
|
-
mime_type: z.string().nullable().optional(),
|
|
28
|
-
file_name: z.string().nullable().optional(),
|
|
29
|
-
name: z.string().nullable().optional(),
|
|
30
|
-
size: z.number().int().nonnegative().nullable().optional(),
|
|
31
|
-
});
|
|
32
13
|
export function isArtifactDownloadSupportedPlatform(platform = process.platform) {
|
|
33
14
|
return ARTIFACT_DOWNLOAD_PLATFORMS.has(platform);
|
|
34
15
|
}
|
|
35
|
-
export function registerArtifactTools(server, { config, workspaces, hooks, incomingArtifactAdapters = [], incomingArtifactRegistry, }) {
|
|
36
|
-
const incomingRegistry = incomingArtifactRegistry
|
|
37
|
-
?? new IncomingArtifactAdapterRegistry(incomingArtifactAdapters);
|
|
38
|
-
registerAppTool(server, "download_artifact", {
|
|
39
|
-
title: "Download attached or generated file",
|
|
40
|
-
description: "Stream one MCP-host-provided native file to a requested relative path inside an already-open workspace. Existing destinations, arbitrary URLs, absolute paths, traversal, symlinked parents, local source paths, and malformed file objects are rejected.",
|
|
41
|
-
inputSchema: {
|
|
42
|
-
file: openAIFileReferenceInputSchema.describe("Native file value authorized and supplied by the MCP host."),
|
|
43
|
-
workspaceId: z.string().min(1).describe("Workspace identifier returned by open_workspace."),
|
|
44
|
-
path: z.string().min(1).describe("Relative destination path inside the selected workspace. The destination must not already exist."),
|
|
45
|
-
},
|
|
46
|
-
outputSchema: {
|
|
47
|
-
path: z.string(),
|
|
48
|
-
},
|
|
49
|
-
_meta: { "openai/fileParams": ["file"] },
|
|
50
|
-
annotations: ARTIFACT_WRITE_ANNOTATIONS,
|
|
51
|
-
}, async (input) => {
|
|
52
|
-
const workspace = workspaces.getWorkspace(input.workspaceId);
|
|
53
|
-
return runToolWithHooks(hooks, {
|
|
54
|
-
tool: "download_artifact",
|
|
55
|
-
invocation: {
|
|
56
|
-
workspaceId: workspace.id,
|
|
57
|
-
workspaceRoot: workspace.root,
|
|
58
|
-
workspaceMode: workspace.mode,
|
|
59
|
-
sourceRoot: workspace.sourceRoot,
|
|
60
|
-
},
|
|
61
|
-
payload: { path: input.path },
|
|
62
|
-
changedPaths: (result) => [result.structuredContent.path],
|
|
63
|
-
operation: () => executeArtifactTool(config, input, {
|
|
64
|
-
workspace: workspaceLogLabel(workspace.root, workspace.id),
|
|
65
|
-
}, async () => {
|
|
66
|
-
const downloaded = await downloadIncomingArtifact({
|
|
67
|
-
registry: incomingRegistry,
|
|
68
|
-
workspaceId: workspace.id,
|
|
69
|
-
workspaceRoot: workspace.root,
|
|
70
|
-
maxFileBytes: config.artifactMaxFileBytes,
|
|
71
|
-
file: input.file,
|
|
72
|
-
path: input.path,
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
publicResult: { path: downloaded.path },
|
|
76
|
-
logResult: downloaded,
|
|
77
|
-
};
|
|
78
|
-
}),
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
16
|
/**
|
|
83
17
|
* Stream a trusted native file directly into one already-open workspace.
|
|
84
18
|
*
|
|
@@ -159,53 +93,6 @@ export async function downloadIncomingArtifact({ registry, workspaceId, workspac
|
|
|
159
93
|
await workspaceHandle?.close().catch(() => undefined);
|
|
160
94
|
}
|
|
161
95
|
}
|
|
162
|
-
export function artifactToolLogFields(input) {
|
|
163
|
-
return {
|
|
164
|
-
fileProvided: input.file !== undefined,
|
|
165
|
-
fileReferenceShape: describeIncomingArtifactValue(input.file),
|
|
166
|
-
downloadUrlHostname: incomingFileDownloadHostname(input.file),
|
|
167
|
-
workspaceId: input.workspaceId,
|
|
168
|
-
path: input.path,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
async function executeArtifactTool(config, input, logContext, operation) {
|
|
172
|
-
const startedAt = performance.now();
|
|
173
|
-
try {
|
|
174
|
-
const { publicResult, logResult } = await operation();
|
|
175
|
-
if (config.logging.toolCalls) {
|
|
176
|
-
logEvent(config.logging, "info", "artifact_tool_call", {
|
|
177
|
-
tool: "download_artifact",
|
|
178
|
-
...artifactToolLogFields(input),
|
|
179
|
-
...logContext,
|
|
180
|
-
path: logResult.path,
|
|
181
|
-
size: logResult.size,
|
|
182
|
-
sha256: logResult.sha256,
|
|
183
|
-
success: true,
|
|
184
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
return artifactToolResponse(publicResult);
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
if (config.logging.toolCalls) {
|
|
191
|
-
logEvent(config.logging, "warn", "artifact_tool_call", {
|
|
192
|
-
tool: "download_artifact",
|
|
193
|
-
...artifactToolLogFields(input),
|
|
194
|
-
...logContext,
|
|
195
|
-
success: false,
|
|
196
|
-
errorCode: error instanceof ArtifactError ? error.code : "internal_error",
|
|
197
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
throw error;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function artifactToolResponse(result) {
|
|
204
|
-
return {
|
|
205
|
-
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
206
|
-
structuredContent: result,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
96
|
async function openDirectoryNoFollow(path, code, message) {
|
|
210
97
|
let handle;
|
|
211
98
|
try {
|
|
@@ -356,21 +243,6 @@ async function writeAll(handle, buffer, position) {
|
|
|
356
243
|
offset += bytesWritten;
|
|
357
244
|
}
|
|
358
245
|
}
|
|
359
|
-
function incomingFileDownloadHostname(value) {
|
|
360
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
361
|
-
return undefined;
|
|
362
|
-
}
|
|
363
|
-
const rawUrl = value.download_url;
|
|
364
|
-
if (typeof rawUrl !== "string")
|
|
365
|
-
return undefined;
|
|
366
|
-
try {
|
|
367
|
-
const hostname = new URL(rawUrl).hostname.toLowerCase();
|
|
368
|
-
return hostname.length > 0 && hostname.length <= 253 ? hostname : undefined;
|
|
369
|
-
}
|
|
370
|
-
catch {
|
|
371
|
-
return undefined;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
246
|
function incomingStreamChunk(value) {
|
|
375
247
|
if (Buffer.isBuffer(value))
|
|
376
248
|
return value;
|
package/dist/capabilities.js
CHANGED
|
@@ -21,7 +21,7 @@ const CAPABILITY_GUIDE_DEFINITIONS = [
|
|
|
21
21
|
{
|
|
22
22
|
name: "artifacts-review",
|
|
23
23
|
description: "Native artifact transfer and aggregate change review.",
|
|
24
|
-
whenToRead: "Read for host-provided files or
|
|
24
|
+
whenToRead: "Read for host-provided files or aggregate change review.",
|
|
25
25
|
enabled: (config) => config.artifactsEnabled || config.widgets === "changes",
|
|
26
26
|
},
|
|
27
27
|
{
|
|
@@ -76,9 +76,6 @@ export function buildCapabilityFingerprint(config, version, context = {}) {
|
|
|
76
76
|
"hooks.lifecycle",
|
|
77
77
|
"capability-guides.read",
|
|
78
78
|
];
|
|
79
|
-
if (config.toolMode === "full") {
|
|
80
|
-
capabilities.push("inspection.search-tools");
|
|
81
|
-
}
|
|
82
79
|
if (config.subagents) {
|
|
83
80
|
capabilities.push("subagent.profiles");
|
|
84
81
|
}
|
|
@@ -89,7 +86,7 @@ export function buildCapabilityFingerprint(config, version, context = {}) {
|
|
|
89
86
|
capabilities.push("ui.mcp-app");
|
|
90
87
|
}
|
|
91
88
|
if (config.widgets === "changes") {
|
|
92
|
-
capabilities.push("review.
|
|
89
|
+
capabilities.push("review.changes");
|
|
93
90
|
}
|
|
94
91
|
return {
|
|
95
92
|
version,
|
package/dist/hooks.js
CHANGED
|
@@ -152,6 +152,10 @@ export function attachHookReports(result, executions) {
|
|
|
152
152
|
const summary = formatVisibleHookReports(executions);
|
|
153
153
|
if (!summary || !isRecord(result) || !Array.isArray(result.content))
|
|
154
154
|
return result;
|
|
155
|
+
const structuredContent = isRecord(result.structuredContent) ? result.structuredContent : undefined;
|
|
156
|
+
const structuredResult = typeof structuredContent?.result === "string"
|
|
157
|
+
? structuredContent.result
|
|
158
|
+
: undefined;
|
|
155
159
|
return {
|
|
156
160
|
...result,
|
|
157
161
|
content: [
|
|
@@ -161,6 +165,14 @@ export function attachHookReports(result, executions) {
|
|
|
161
165
|
text: summary,
|
|
162
166
|
},
|
|
163
167
|
],
|
|
168
|
+
...(structuredContent && structuredResult !== undefined
|
|
169
|
+
? {
|
|
170
|
+
structuredContent: {
|
|
171
|
+
...structuredContent,
|
|
172
|
+
result: `${structuredResult}\n\n${summary}`,
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
: {}),
|
|
164
176
|
};
|
|
165
177
|
}
|
|
166
178
|
function appendHookReportsToError(error, executions) {
|
|
@@ -6,9 +6,6 @@ export const toolNames = {
|
|
|
6
6
|
edit: "edit",
|
|
7
7
|
rename: "rename",
|
|
8
8
|
delete: "delete",
|
|
9
|
-
grep: "grep",
|
|
10
|
-
glob: "glob",
|
|
11
|
-
ls: "ls",
|
|
12
9
|
shell: "bash",
|
|
13
10
|
writeStdin: "write_stdin",
|
|
14
11
|
capability: "capability",
|
|
@@ -23,9 +20,9 @@ export function buildToolDescriptions(config) {
|
|
|
23
20
|
const skillCapability = config.skillsEnabled
|
|
24
21
|
? " Advertised skill paths may also be outside the workspace."
|
|
25
22
|
: "";
|
|
26
|
-
const shellSurface = config.toolMode === "
|
|
27
|
-
?
|
|
28
|
-
: "";
|
|
23
|
+
const shellSurface = config.toolMode === "codex"
|
|
24
|
+
? ""
|
|
25
|
+
: " Use shell commands for search and directory inspection instead of dedicated MCP search tools.";
|
|
29
26
|
return {
|
|
30
27
|
read: `Read a file inside an open workspace or the OS temp directory. Instruction files and advertised capability guides returned by ${toolNames.openWorkspace} are also readable when applicable.${skillCapability} Only advertised entry files and files under already-loaded advertised directories are readable outside the normal roots. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
31
28
|
write: `Create or completely overwrite a file inside an open workspace or the OS temp directory. Workspace paths may be relative; OS temp paths may be absolute. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
@@ -62,9 +59,7 @@ function defaultWorkflowInstructions(config) {
|
|
|
62
59
|
if (config.toolMode === "codex") {
|
|
63
60
|
return `Use ${toolNames.read} for direct file reads, ${toolNames.rename} and ${toolNames.delete} for direct path moves or removals, apply_patch for content modifications, exec_command for inspection, tests, builds, and other commands, and ${toolNames.writeStdin} to poll or interact with running processes.`;
|
|
64
61
|
}
|
|
65
|
-
const inspection =
|
|
66
|
-
? `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection.`
|
|
67
|
-
: `Use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection.`;
|
|
62
|
+
const inspection = `Use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection.`;
|
|
68
63
|
return joinInstructions(inspection, `Prefer ${toolNames.edit} for targeted content modifications, ${toolNames.write} only for new files or complete rewrites, ${toolNames.rename} for path moves, ${toolNames.delete} for removals, and ${toolNames.shell} for tests, builds, git inspection, package scripts, generators, formatters, and commands that are better executed by the shell. If ${toolNames.shell} returns a running process with a processId, call ${toolNames.shell} again with action=\"process\" when you need to poll, wait, interact, resize, or interrupt it; otherwise you may continue other work and consume its completion notice from a later tool result.`);
|
|
69
64
|
}
|
|
70
65
|
function joinInstructions(...parts) {
|
|
@@ -39,19 +39,19 @@ export function createReviewCheckpointManager() {
|
|
|
39
39
|
}
|
|
40
40
|
assertWorkspaceRoot(state, workspaceId, root);
|
|
41
41
|
if (!state?.gitRoot) {
|
|
42
|
-
throw new Error(state?.diagnostic ?? "
|
|
42
|
+
throw new Error(state?.diagnostic ?? "review.changes requires a Git workspace in this version.");
|
|
43
43
|
}
|
|
44
44
|
let effectiveSince = since;
|
|
45
45
|
let usedWorkspaceOpenFallback = false;
|
|
46
46
|
if (since === "last_shown" && !state.baselineRefAvailable) {
|
|
47
47
|
if (!state.openRefAvailable) {
|
|
48
|
-
throw new Error("Review checkpoints are missing;
|
|
48
|
+
throw new Error("Review checkpoints are missing; review.changes cannot reconstruct that history safely.");
|
|
49
49
|
}
|
|
50
50
|
effectiveSince = "workspace_open";
|
|
51
51
|
usedWorkspaceOpenFallback = true;
|
|
52
52
|
}
|
|
53
53
|
else if (since === "workspace_open" && !state.openRefAvailable) {
|
|
54
|
-
throw new Error("The workspace-open review checkpoint is missing;
|
|
54
|
+
throw new Error("The workspace-open review checkpoint is missing; review.changes cannot reconstruct that history safely.");
|
|
55
55
|
}
|
|
56
56
|
const baselineRef = effectiveSince === "workspace_open" ? state.openRef : state.baselineRef;
|
|
57
57
|
const baseline = (await git(state.gitRoot, ["rev-parse", "--verify", `${baselineRef}^{commit}`])).stdout.trim();
|
|
@@ -98,7 +98,7 @@ async function initializeWorkspaceState(states, workspaceId, root) {
|
|
|
98
98
|
try {
|
|
99
99
|
const eligibility = await getGitEligibility(root);
|
|
100
100
|
if (!eligibility.ok || !eligibility.gitRoot) {
|
|
101
|
-
state.diagnostic = eligibility.message ?? "
|
|
101
|
+
state.diagnostic = eligibility.message ?? "review.changes requires a Git workspace in this version.";
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
const [openCommit, baselineCommit] = await Promise.all([
|
package/dist/server.js
CHANGED
|
@@ -17,7 +17,7 @@ import { applyPatch } from "./apply-patch.js";
|
|
|
17
17
|
import { buildCapabilityFingerprint } from "./capabilities.js";
|
|
18
18
|
import { CapabilityError, createCapabilityRegistry, } from "./capability-registry.js";
|
|
19
19
|
import { deletePath, renamePath } from "./file-mutations.js";
|
|
20
|
-
import { downloadIncomingArtifact, isArtifactDownloadSupportedPlatform,
|
|
20
|
+
import { downloadIncomingArtifact, isArtifactDownloadSupportedPlatform, } from "./artifact-tools.js";
|
|
21
21
|
import { ArtifactError } from "./artifact-error.js";
|
|
22
22
|
import { loadConfig } from "./config.js";
|
|
23
23
|
import { attachHookReports, HookRunner, runToolWithHooks } from "./hooks.js";
|
|
@@ -25,7 +25,7 @@ import { checkHookConfiguration } from "./hook-cli.js";
|
|
|
25
25
|
import { buildServerInstructions, buildShellMutationPolicy, buildToolDescriptions, toolNames, } from "./mcp/server-instructions.js";
|
|
26
26
|
import { createOpenAIIncomingArtifactAdapter, IncomingArtifactAdapterRegistry, } from "./incoming-artifacts.js";
|
|
27
27
|
import { logEvent, requestIp, requestPath, commandPreview, transportSessionIdPrefix, workspaceLogLabel, } from "./logger.js";
|
|
28
|
-
import { editFileTool,
|
|
28
|
+
import { editFileTool, readFileTool, writeFileTool, } from "./pi-tools.js";
|
|
29
29
|
import { SingleUserOAuthProvider } from "./oauth-provider.js";
|
|
30
30
|
import { McpTransportRegistry, } from "./mcp-sessions.js";
|
|
31
31
|
import { ProcessManager, resolveProcessId, } from "./process-sessions.js";
|
|
@@ -67,7 +67,7 @@ function shouldAttachWidget(mode, kind) {
|
|
|
67
67
|
case "off":
|
|
68
68
|
return false;
|
|
69
69
|
case "changes":
|
|
70
|
-
return kind === "workspace" || kind === "
|
|
70
|
+
return kind === "workspace" || kind === "capability";
|
|
71
71
|
case "full":
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
@@ -711,7 +711,6 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
711
711
|
files: review.files,
|
|
712
712
|
},
|
|
713
713
|
card: {
|
|
714
|
-
tool: "show_changes",
|
|
715
714
|
summary: review.summary,
|
|
716
715
|
files: review.files,
|
|
717
716
|
payload: { patch: review.patch },
|
|
@@ -1120,9 +1119,10 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
1120
1119
|
...(execution.card
|
|
1121
1120
|
? {
|
|
1122
1121
|
_meta: {
|
|
1123
|
-
tool:
|
|
1122
|
+
tool: toolNames.capability,
|
|
1124
1123
|
card: {
|
|
1125
1124
|
workspaceId,
|
|
1125
|
+
capabilityName: name,
|
|
1126
1126
|
summary: execution.card.summary ?? {},
|
|
1127
1127
|
files: execution.card.files,
|
|
1128
1128
|
payload: execution.card.payload ?? {},
|
|
@@ -1716,255 +1716,6 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
1716
1716
|
});
|
|
1717
1717
|
});
|
|
1718
1718
|
}
|
|
1719
|
-
if (config.widgets === "changes") {
|
|
1720
|
-
registerAppTool(server, "show_changes", {
|
|
1721
|
-
title: "Show changes",
|
|
1722
|
-
description: "Show the changes made in this turn for an open workspace. Call this once after the final related file change and before your final response so the user can review the combined diff. Do not call it after each individual file change.",
|
|
1723
|
-
inputSchema: {
|
|
1724
|
-
workspaceId: z
|
|
1725
|
-
.string()
|
|
1726
|
-
.describe("Workspace identifier returned by open_workspace."),
|
|
1727
|
-
},
|
|
1728
|
-
outputSchema: resultOutputSchema(),
|
|
1729
|
-
...toolWidgetDescriptorMeta(config, "show_changes"),
|
|
1730
|
-
annotations: { readOnlyHint: true },
|
|
1731
|
-
}, async ({ workspaceId }, extra) => {
|
|
1732
|
-
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1733
|
-
return runToolWithHooks(hooks, {
|
|
1734
|
-
tool: "show_changes",
|
|
1735
|
-
invocation: workspaceHookInvocation(workspace),
|
|
1736
|
-
operation: async () => {
|
|
1737
|
-
const startedAt = performance.now();
|
|
1738
|
-
const review = await reviewWorkspaceChanges(reviewCheckpoints, workspace);
|
|
1739
|
-
const content = [textBlock(review.result)];
|
|
1740
|
-
logToolCall(config, {
|
|
1741
|
-
tool: "show_changes",
|
|
1742
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1743
|
-
success: true,
|
|
1744
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
1745
|
-
});
|
|
1746
|
-
return {
|
|
1747
|
-
content,
|
|
1748
|
-
_meta: {
|
|
1749
|
-
tool: "show_changes",
|
|
1750
|
-
card: {
|
|
1751
|
-
workspaceId,
|
|
1752
|
-
summary: review.summary,
|
|
1753
|
-
files: review.files,
|
|
1754
|
-
payload: {
|
|
1755
|
-
patch: review.patch,
|
|
1756
|
-
},
|
|
1757
|
-
},
|
|
1758
|
-
},
|
|
1759
|
-
structuredContent: {
|
|
1760
|
-
result: contentText(content),
|
|
1761
|
-
},
|
|
1762
|
-
};
|
|
1763
|
-
},
|
|
1764
|
-
});
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
|
-
if (config.toolMode === "full") {
|
|
1768
|
-
registerAppTool(server, toolNames.grep, {
|
|
1769
|
-
title: "Grep",
|
|
1770
|
-
description: "Search file contents inside an open workspace or the OS temp directory. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
|
|
1771
|
-
inputSchema: {
|
|
1772
|
-
workspaceId: z
|
|
1773
|
-
.string()
|
|
1774
|
-
.describe("Workspace identifier returned by open_workspace."),
|
|
1775
|
-
pattern: z.string().describe("Search pattern."),
|
|
1776
|
-
path: z
|
|
1777
|
-
.string()
|
|
1778
|
-
.optional()
|
|
1779
|
-
.describe("Optional path or glob scope relative to the workspace root, or an absolute path inside the OS temp directory."),
|
|
1780
|
-
include: z.string().optional().describe("Optional include glob."),
|
|
1781
|
-
},
|
|
1782
|
-
outputSchema: resultOutputSchema(),
|
|
1783
|
-
...toolWidgetDescriptorMeta(config, "search"),
|
|
1784
|
-
annotations: { readOnlyHint: true },
|
|
1785
|
-
}, async ({ workspaceId, ...input }, extra) => {
|
|
1786
|
-
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1787
|
-
return runToolWithHooks(hooks, {
|
|
1788
|
-
tool: toolNames.grep,
|
|
1789
|
-
invocation: workspaceHookInvocation(workspace),
|
|
1790
|
-
payload: { pattern: input.pattern, path: input.path, include: input.include },
|
|
1791
|
-
isFailure: toolResultIsError,
|
|
1792
|
-
operation: async () => {
|
|
1793
|
-
const startedAt = performance.now();
|
|
1794
|
-
const response = await grepFilesTool(input, {
|
|
1795
|
-
cwd: workspace.root,
|
|
1796
|
-
root: workspace.root,
|
|
1797
|
-
fileRoots: workspaces.fileToolRoots(workspace),
|
|
1798
|
-
});
|
|
1799
|
-
if (response.isError) {
|
|
1800
|
-
logFailedToolResponse(config, {
|
|
1801
|
-
tool: toolNames.grep,
|
|
1802
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1803
|
-
path: input.path,
|
|
1804
|
-
}, response.content, startedAt);
|
|
1805
|
-
return response;
|
|
1806
|
-
}
|
|
1807
|
-
const summary = {
|
|
1808
|
-
pattern: input.pattern,
|
|
1809
|
-
scope: input.path ?? ".",
|
|
1810
|
-
...textSummary(response.content),
|
|
1811
|
-
};
|
|
1812
|
-
logToolCall(config, {
|
|
1813
|
-
tool: toolNames.grep,
|
|
1814
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1815
|
-
path: input.path,
|
|
1816
|
-
success: true,
|
|
1817
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
1818
|
-
});
|
|
1819
|
-
return {
|
|
1820
|
-
...response,
|
|
1821
|
-
_meta: {
|
|
1822
|
-
tool: toolNames.grep,
|
|
1823
|
-
card: {
|
|
1824
|
-
workspaceId,
|
|
1825
|
-
path: input.path,
|
|
1826
|
-
summary,
|
|
1827
|
-
payload: { content: response.content },
|
|
1828
|
-
},
|
|
1829
|
-
},
|
|
1830
|
-
structuredContent: {
|
|
1831
|
-
result: contentText(response.content),
|
|
1832
|
-
},
|
|
1833
|
-
};
|
|
1834
|
-
},
|
|
1835
|
-
});
|
|
1836
|
-
});
|
|
1837
|
-
registerAppTool(server, toolNames.glob, {
|
|
1838
|
-
title: "Glob",
|
|
1839
|
-
description: "Find files by glob pattern inside an open workspace or the OS temp directory. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
|
|
1840
|
-
inputSchema: {
|
|
1841
|
-
workspaceId: z
|
|
1842
|
-
.string()
|
|
1843
|
-
.describe("Workspace identifier returned by open_workspace."),
|
|
1844
|
-
pattern: z.string().describe("File glob pattern."),
|
|
1845
|
-
path: z
|
|
1846
|
-
.string()
|
|
1847
|
-
.optional()
|
|
1848
|
-
.describe("Optional path scope relative to the workspace root, or an absolute path inside the OS temp directory."),
|
|
1849
|
-
},
|
|
1850
|
-
outputSchema: resultOutputSchema(),
|
|
1851
|
-
...toolWidgetDescriptorMeta(config, "search"),
|
|
1852
|
-
annotations: { readOnlyHint: true },
|
|
1853
|
-
}, async ({ workspaceId, ...input }, extra) => {
|
|
1854
|
-
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1855
|
-
return runToolWithHooks(hooks, {
|
|
1856
|
-
tool: toolNames.glob,
|
|
1857
|
-
invocation: workspaceHookInvocation(workspace),
|
|
1858
|
-
payload: { pattern: input.pattern, path: input.path },
|
|
1859
|
-
isFailure: toolResultIsError,
|
|
1860
|
-
operation: async () => {
|
|
1861
|
-
const startedAt = performance.now();
|
|
1862
|
-
const response = await findFilesTool(input, {
|
|
1863
|
-
cwd: workspace.root,
|
|
1864
|
-
root: workspace.root,
|
|
1865
|
-
fileRoots: workspaces.fileToolRoots(workspace),
|
|
1866
|
-
});
|
|
1867
|
-
if (response.isError) {
|
|
1868
|
-
logFailedToolResponse(config, {
|
|
1869
|
-
tool: toolNames.glob,
|
|
1870
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1871
|
-
path: input.path,
|
|
1872
|
-
}, response.content, startedAt);
|
|
1873
|
-
return response;
|
|
1874
|
-
}
|
|
1875
|
-
const summary = {
|
|
1876
|
-
pattern: input.pattern,
|
|
1877
|
-
scope: input.path ?? ".",
|
|
1878
|
-
...textSummary(response.content),
|
|
1879
|
-
};
|
|
1880
|
-
logToolCall(config, {
|
|
1881
|
-
tool: toolNames.glob,
|
|
1882
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1883
|
-
path: input.path,
|
|
1884
|
-
success: true,
|
|
1885
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
1886
|
-
});
|
|
1887
|
-
return {
|
|
1888
|
-
...response,
|
|
1889
|
-
_meta: {
|
|
1890
|
-
tool: toolNames.glob,
|
|
1891
|
-
card: {
|
|
1892
|
-
workspaceId,
|
|
1893
|
-
path: input.path,
|
|
1894
|
-
summary,
|
|
1895
|
-
payload: { content: response.content },
|
|
1896
|
-
},
|
|
1897
|
-
},
|
|
1898
|
-
structuredContent: {
|
|
1899
|
-
result: contentText(response.content),
|
|
1900
|
-
},
|
|
1901
|
-
};
|
|
1902
|
-
},
|
|
1903
|
-
});
|
|
1904
|
-
});
|
|
1905
|
-
registerAppTool(server, toolNames.ls, {
|
|
1906
|
-
title: "Ls",
|
|
1907
|
-
description: "List a directory inside an open workspace or the OS temp directory. Use this for directory inspection before reading files. Call open_workspace first and pass workspaceId.",
|
|
1908
|
-
inputSchema: {
|
|
1909
|
-
workspaceId: z
|
|
1910
|
-
.string()
|
|
1911
|
-
.describe("Workspace identifier returned by open_workspace."),
|
|
1912
|
-
path: z
|
|
1913
|
-
.string()
|
|
1914
|
-
.describe("Directory path to list, relative to the workspace root or absolute inside the OS temp directory."),
|
|
1915
|
-
},
|
|
1916
|
-
outputSchema: resultOutputSchema(),
|
|
1917
|
-
...toolWidgetDescriptorMeta(config, "directory"),
|
|
1918
|
-
annotations: { readOnlyHint: true },
|
|
1919
|
-
}, async ({ workspaceId, ...input }, extra) => {
|
|
1920
|
-
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1921
|
-
return runToolWithHooks(hooks, {
|
|
1922
|
-
tool: toolNames.ls,
|
|
1923
|
-
invocation: workspaceHookInvocation(workspace),
|
|
1924
|
-
payload: { path: input.path },
|
|
1925
|
-
isFailure: toolResultIsError,
|
|
1926
|
-
operation: async () => {
|
|
1927
|
-
const startedAt = performance.now();
|
|
1928
|
-
const response = await listDirectoryTool(input, {
|
|
1929
|
-
cwd: workspace.root,
|
|
1930
|
-
root: workspace.root,
|
|
1931
|
-
fileRoots: workspaces.fileToolRoots(workspace),
|
|
1932
|
-
});
|
|
1933
|
-
if (response.isError) {
|
|
1934
|
-
logFailedToolResponse(config, {
|
|
1935
|
-
tool: toolNames.ls,
|
|
1936
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1937
|
-
path: input.path,
|
|
1938
|
-
}, response.content, startedAt);
|
|
1939
|
-
return response;
|
|
1940
|
-
}
|
|
1941
|
-
const summary = textSummary(response.content);
|
|
1942
|
-
logToolCall(config, {
|
|
1943
|
-
tool: toolNames.ls,
|
|
1944
|
-
...workspaceLogContext(workspace, extra.sessionId),
|
|
1945
|
-
path: input.path,
|
|
1946
|
-
success: true,
|
|
1947
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
1948
|
-
});
|
|
1949
|
-
return {
|
|
1950
|
-
...response,
|
|
1951
|
-
_meta: {
|
|
1952
|
-
tool: toolNames.ls,
|
|
1953
|
-
card: {
|
|
1954
|
-
workspaceId,
|
|
1955
|
-
path: input.path,
|
|
1956
|
-
summary,
|
|
1957
|
-
payload: { content: response.content },
|
|
1958
|
-
},
|
|
1959
|
-
},
|
|
1960
|
-
structuredContent: {
|
|
1961
|
-
result: contentText(response.content),
|
|
1962
|
-
},
|
|
1963
|
-
};
|
|
1964
|
-
},
|
|
1965
|
-
});
|
|
1966
|
-
});
|
|
1967
|
-
}
|
|
1968
1719
|
if (config.toolMode !== "codex") {
|
|
1969
1720
|
registerAppTool(server, toolNames.shell, {
|
|
1970
1721
|
title: "Bash",
|
|
@@ -2146,15 +1897,6 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2146
1897
|
});
|
|
2147
1898
|
}
|
|
2148
1899
|
registerProcessTools(server, config, workspaces, processSessions, hooks);
|
|
2149
|
-
if (config.artifactsEnabled && isArtifactDownloadSupportedPlatform()) {
|
|
2150
|
-
registerArtifactTools(server, {
|
|
2151
|
-
config,
|
|
2152
|
-
workspaces,
|
|
2153
|
-
hooks,
|
|
2154
|
-
incomingArtifactAdapters,
|
|
2155
|
-
incomingArtifactRegistry,
|
|
2156
|
-
});
|
|
2157
|
-
}
|
|
2158
1900
|
return server;
|
|
2159
1901
|
}
|
|
2160
1902
|
export function createServer(config = loadConfig(), options = {}) {
|