@akira-tl/forgerelay 0.8.5 → 0.8.7
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 +22 -0
- package/README.md +8 -10
- package/dist/activity/audit-store.js +44 -6
- package/dist/activity/mcp-query-tools.js +53 -36
- package/dist/activity/query-service.js +41 -11
- package/dist/cli.js +16 -17
- package/dist/composite-activity.js +124 -33
- package/dist/config.js +8 -13
- package/dist/db/migrations.js +9 -0
- package/dist/db/schema.js +1 -0
- package/dist/lsp/test-support/server-fixture.js +7 -7
- package/dist/mcp/server-instructions.js +2 -2
- package/dist/process-sessions.js +2 -2
- package/dist/remote-auth.js +11 -1
- package/dist/remote-mcp-connection-pool.js +90 -0
- package/dist/remote-transport.js +28 -14
- package/dist/remote-workspace-relay.js +53 -23
- package/dist/server.js +105 -70
- package/dist/skills.js +1 -1
- package/dist/subagents/profiles.js +1 -2
- package/dist/subagents/providers/adapters/pi.js +2 -2
- package/dist/subagents/providers/availability.js +2 -2
- package/dist/subagents/providers/path.js +4 -4
- package/dist/ui/.vite/manifest.json +32 -32
- package/dist/ui/activity-panel-app.html +3 -3
- package/dist/ui/assets/{activity-panel-app-E1ju2dqI.js → activity-panel-app-CUAN6zyW.js} +1 -1
- package/dist/ui/assets/{heavy-payload-CeW-n9w5.js → heavy-payload-CgzrutLm.js} +1 -1
- package/dist/ui/assets/{review-payload-B9CO298v.js → review-payload-BrLbezbq.js} +1 -1
- package/dist/ui/assets/{scrollbar-C2twAENW.js → scrollbar-CbhpdW05.js} +1 -1
- package/dist/ui/assets/workspace-app-Bhj96tsR.js +1 -0
- package/dist/ui/assets/{workspace-app-CwbJnb_w.js → workspace-app-CxwJuZyS.js} +1 -1
- package/dist/ui/assets/{workspace-app-BztEvZIC.js → workspace-app-D6UR0AFl.js} +3 -3
- package/dist/ui/assets/workspace-app-ldjBmCJR.css +1 -0
- package/dist/ui/assets/workspace-lifecycle-app-Cqfhx9pV.js +1 -0
- package/dist/ui/workspace-app.html +4 -4
- package/dist/ui/workspace-lifecycle-app.html +4 -4
- package/dist/user-config.js +3 -19
- package/dist/workspace-presentation.js +69 -0
- package/dist/workspace-store.js +29 -1
- package/dist/workspaces.js +59 -19
- package/docs/agent-profile-schema.md +4 -9
- package/docs/artifact-exchange.md +2 -1
- package/docs/chatgpt-coding-workflow.md +21 -19
- package/docs/configuration.md +34 -39
- package/docs/gotchas.md +10 -7
- package/docs/roadmap.md +11 -4
- package/docs/security.md +3 -2
- package/docs/setup.md +8 -5
- package/docs/versioning.md +1 -1
- package/package.json +3 -2
- package/scripts/debug/accept.mjs +7 -1
- package/scripts/debug/relay-accept.mjs +0 -1
- package/scripts/debug/runtime.mjs +0 -4
- package/scripts/debug/runtime.test.mjs +0 -2
- package/scripts/debug/traffic/run.sh +5 -0
- package/scripts/debug/traffic/traffic-audit.mjs +907 -0
- package/scripts/release/push-ready.mjs +124 -0
- package/scripts/release/push-ready.test.mjs +108 -0
- package/scripts/release/release-gate.test.mjs +1 -0
- package/scripts/release-proof.mjs +16 -1
- package/dist/ui/assets/workspace-app-YnUST8IP.css +0 -1
- package/dist/ui/assets/workspace-app-rKuhdae8.js +0 -1
- package/dist/ui/assets/workspace-lifecycle-app-CEfMdudP.js +0 -1
|
@@ -2,7 +2,8 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import { isRemoteMcpUnauthorized, refreshRemoteAuthentication,
|
|
5
|
+
import { isRemoteMcpUnauthorized, refreshRemoteAuthentication, } from "./remote-auth.js";
|
|
6
|
+
import { RemoteMcpConnectionPool } from "./remote-mcp-connection-pool.js";
|
|
6
7
|
import { withRemoteServiceEndpoint } from "./remote-transport.js";
|
|
7
8
|
import { loadForgeRelayFiles, writeForgeRelayRemote, } from "./user-config.js";
|
|
8
9
|
const ROUTE_LOCK_RETRY_MS = 10;
|
|
@@ -15,6 +16,7 @@ export class RemoteWorkspaceRelay {
|
|
|
15
16
|
authEnv;
|
|
16
17
|
routeStateDir;
|
|
17
18
|
routeStatePath;
|
|
19
|
+
mcpConnections = new RemoteMcpConnectionPool();
|
|
18
20
|
constructor(configDir, stateDir) {
|
|
19
21
|
this.authEnv = { FORGERELAY_CONFIG_DIR: configDir };
|
|
20
22
|
this.routeStateDir = stateDir;
|
|
@@ -27,6 +29,9 @@ export class RemoteWorkspaceRelay {
|
|
|
27
29
|
this.loadRoutes();
|
|
28
30
|
return this.routes.has(workspaceId);
|
|
29
31
|
}
|
|
32
|
+
async shutdown() {
|
|
33
|
+
await this.mcpConnections.closeAll();
|
|
34
|
+
}
|
|
30
35
|
async inspectWorkspace(gatewayWorkspaceId) {
|
|
31
36
|
const route = this.requireRoute(gatewayWorkspaceId);
|
|
32
37
|
const resolved = this.remoteByInstance(route.remoteInstanceId);
|
|
@@ -235,11 +240,14 @@ export class RemoteWorkspaceRelay {
|
|
|
235
240
|
throw sanitizedRemoteError(error, route.remoteWorkspaceId, gatewayWorkspaceId);
|
|
236
241
|
}
|
|
237
242
|
}
|
|
243
|
+
async activityIndex(turnId, knownRevision, conversationScopeId) {
|
|
244
|
+
return this.callTurnTool(turnId, "activity_index", { turnId, ...(knownRevision !== undefined ? { knownRevision } : {}) }, conversationScopeId);
|
|
245
|
+
}
|
|
238
246
|
async activityDetail(turnId, activityId, conversationScopeId) {
|
|
239
247
|
return this.callTurnTool(turnId, "activity_detail", { turnId, activityId }, conversationScopeId);
|
|
240
248
|
}
|
|
241
|
-
async activityOutput(turnId, outputId, conversationScopeId) {
|
|
242
|
-
return this.callTurnTool(turnId, "activity_output", { turnId, outputId }, conversationScopeId);
|
|
249
|
+
async activityOutput(turnId, outputId, conversationScopeId, cursor) {
|
|
250
|
+
return this.callTurnTool(turnId, "activity_output", { turnId, outputId, ...(cursor !== undefined ? { cursor } : {}) }, conversationScopeId);
|
|
243
251
|
}
|
|
244
252
|
async callTurnTool(turnId, name, args, conversationScopeId) {
|
|
245
253
|
const gatewayWorkspaceId = this.turnRoutes.get(turnId);
|
|
@@ -464,31 +472,53 @@ export class RemoteWorkspaceRelay {
|
|
|
464
472
|
return { alias: entry[0], remote: entry[1] };
|
|
465
473
|
}
|
|
466
474
|
async callRemoteTool(alias, initialRemote, name, args, conversationScopeId) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
475
|
+
let remote = initialRemote;
|
|
476
|
+
let refreshed = false;
|
|
477
|
+
if (remote.accessTokenExpiresAt <= Math.floor(Date.now() / 1000)) {
|
|
478
|
+
remote = await withRemoteServiceEndpoint(remote.target, remote.sshRoute, (endpoint) => this.refreshRemote(alias, remote, endpoint));
|
|
479
|
+
refreshed = true;
|
|
480
|
+
}
|
|
481
|
+
let connection;
|
|
482
|
+
try {
|
|
483
|
+
connection = await this.mcpConnections.get(remote);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
if (!refreshed && isRemoteMcpUnauthorized(error)) {
|
|
487
|
+
remote = await withRemoteServiceEndpoint(remote.target, remote.sshRoute, (endpoint) => this.refreshRemote(alias, remote, endpoint));
|
|
472
488
|
refreshed = true;
|
|
489
|
+
connection = await this.mcpConnections.get(remote);
|
|
473
490
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
arguments: args,
|
|
477
|
-
...(conversationScopeId
|
|
478
|
-
? { _meta: { "openai/session": conversationScopeId } }
|
|
479
|
-
: {}),
|
|
480
|
-
})));
|
|
481
|
-
try {
|
|
482
|
-
return await invoke();
|
|
491
|
+
else {
|
|
492
|
+
throw new Error(`Remote ForgeRelay ${alias} request failed: ${errorMessage(error)}`, { cause: error });
|
|
483
493
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
494
|
+
}
|
|
495
|
+
const invoke = async (active) => CallToolResultSchema.parse(await active.client.callTool({
|
|
496
|
+
name,
|
|
497
|
+
arguments: args,
|
|
498
|
+
...(conversationScopeId
|
|
499
|
+
? { _meta: { "openai/session": conversationScopeId } }
|
|
500
|
+
: {}),
|
|
501
|
+
}));
|
|
502
|
+
try {
|
|
503
|
+
return await invoke(connection);
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
if (!refreshed && isRemoteMcpUnauthorized(error)) {
|
|
507
|
+
remote = await this.refreshRemote(alias, remote, connection.endpoint);
|
|
508
|
+
refreshed = true;
|
|
509
|
+
await this.mcpConnections.invalidate(remote.instanceId, connection);
|
|
510
|
+
const refreshedConnection = await this.mcpConnections.get(remote);
|
|
511
|
+
try {
|
|
512
|
+
return await invoke(refreshedConnection);
|
|
513
|
+
}
|
|
514
|
+
catch (retryError) {
|
|
515
|
+
await this.mcpConnections.invalidate(remote.instanceId, refreshedConnection);
|
|
516
|
+
throw new Error(`Remote ForgeRelay ${alias} request failed: ${errorMessage(retryError)}`, { cause: retryError });
|
|
488
517
|
}
|
|
489
|
-
throw new Error(`Remote ForgeRelay ${alias} request failed: ${errorMessage(error)}`, { cause: error });
|
|
490
518
|
}
|
|
491
|
-
|
|
519
|
+
await this.mcpConnections.invalidate(remote.instanceId, connection);
|
|
520
|
+
throw new Error(`Remote ForgeRelay ${alias} request failed: ${errorMessage(error)}`, { cause: error });
|
|
521
|
+
}
|
|
492
522
|
}
|
|
493
523
|
async refreshRemote(alias, remote, endpoint) {
|
|
494
524
|
const refreshed = await refreshRemoteAuthentication(remote, endpoint);
|
package/dist/server.js
CHANGED
|
@@ -55,7 +55,8 @@ import { formatPathForPrompt } from "./skills.js";
|
|
|
55
55
|
import { createWorkspaceStore } from "./workspace-store.js";
|
|
56
56
|
import { WorkspaceTaskReminderTracker } from "./workspace-task-reminders.js";
|
|
57
57
|
import { WorkspaceTaskStore } from "./workspace-tasks.js";
|
|
58
|
-
import {
|
|
58
|
+
import { compactWorkspacePresentation } from "./workspace-presentation.js";
|
|
59
|
+
import { formatAgentsPath, WorkspaceRegistry, } from "./workspaces.js";
|
|
59
60
|
import { formatAvailableSubagentProfile, summarizeSubagentProfile } from "./subagents/profiles.js";
|
|
60
61
|
import { formatSubagentProviderAvailabilitySummary, formatUnavailableSubagentProvider, getSubagentProviderAvailabilitySnapshot, } from "./subagents/providers/availability.js";
|
|
61
62
|
import { capabilityActivityAuditRequest, capabilityActivityAuditResult } from "./subagents/sessions/mcp/audit.js";
|
|
@@ -1342,6 +1343,7 @@ function registerProcessTools(server, config, workspaces, processSessions, hooks
|
|
|
1342
1343
|
}
|
|
1343
1344
|
export function createMcpServer(config, workspaces, reviewCheckpoints, processSessions, subagentProviders, incomingArtifactAdapters, codeIntelligence, activityLifecycle, bashOutputStore, activityQueries, options = {}) {
|
|
1344
1345
|
const connectionScopeId = `mcp-connection:${randomUUID()}`;
|
|
1346
|
+
const ownsRemoteWorkspaces = options.remoteWorkspaces === undefined;
|
|
1345
1347
|
const remoteWorkspaces = options.remoteWorkspaces
|
|
1346
1348
|
?? new RemoteWorkspaceRelay(config.configDir, config.stateDir);
|
|
1347
1349
|
const compositeWorkspaces = options.compositeWorkspaces
|
|
@@ -1564,6 +1566,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
1564
1566
|
providerUnavailableReason: availability?.reason,
|
|
1565
1567
|
};
|
|
1566
1568
|
});
|
|
1569
|
+
const bootstrapComponents = new Set(opened.bootstrapContextComponents);
|
|
1567
1570
|
return {
|
|
1568
1571
|
member: memberName,
|
|
1569
1572
|
workspaceId: compositeWorkspaceId,
|
|
@@ -1573,16 +1576,13 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
1573
1576
|
capabilityFingerprint,
|
|
1574
1577
|
capabilityCatalog,
|
|
1575
1578
|
includeBootstrapContext: opened.includeBootstrapContext,
|
|
1576
|
-
...(
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
agents,
|
|
1584
|
-
skillDiagnostics: redactSkillDiagnosticPaths(workspace.skillDiagnostics),
|
|
1585
|
-
}
|
|
1579
|
+
...(bootstrapComponents.has("capabilityGuides") ? { capabilityGuides } : {}),
|
|
1580
|
+
...(bootstrapComponents.has("agentsFiles") ? { agentsFiles } : {}),
|
|
1581
|
+
...(bootstrapComponents.has("availableAgentsFiles") ? { availableAgentsFiles } : {}),
|
|
1582
|
+
...(bootstrapComponents.has("skills") ? { skills } : {}),
|
|
1583
|
+
...(bootstrapComponents.has("agentProfiles") ? { agentProviders, agents } : {}),
|
|
1584
|
+
...(bootstrapComponents.has("skillDiagnostics")
|
|
1585
|
+
? { skillDiagnostics: redactSkillDiagnosticPaths(workspace.skillDiagnostics) }
|
|
1586
1586
|
: {}),
|
|
1587
1587
|
instruction: opened.includeBootstrapContext
|
|
1588
1588
|
? `Bootstrap context for Composite member ${memberName}. Keep using Composite workspaceId ${compositeWorkspaceId} and pass member=${memberName} for work operations.`
|
|
@@ -2107,19 +2107,21 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2107
2107
|
const workspacePanelStates = new Map();
|
|
2108
2108
|
const workspacePanelState = (workspaceId) => {
|
|
2109
2109
|
const remembered = workspacePanelStates.get(workspaceId);
|
|
2110
|
-
if (
|
|
2110
|
+
if (remoteWorkspaces.has(workspaceId) || compositeWorkspaces.has(workspaceId)) {
|
|
2111
2111
|
return remembered;
|
|
2112
|
+
}
|
|
2112
2113
|
try {
|
|
2113
2114
|
const workspace = workspaces.getWorkspace(workspaceId);
|
|
2114
|
-
|
|
2115
|
+
if (remembered)
|
|
2116
|
+
return remembered;
|
|
2117
|
+
return compactWorkspacePresentation({
|
|
2115
2118
|
workspaceId: workspace.id,
|
|
2116
2119
|
root: workspace.root,
|
|
2117
2120
|
path: workspace.root,
|
|
2118
2121
|
mode: workspace.mode,
|
|
2119
2122
|
sourceRoot: workspace.sourceRoot,
|
|
2120
|
-
instruction: `Use workspaceId ${workspace.id} for subsequent calls.`,
|
|
2121
2123
|
summary: { mode: workspace.mode },
|
|
2122
|
-
};
|
|
2124
|
+
});
|
|
2123
2125
|
}
|
|
2124
2126
|
catch {
|
|
2125
2127
|
return undefined;
|
|
@@ -2132,7 +2134,11 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2132
2134
|
const card = meta.card;
|
|
2133
2135
|
if (meta.tool !== toolNames.openWorkspace || typeof card !== "object" || card === null)
|
|
2134
2136
|
return;
|
|
2135
|
-
|
|
2137
|
+
const compact = compactWorkspacePresentation(card);
|
|
2138
|
+
workspacePanelStates.set(workspaceId, {
|
|
2139
|
+
...(workspacePanelStates.get(workspaceId) ?? {}),
|
|
2140
|
+
...compact,
|
|
2141
|
+
});
|
|
2136
2142
|
};
|
|
2137
2143
|
const workspaceAppResourceMetadata = {
|
|
2138
2144
|
description: "Historical ForgeRelay tool card UI.",
|
|
@@ -2880,7 +2886,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2880
2886
|
rememberWorkspacePanelState(opened.workspaceId, response);
|
|
2881
2887
|
return response;
|
|
2882
2888
|
}
|
|
2883
|
-
const { workspace, agentsFiles, availableAgentsFiles, hookReports, workspaceReused, includeBootstrapContext, contextFingerprint, } = await workspaces.openWorkspace({ path, workspaceId, mode, baseRef, newWorktree, newWorkspace, context }, {
|
|
2889
|
+
const { workspace, agentsFiles, availableAgentsFiles, hookReports, workspaceReused, includeBootstrapContext, bootstrapContextComponents, contextFingerprint, } = await workspaces.openWorkspace({ path, workspaceId, mode, baseRef, newWorktree, newWorkspace, context }, {
|
|
2884
2890
|
conversationScopeId,
|
|
2885
2891
|
protectedWorkspaceIds,
|
|
2886
2892
|
});
|
|
@@ -2926,15 +2932,18 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2926
2932
|
const cardAvailableAgentsFiles = availableAgentsFiles.map((file) => ({
|
|
2927
2933
|
path: formatAgentsPath(file.path, workspace.root),
|
|
2928
2934
|
}));
|
|
2929
|
-
const
|
|
2930
|
-
const
|
|
2935
|
+
const bootstrapComponents = new Set(bootstrapContextComponents);
|
|
2936
|
+
const visibleSkills = bootstrapComponents.has("skills") ? cardSkills : [];
|
|
2937
|
+
const visibleSkillDiagnostics = bootstrapComponents.has("skillDiagnostics")
|
|
2931
2938
|
? redactSkillDiagnosticPaths(workspace.skillDiagnostics)
|
|
2932
2939
|
: [];
|
|
2933
|
-
const visibleCapabilityGuides =
|
|
2934
|
-
const visibleAgentProviders =
|
|
2935
|
-
const visibleAgents =
|
|
2936
|
-
const loadedAgentsFiles =
|
|
2937
|
-
const availableAgentsFileOutputs =
|
|
2940
|
+
const visibleCapabilityGuides = bootstrapComponents.has("capabilityGuides") ? capabilityGuides : [];
|
|
2941
|
+
const visibleAgentProviders = bootstrapComponents.has("agentProfiles") ? cardAgentProviders : [];
|
|
2942
|
+
const visibleAgents = bootstrapComponents.has("agentProfiles") ? cardAgents : [];
|
|
2943
|
+
const loadedAgentsFiles = bootstrapComponents.has("agentsFiles") ? cardAgentsFiles : [];
|
|
2944
|
+
const availableAgentsFileOutputs = bootstrapComponents.has("availableAgentsFiles")
|
|
2945
|
+
? cardAvailableAgentsFiles
|
|
2946
|
+
: [];
|
|
2938
2947
|
const workspaceContextInstruction = "For later open_workspace calls, context=\"auto\" avoids repeating unchanged bootstrap context; use context=\"none\" when only the workspace handle/metadata is needed, or context=\"full\" to force a refresh.";
|
|
2939
2948
|
const workspaceManagementInstruction = "Use open_workspace(action=\"list\") for lightweight Workspace inventory. Use action=\"inspect\" with one known workspaceId for bounded read-only metadata without opening/resuming it. Explicitly open a Workspace before executing or mutating against it, and ask the user before close_workspace cleanup.";
|
|
2940
2949
|
const cardInstruction = config.skillsEnabled
|
|
@@ -2945,7 +2954,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2945
2954
|
? [
|
|
2946
2955
|
`Workspace already exists as ${workspace.id} for this directory.`,
|
|
2947
2956
|
"Reuse this workspaceId for subsequent tool calls.",
|
|
2948
|
-
|
|
2957
|
+
`Project bootstrap context components included in this response: ${bootstrapContextComponents.join(", ")}. Components not listed are unchanged and are not repeated.`,
|
|
2949
2958
|
workspaceContextInstruction,
|
|
2950
2959
|
workspaceManagementInstruction,
|
|
2951
2960
|
].join("\n\n")
|
|
@@ -3012,41 +3021,44 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
3012
3021
|
success: true,
|
|
3013
3022
|
durationMs: Math.round(performance.now() - startedAt),
|
|
3014
3023
|
});
|
|
3024
|
+
const workspaceCard = {
|
|
3025
|
+
workspaceId: workspace.id,
|
|
3026
|
+
kind: "workspace",
|
|
3027
|
+
root: workspace.root,
|
|
3028
|
+
path: workspace.root,
|
|
3029
|
+
mode: workspace.mode,
|
|
3030
|
+
workspaceReused,
|
|
3031
|
+
includeBootstrapContext,
|
|
3032
|
+
sourceRoot: workspace.sourceRoot,
|
|
3033
|
+
worktree: workspace.worktree,
|
|
3034
|
+
worktrees: knownWorktrees,
|
|
3035
|
+
staleWorkspaces,
|
|
3036
|
+
capabilityFingerprint,
|
|
3037
|
+
contextFingerprint,
|
|
3038
|
+
capabilityCatalog,
|
|
3039
|
+
agentsFiles: cardAgentsFiles,
|
|
3040
|
+
availableAgentsFiles: cardAvailableAgentsFiles,
|
|
3041
|
+
skills: cardSkills,
|
|
3042
|
+
agentProviders: cardAgentProviders,
|
|
3043
|
+
agents: cardAgents,
|
|
3044
|
+
instruction: cardInstruction,
|
|
3045
|
+
summary: {
|
|
3046
|
+
mode: workspace.mode,
|
|
3047
|
+
agentsFiles: cardAgentsFiles.length,
|
|
3048
|
+
availableAgentsFiles: cardAvailableAgentsFiles.length,
|
|
3049
|
+
skills: cardSkills.length,
|
|
3050
|
+
capabilities: capabilityCatalog.length,
|
|
3051
|
+
agentProviders: cardAgentProviders.length,
|
|
3052
|
+
agents: cardAgents.length,
|
|
3053
|
+
},
|
|
3054
|
+
};
|
|
3015
3055
|
const response = hooks.decorateResult(workspace.id, attachHookReports({
|
|
3016
3056
|
content: resultContent,
|
|
3017
3057
|
_meta: {
|
|
3018
3058
|
tool: "open_workspace",
|
|
3019
|
-
card:
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
root: workspace.root,
|
|
3023
|
-
path: workspace.root,
|
|
3024
|
-
mode: workspace.mode,
|
|
3025
|
-
workspaceReused,
|
|
3026
|
-
includeBootstrapContext,
|
|
3027
|
-
sourceRoot: workspace.sourceRoot,
|
|
3028
|
-
worktree: workspace.worktree,
|
|
3029
|
-
worktrees: knownWorktrees,
|
|
3030
|
-
staleWorkspaces,
|
|
3031
|
-
capabilityFingerprint,
|
|
3032
|
-
contextFingerprint,
|
|
3033
|
-
capabilityCatalog,
|
|
3034
|
-
agentsFiles: cardAgentsFiles,
|
|
3035
|
-
availableAgentsFiles: cardAvailableAgentsFiles,
|
|
3036
|
-
skills: cardSkills,
|
|
3037
|
-
agentProviders: cardAgentProviders,
|
|
3038
|
-
agents: cardAgents,
|
|
3039
|
-
instruction: cardInstruction,
|
|
3040
|
-
summary: {
|
|
3041
|
-
mode: workspace.mode,
|
|
3042
|
-
agentsFiles: cardAgentsFiles.length,
|
|
3043
|
-
availableAgentsFiles: cardAvailableAgentsFiles.length,
|
|
3044
|
-
skills: cardSkills.length,
|
|
3045
|
-
capabilities: capabilityCatalog.length,
|
|
3046
|
-
agentProviders: cardAgentProviders.length,
|
|
3047
|
-
agents: cardAgents.length,
|
|
3048
|
-
},
|
|
3049
|
-
},
|
|
3059
|
+
card: includeBootstrapContext
|
|
3060
|
+
? workspaceCard
|
|
3061
|
+
: compactWorkspacePresentation(workspaceCard),
|
|
3050
3062
|
},
|
|
3051
3063
|
structuredContent: {
|
|
3052
3064
|
action: "open",
|
|
@@ -3061,16 +3073,19 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
3061
3073
|
capabilityFingerprint,
|
|
3062
3074
|
contextFingerprint,
|
|
3063
3075
|
capabilityCatalog,
|
|
3064
|
-
...(
|
|
3065
|
-
? {
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
}
|
|
3076
|
+
...(bootstrapComponents.has("capabilityGuides")
|
|
3077
|
+
? { capabilityGuides: visibleCapabilityGuides }
|
|
3078
|
+
: {}),
|
|
3079
|
+
...(bootstrapComponents.has("agentsFiles") ? { agentsFiles: loadedAgentsFiles } : {}),
|
|
3080
|
+
...(bootstrapComponents.has("availableAgentsFiles")
|
|
3081
|
+
? { availableAgentsFiles: availableAgentsFileOutputs }
|
|
3082
|
+
: {}),
|
|
3083
|
+
...(bootstrapComponents.has("skills") ? { skills: visibleSkills } : {}),
|
|
3084
|
+
...(bootstrapComponents.has("agentProfiles")
|
|
3085
|
+
? { agentProviders: visibleAgentProviders, agents: visibleAgents }
|
|
3086
|
+
: {}),
|
|
3087
|
+
...(bootstrapComponents.has("skillDiagnostics")
|
|
3088
|
+
? { skillDiagnostics: visibleSkillDiagnostics }
|
|
3074
3089
|
: {}),
|
|
3075
3090
|
instruction,
|
|
3076
3091
|
},
|
|
@@ -3098,13 +3113,17 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
3098
3113
|
}
|
|
3099
3114
|
return remoteWorkspaces.activitySnapshot(input, conversationScopeId);
|
|
3100
3115
|
},
|
|
3116
|
+
index: async (turnId, knownRevision, conversationScopeId) => {
|
|
3117
|
+
const composite = await compositeActivity.index(turnId, knownRevision);
|
|
3118
|
+
return composite ?? remoteWorkspaces.activityIndex(turnId, knownRevision, conversationScopeId);
|
|
3119
|
+
},
|
|
3101
3120
|
detail: async (turnId, activityId, conversationScopeId) => {
|
|
3102
3121
|
const composite = await compositeActivity.detail(turnId, activityId);
|
|
3103
3122
|
return composite ?? remoteWorkspaces.activityDetail(turnId, activityId, conversationScopeId);
|
|
3104
3123
|
},
|
|
3105
|
-
output: async (turnId, outputId, conversationScopeId) => {
|
|
3106
|
-
const composite = await compositeActivity.output(turnId, outputId);
|
|
3107
|
-
return composite ?? remoteWorkspaces.activityOutput(turnId, outputId, conversationScopeId);
|
|
3124
|
+
output: async (turnId, outputId, conversationScopeId, cursor) => {
|
|
3125
|
+
const composite = await compositeActivity.output(turnId, outputId, cursor);
|
|
3126
|
+
return composite ?? remoteWorkspaces.activityOutput(turnId, outputId, conversationScopeId, cursor);
|
|
3108
3127
|
},
|
|
3109
3128
|
});
|
|
3110
3129
|
registerAppTool(server, toolNames.capability, {
|
|
@@ -4082,7 +4101,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
4082
4101
|
.min(0)
|
|
4083
4102
|
.max(300_000)
|
|
4084
4103
|
.optional()
|
|
4085
|
-
.describe("
|
|
4104
|
+
.describe("Maximum feedback wait, not a minimum delay: if the process finishes sooner, the call returns immediately. For long-running commands or wait-only action=process calls, set a long window near the Host request deadline (60000ms when supported) instead of repeated short polling. For action=run, use 0 for immediate background handoff; otherwise defaults to 10000ms. For action=process, wait-only calls default to 5000ms and interaction to 250ms."),
|
|
4086
4105
|
timeoutMs: z
|
|
4087
4106
|
.number()
|
|
4088
4107
|
.int()
|
|
@@ -4232,6 +4251,21 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
4232
4251
|
writeStdinRemote: (workspaceId, input, conversationScopeId) => remoteWorkspaces.writeStdin(workspaceId, input, conversationScopeId),
|
|
4233
4252
|
hostScopeIdFor,
|
|
4234
4253
|
});
|
|
4254
|
+
if (ownsRemoteWorkspaces) {
|
|
4255
|
+
const closeServer = server.close.bind(server);
|
|
4256
|
+
let closePromise;
|
|
4257
|
+
server.close = () => {
|
|
4258
|
+
closePromise ??= (async () => {
|
|
4259
|
+
try {
|
|
4260
|
+
await closeServer();
|
|
4261
|
+
}
|
|
4262
|
+
finally {
|
|
4263
|
+
await remoteWorkspaces.shutdown();
|
|
4264
|
+
}
|
|
4265
|
+
})();
|
|
4266
|
+
return closePromise;
|
|
4267
|
+
};
|
|
4268
|
+
}
|
|
4235
4269
|
return server;
|
|
4236
4270
|
}
|
|
4237
4271
|
export function createServer(config = loadConfig(), options = {}) {
|
|
@@ -4488,6 +4522,7 @@ export function createServer(config = loadConfig(), options = {}) {
|
|
|
4488
4522
|
clearInterval(transportCleanupTimer);
|
|
4489
4523
|
const results = await transports.closeAll();
|
|
4490
4524
|
logTransportCloseResults("server_shutdown", results);
|
|
4525
|
+
await sharedRemoteWorkspaces.shutdown();
|
|
4491
4526
|
processSessions.shutdown();
|
|
4492
4527
|
await codeIntelligence.shutdown();
|
|
4493
4528
|
oauthProvider.close();
|
package/dist/skills.js
CHANGED
|
@@ -9,7 +9,7 @@ export function effectiveSkillPaths(config, cwd) {
|
|
|
9
9
|
const defaultPathCandidates = [
|
|
10
10
|
join(homedir(), ".agents", "skills"),
|
|
11
11
|
resolve(cwd, ".agents", "skills"),
|
|
12
|
-
config.
|
|
12
|
+
config.configSkillsDir,
|
|
13
13
|
join(config.agentDir, "skills"),
|
|
14
14
|
];
|
|
15
15
|
const defaultPaths = defaultPathCandidates.filter((path) => path !== undefined && existsSync(path));
|
|
@@ -16,8 +16,7 @@ export async function loadSubagentProfiles(config, workspaceRoot) {
|
|
|
16
16
|
if (!config.subagents)
|
|
17
17
|
return [];
|
|
18
18
|
const profileDirs = [
|
|
19
|
-
config.
|
|
20
|
-
join(workspaceRoot, ".devspace", "agents"),
|
|
19
|
+
config.configAgentsDir,
|
|
21
20
|
join(workspaceRoot, ".forgerelay", "agents"),
|
|
22
21
|
];
|
|
23
22
|
const profilesByName = new Map();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { removeForgeRelayNodeModulesBinFromPath } from "../path.js";
|
|
3
3
|
import { asRecord, assertPipedChild, errorMessage, readArray, readNestedString, requireFinalResponse, terminateChildOnAbort, unwrapProviderPayload, } from "../shared.js";
|
|
4
4
|
const PI_AGENT_TIMEOUT_MS = 120_000;
|
|
5
5
|
export class PiRpcSubagentAdapter {
|
|
@@ -69,7 +69,7 @@ export function piCommandEnvironment(env) {
|
|
|
69
69
|
return env;
|
|
70
70
|
return {
|
|
71
71
|
...env,
|
|
72
|
-
PATH:
|
|
72
|
+
PATH: removeForgeRelayNodeModulesBinFromPath(path),
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
class JsonLineRpc {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { delimiter, resolve } from "node:path";
|
|
3
3
|
import { subagentProviderContinuationSupported } from "./continuation.js";
|
|
4
|
-
import {
|
|
4
|
+
import { removeForgeRelayNodeModulesBinFromPath } from "./path.js";
|
|
5
5
|
import { SUBAGENT_PROVIDERS, } from "../profiles.js";
|
|
6
6
|
export function getSubagentProviderAvailabilitySnapshot(env = process.env) {
|
|
7
7
|
return SUBAGENT_PROVIDERS.map((provider) => checkSubagentProviderAvailability(provider, env));
|
|
@@ -128,6 +128,6 @@ function piAvailabilityEnvironment(env) {
|
|
|
128
128
|
return env;
|
|
129
129
|
return {
|
|
130
130
|
...env,
|
|
131
|
-
PATH:
|
|
131
|
+
PATH: removeForgeRelayNodeModulesBinFromPath(path),
|
|
132
132
|
};
|
|
133
133
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { delimiter, resolve, sep } from "node:path";
|
|
3
|
-
export function
|
|
3
|
+
export function removeForgeRelayNodeModulesBinFromPath(pathValue) {
|
|
4
4
|
return pathValue
|
|
5
5
|
.split(delimiter)
|
|
6
|
-
.filter((entry) => entry && !
|
|
6
|
+
.filter((entry) => entry && !isForgeRelayNodeModulesBin(entry))
|
|
7
7
|
.join(delimiter);
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function isForgeRelayNodeModulesBin(pathEntry) {
|
|
10
10
|
const resolvedEntry = resolve(pathEntry);
|
|
11
11
|
if (!resolvedEntry.endsWith(`${sep}node_modules${sep}.bin`)) {
|
|
12
12
|
return false;
|
|
@@ -17,7 +17,7 @@ function isDevspaceNodeModulesBin(pathEntry) {
|
|
|
17
17
|
try {
|
|
18
18
|
const packageInfo = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
19
19
|
const packageName = typeof packageInfo.name === "string" ? packageInfo.name : "";
|
|
20
|
-
return
|
|
20
|
+
return packageName === "@akira-tl/forgerelay";
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
23
23
|
return false;
|
|
@@ -2123,12 +2123,12 @@
|
|
|
2123
2123
|
"_chunk-EyZ2wyi3.js"
|
|
2124
2124
|
]
|
|
2125
2125
|
},
|
|
2126
|
-
"_scrollbar-
|
|
2127
|
-
"file": "assets/scrollbar-
|
|
2126
|
+
"_scrollbar-CbhpdW05.js": {
|
|
2127
|
+
"file": "assets/scrollbar-CbhpdW05.js",
|
|
2128
2128
|
"name": "scrollbar",
|
|
2129
2129
|
"imports": [
|
|
2130
2130
|
"_chunk-EyZ2wyi3.js",
|
|
2131
|
-
"_workspace-app-
|
|
2131
|
+
"_workspace-app-CxwJuZyS.js"
|
|
2132
2132
|
],
|
|
2133
2133
|
"dynamicImports": [
|
|
2134
2134
|
"../../node_modules/@shikijs/langs/dist/abap.mjs",
|
|
@@ -2474,33 +2474,33 @@
|
|
|
2474
2474
|
"_chunk-EyZ2wyi3.js"
|
|
2475
2475
|
]
|
|
2476
2476
|
},
|
|
2477
|
-
"_workspace-app-
|
|
2478
|
-
"file": "assets/workspace-app-
|
|
2477
|
+
"_workspace-app-CxwJuZyS.js": {
|
|
2478
|
+
"file": "assets/workspace-app-CxwJuZyS.js",
|
|
2479
2479
|
"name": "workspace-app",
|
|
2480
2480
|
"imports": [
|
|
2481
|
-
"
|
|
2481
|
+
"_chunk-EyZ2wyi3.js"
|
|
2482
2482
|
],
|
|
2483
2483
|
"dynamicImports": [
|
|
2484
|
-
"
|
|
2485
|
-
|
|
2484
|
+
"_workspace-app-CxwJuZyS.js"
|
|
2485
|
+
],
|
|
2486
|
+
"css": [
|
|
2487
|
+
"assets/workspace-app-ldjBmCJR.css"
|
|
2486
2488
|
]
|
|
2487
2489
|
},
|
|
2488
|
-
"_workspace-app-
|
|
2489
|
-
"file": "assets/workspace-app-
|
|
2490
|
+
"_workspace-app-D6UR0AFl.js": {
|
|
2491
|
+
"file": "assets/workspace-app-D6UR0AFl.js",
|
|
2490
2492
|
"name": "workspace-app",
|
|
2491
2493
|
"imports": [
|
|
2492
|
-
"
|
|
2494
|
+
"_workspace-app-CxwJuZyS.js"
|
|
2493
2495
|
],
|
|
2494
2496
|
"dynamicImports": [
|
|
2495
|
-
"
|
|
2496
|
-
|
|
2497
|
-
"css": [
|
|
2498
|
-
"assets/workspace-app-YnUST8IP.css"
|
|
2497
|
+
"heavy-payload.tsx",
|
|
2498
|
+
"review-payload.tsx"
|
|
2499
2499
|
]
|
|
2500
2500
|
},
|
|
2501
|
-
"_workspace-app-
|
|
2502
|
-
"file": "assets/workspace-app-
|
|
2503
|
-
"src": "_workspace-app-
|
|
2501
|
+
"_workspace-app-ldjBmCJR.css": {
|
|
2502
|
+
"file": "assets/workspace-app-ldjBmCJR.css",
|
|
2503
|
+
"src": "_workspace-app-ldjBmCJR.css"
|
|
2504
2504
|
},
|
|
2505
2505
|
"_xml-JnmX6vyS.js": {
|
|
2506
2506
|
"file": "assets/xml-JnmX6vyS.js",
|
|
@@ -2518,52 +2518,52 @@
|
|
|
2518
2518
|
]
|
|
2519
2519
|
},
|
|
2520
2520
|
"activity-panel-app.html": {
|
|
2521
|
-
"file": "assets/activity-panel-app-
|
|
2521
|
+
"file": "assets/activity-panel-app-CUAN6zyW.js",
|
|
2522
2522
|
"name": "activity-panel-app",
|
|
2523
2523
|
"src": "activity-panel-app.html",
|
|
2524
2524
|
"isEntry": true,
|
|
2525
2525
|
"imports": [
|
|
2526
|
-
"_workspace-app-
|
|
2526
|
+
"_workspace-app-CxwJuZyS.js"
|
|
2527
2527
|
]
|
|
2528
2528
|
},
|
|
2529
2529
|
"heavy-payload.tsx": {
|
|
2530
|
-
"file": "assets/heavy-payload-
|
|
2530
|
+
"file": "assets/heavy-payload-CgzrutLm.js",
|
|
2531
2531
|
"name": "heavy-payload",
|
|
2532
2532
|
"src": "heavy-payload.tsx",
|
|
2533
2533
|
"isDynamicEntry": true,
|
|
2534
2534
|
"imports": [
|
|
2535
|
-
"_scrollbar-
|
|
2536
|
-
"_workspace-app-
|
|
2535
|
+
"_scrollbar-CbhpdW05.js",
|
|
2536
|
+
"_workspace-app-D6UR0AFl.js"
|
|
2537
2537
|
]
|
|
2538
2538
|
},
|
|
2539
2539
|
"review-payload.tsx": {
|
|
2540
|
-
"file": "assets/review-payload-
|
|
2540
|
+
"file": "assets/review-payload-BrLbezbq.js",
|
|
2541
2541
|
"name": "review-payload",
|
|
2542
2542
|
"src": "review-payload.tsx",
|
|
2543
2543
|
"isDynamicEntry": true,
|
|
2544
2544
|
"imports": [
|
|
2545
|
-
"_scrollbar-
|
|
2546
|
-
"_workspace-app-
|
|
2545
|
+
"_scrollbar-CbhpdW05.js",
|
|
2546
|
+
"_workspace-app-D6UR0AFl.js"
|
|
2547
2547
|
]
|
|
2548
2548
|
},
|
|
2549
2549
|
"workspace-app.html": {
|
|
2550
|
-
"file": "assets/workspace-app-
|
|
2550
|
+
"file": "assets/workspace-app-Bhj96tsR.js",
|
|
2551
2551
|
"name": "workspace-app",
|
|
2552
2552
|
"src": "workspace-app.html",
|
|
2553
2553
|
"isEntry": true,
|
|
2554
2554
|
"imports": [
|
|
2555
|
-
"_workspace-app-
|
|
2556
|
-
"_workspace-app-
|
|
2555
|
+
"_workspace-app-CxwJuZyS.js",
|
|
2556
|
+
"_workspace-app-D6UR0AFl.js"
|
|
2557
2557
|
]
|
|
2558
2558
|
},
|
|
2559
2559
|
"workspace-lifecycle-app.html": {
|
|
2560
|
-
"file": "assets/workspace-lifecycle-app-
|
|
2560
|
+
"file": "assets/workspace-lifecycle-app-Cqfhx9pV.js",
|
|
2561
2561
|
"name": "workspace-lifecycle-app",
|
|
2562
2562
|
"src": "workspace-lifecycle-app.html",
|
|
2563
2563
|
"isEntry": true,
|
|
2564
2564
|
"imports": [
|
|
2565
|
-
"_workspace-app-
|
|
2566
|
-
"_workspace-app-
|
|
2565
|
+
"_workspace-app-CxwJuZyS.js",
|
|
2566
|
+
"_workspace-app-D6UR0AFl.js"
|
|
2567
2567
|
]
|
|
2568
2568
|
}
|
|
2569
2569
|
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>ForgeRelay Panel</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/activity-panel-app-
|
|
7
|
+
<script type="module" crossorigin src="./assets/activity-panel-app-CUAN6zyW.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-EyZ2wyi3.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="./assets/workspace-app-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="./assets/workspace-app-
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/workspace-app-CxwJuZyS.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="./assets/workspace-app-ldjBmCJR.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<main id="app" class="shell">
|