@akira-tl/forgerelay 1.0.0 → 1.0.1
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
CHANGED
|
@@ -4,6 +4,14 @@ All notable ForgeRelay changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.0.1] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Preserved Capability names/actions and Workspace lifecycle actions in default pretty tool-call logs instead of collapsing multiplexed operations to generic `ok` lines.
|
|
12
|
+
- Completed attributable tool-call logging for Composite member mutations, checkout/Composite close and delete paths, and Composite-owned `workspace.tasks` success/failure results without logging arbitrary capability arguments.
|
|
13
|
+
- Preserved deletion of closed checkout Workspaces through historical aliases while adding lifecycle logging, avoiding a regression in the existing close/reopen/delete contract.
|
|
14
|
+
|
|
7
15
|
## [1.0.0] - 2026-09-07
|
|
8
16
|
|
|
9
17
|
### Changed
|
|
@@ -7,6 +7,12 @@ export function workspaceLogContext(workspace, _transportSessionId) {
|
|
|
7
7
|
workspace: workspaceLogLabel(workspace.root, workspace.id),
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
+
export function compositeWorkspaceLogContext(workspace) {
|
|
11
|
+
return {
|
|
12
|
+
workspaceId: workspace.id,
|
|
13
|
+
workspace: workspaceLogLabel(workspace.name, workspace.id),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
10
16
|
export function formatDiscoveredWorkspaceInstructions(files, workspaceRoot) {
|
|
11
17
|
return [
|
|
12
18
|
"Workspace instructions discovered for this path. Apply them to follow-up work under their directories:",
|
|
@@ -2,7 +2,7 @@ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
|
2
2
|
import { openAiConversationScopeId } from "../../../request-meta.js";
|
|
3
3
|
import { formatPathForPrompt } from "../../../../workspaces/resources/skills.js";
|
|
4
4
|
import { compositeCapabilityContext } from "../../core/capability-support.js";
|
|
5
|
-
import { logToolCall, textBlock } from "../../core/tool-support.js";
|
|
5
|
+
import { compositeWorkspaceLogContext, logToolCall, textBlock } from "../../core/tool-support.js";
|
|
6
6
|
import { openWorkspaceToolDefinition } from "./workspace-open-schema.js";
|
|
7
7
|
import { presentLocalWorkspaceOpen, workspaceTaskUsageInstruction, } from "./workspace-open-presentation.js";
|
|
8
8
|
export function registerOpenWorkspaceTool(options) {
|
|
@@ -254,6 +254,16 @@ async function handleOpenWorkspace(options, input, extra) {
|
|
|
254
254
|
: "This Composite Workspace currently has no members.",
|
|
255
255
|
"Use the Composite workspaceId as the top-level handle. Work operations on it require an explicit member name; ForgeRelay never infers a member from tool type or purpose.",
|
|
256
256
|
].join("\n");
|
|
257
|
+
logToolCall(config, {
|
|
258
|
+
tool: "open_workspace",
|
|
259
|
+
...compositeWorkspaceLogContext(composite),
|
|
260
|
+
action: `member.${memberAction}`,
|
|
261
|
+
path: memberAction === "update" && member.newName !== undefined
|
|
262
|
+
? `${member.name} -> ${member.newName}`
|
|
263
|
+
: member.name,
|
|
264
|
+
success: true,
|
|
265
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
266
|
+
});
|
|
257
267
|
const response = {
|
|
258
268
|
content: [textBlock(instruction)],
|
|
259
269
|
_meta: {
|
|
@@ -7,7 +7,7 @@ import { formatAgentsPath } from "../../../../workspaces.js";
|
|
|
7
7
|
import { activityRelationFor, activityRequestFor, runActivityToolWithHooks, } from "../../core/activity-support.js";
|
|
8
8
|
import { capabilityContextFor, compositeCapabilityContext, workspaceHookInvocation, } from "../../core/capability-support.js";
|
|
9
9
|
import { capabilityErrorOutputSchema, resultOutputSchema } from "../../core/schemas.js";
|
|
10
|
-
import { logFailedToolResponse, logToolCall, textBlock, toolResultIsError, workspaceLogContext, } from "../../core/tool-support.js";
|
|
10
|
+
import { compositeWorkspaceLogContext, logFailedToolResponse, logToolCall, textBlock, toolResultIsError, workspaceLogContext, } from "../../core/tool-support.js";
|
|
11
11
|
const WRITE_TOOL_ANNOTATIONS = {
|
|
12
12
|
readOnlyHint: false,
|
|
13
13
|
destructiveHint: true,
|
|
@@ -100,6 +100,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
100
100
|
}
|
|
101
101
|
const startedAt = performance.now();
|
|
102
102
|
const context = compositeCapabilityContext(workspaceId, compositeTaskGuides);
|
|
103
|
+
const compositeLogContext = compositeWorkspaceLogContext(compositeWorkspaces.get(workspaceId));
|
|
103
104
|
try {
|
|
104
105
|
if (action === "run") {
|
|
105
106
|
const execution = await capabilityRegistry.run(name, capabilityArguments ?? {}, context, {
|
|
@@ -114,6 +115,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
114
115
|
};
|
|
115
116
|
logToolCall(config, {
|
|
116
117
|
tool: toolNames.capability,
|
|
118
|
+
...compositeLogContext,
|
|
117
119
|
capability: name,
|
|
118
120
|
action,
|
|
119
121
|
success: true,
|
|
@@ -135,6 +137,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
135
137
|
};
|
|
136
138
|
logToolCall(config, {
|
|
137
139
|
tool: toolNames.capability,
|
|
140
|
+
...compositeLogContext,
|
|
138
141
|
capability: name,
|
|
139
142
|
action,
|
|
140
143
|
success: true,
|
|
@@ -148,7 +151,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
148
151
|
const capabilityError = error instanceof CapabilityError
|
|
149
152
|
? error
|
|
150
153
|
: new CapabilityError("execution_failed", error instanceof Error ? error.message : String(error));
|
|
151
|
-
|
|
154
|
+
const result = {
|
|
152
155
|
content: [textBlock(`${capabilityError.code}: ${capabilityError.message}`)],
|
|
153
156
|
structuredContent: {
|
|
154
157
|
name,
|
|
@@ -157,6 +160,13 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
157
160
|
},
|
|
158
161
|
isError: true,
|
|
159
162
|
};
|
|
163
|
+
logFailedToolResponse(config, {
|
|
164
|
+
tool: toolNames.capability,
|
|
165
|
+
...compositeLogContext,
|
|
166
|
+
capability: name,
|
|
167
|
+
action,
|
|
168
|
+
}, result.content, startedAt);
|
|
169
|
+
return result;
|
|
160
170
|
}
|
|
161
171
|
}
|
|
162
172
|
const target = resolveExecutionTarget(workspaceId, member);
|
|
@@ -323,6 +333,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
323
333
|
annotations: WRITE_TOOL_ANNOTATIONS,
|
|
324
334
|
}, async ({ workspaceId, action = "close", commitMessage }, extra) => {
|
|
325
335
|
if (compositeWorkspaces.has(workspaceId)) {
|
|
336
|
+
const startedAt = performance.now();
|
|
326
337
|
if (commitMessage !== undefined) {
|
|
327
338
|
throw new Error("close_workspace commitMessage is not valid for a Composite Workspace.");
|
|
328
339
|
}
|
|
@@ -345,6 +356,14 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
345
356
|
: "The Composite Workspace had no members.",
|
|
346
357
|
"Member Workspace handles, managed worktrees, processes, files, and Workspace Relay routes were not closed, finalized, deleted, or otherwise mutated.",
|
|
347
358
|
].join("\n");
|
|
359
|
+
logToolCall(config, {
|
|
360
|
+
tool: toolNames.closeWorkspace,
|
|
361
|
+
...compositeWorkspaceLogContext(composite),
|
|
362
|
+
action,
|
|
363
|
+
path: composite.name,
|
|
364
|
+
success: true,
|
|
365
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
366
|
+
});
|
|
348
367
|
return {
|
|
349
368
|
content: [textBlock(result)],
|
|
350
369
|
_meta: {
|
|
@@ -382,6 +401,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
382
401
|
}
|
|
383
402
|
const session = workspaces.getWorkspaceSession(workspaceId);
|
|
384
403
|
if (action === "delete" && session.mode === "checkout") {
|
|
404
|
+
const startedAt = performance.now();
|
|
385
405
|
if (commitMessage !== undefined) {
|
|
386
406
|
throw new Error("close_workspace commitMessage is not valid with action=delete for a checkout Workspace.");
|
|
387
407
|
}
|
|
@@ -406,6 +426,14 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
406
426
|
activityQueries.deleteWorkspaceHistory(config.stateDir, session.id);
|
|
407
427
|
await reviewCheckpoints.releaseWorkspace(session.id);
|
|
408
428
|
const result = `Deleted ForgeRelay Workspace ${session.id}. Physical project files were not removed.`;
|
|
429
|
+
logToolCall(config, {
|
|
430
|
+
tool: toolNames.closeWorkspace,
|
|
431
|
+
...workspaceLogContext(session, extra.sessionId),
|
|
432
|
+
action: "delete",
|
|
433
|
+
path: session.root,
|
|
434
|
+
success: true,
|
|
435
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
436
|
+
});
|
|
409
437
|
return {
|
|
410
438
|
content: [textBlock(result)],
|
|
411
439
|
_meta: {
|
|
@@ -531,6 +559,7 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
531
559
|
logToolCall(config, {
|
|
532
560
|
tool: toolNames.closeWorkspace,
|
|
533
561
|
...workspaceLogContext(workspace, extra.sessionId),
|
|
562
|
+
action,
|
|
534
563
|
path: closed.sourceRoot,
|
|
535
564
|
success: true,
|
|
536
565
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -575,9 +604,18 @@ export function registerWorkspaceAuxiliaryTools(options) {
|
|
|
575
604
|
if (processSessions.activeWorkspaceIds().has(checkoutWorkspaceId)) {
|
|
576
605
|
throw new Error(`Workspace ${checkoutWorkspaceId} still owns a running process. Poll, interrupt, or wait for it before closing this workspace.`);
|
|
577
606
|
}
|
|
607
|
+
const startedAt = performance.now();
|
|
578
608
|
workspaces.closeWorkspace(checkoutWorkspaceId);
|
|
579
609
|
await reviewCheckpoints.releaseWorkspace(checkoutWorkspaceId);
|
|
580
610
|
const result = `Closed checkout-backed Workspace ${checkoutWorkspaceId}; its ForgeRelay identity was preserved for later reopen. Physical project files were not removed.`;
|
|
611
|
+
logToolCall(config, {
|
|
612
|
+
tool: toolNames.closeWorkspace,
|
|
613
|
+
...workspaceLogContext(workspace, extra.sessionId),
|
|
614
|
+
action: "close",
|
|
615
|
+
path: workspace.root,
|
|
616
|
+
success: true,
|
|
617
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
618
|
+
});
|
|
581
619
|
return {
|
|
582
620
|
content: [textBlock(result)],
|
|
583
621
|
_meta: {
|
|
@@ -131,6 +131,17 @@ function toolTarget(entry, tool) {
|
|
|
131
131
|
if (isShellTool(tool)) {
|
|
132
132
|
return stringField(entry.commandPreview) ?? stringField(entry.workingDirectory);
|
|
133
133
|
}
|
|
134
|
+
if (tool === "capability") {
|
|
135
|
+
return [stringField(entry.capability), stringField(entry.action)]
|
|
136
|
+
.filter((value) => value !== undefined)
|
|
137
|
+
.join(" ") || undefined;
|
|
138
|
+
}
|
|
139
|
+
if (tool === "open_workspace" || tool === "close_workspace") {
|
|
140
|
+
return [
|
|
141
|
+
stringField(entry.action),
|
|
142
|
+
stringField(entry.path) ?? stringField(entry.workingDirectory),
|
|
143
|
+
].filter((value) => value !== undefined).join(" ") || undefined;
|
|
144
|
+
}
|
|
134
145
|
return stringField(entry.path) ?? stringField(entry.workingDirectory);
|
|
135
146
|
}
|
|
136
147
|
function toolResult(entry, tool, options) {
|