nextclaw 0.16.32 → 0.16.33
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/dist/cli/index.js +963 -270
- package/package.json +11 -11
- package/ui-dist/assets/{ChannelsList-Zeys_w43.js → ChannelsList-DVDu1xvz.js} +1 -1
- package/ui-dist/assets/ChatPage-Z9tRzm_n.js +43 -0
- package/ui-dist/assets/{MarketplacePage-Cd4faegU.js → MarketplacePage-Buo9HrOz.js} +1 -1
- package/ui-dist/assets/MarketplacePage-D6rVQEQR.js +1 -0
- package/ui-dist/assets/{McpMarketplacePage-C09Ngs7O.js → McpMarketplacePage-JnkYwK7p.js} +1 -1
- package/ui-dist/assets/{ModelConfig-DJgdcgvQ.js → ModelConfig-BYRhgp0c.js} +1 -1
- package/ui-dist/assets/{ProvidersList-w0rVFIBf.js → ProvidersList-DmLyyHvX.js} +1 -1
- package/ui-dist/assets/{RemoteAccessPage-BJ_ckkOV.js → RemoteAccessPage-CDSSvH7Z.js} +1 -1
- package/ui-dist/assets/{RuntimeConfig-Cmn2xPQO.js → RuntimeConfig-v7a7Fe3x.js} +1 -1
- package/ui-dist/assets/{SearchConfig-BT13qpR_.js → SearchConfig-D5f1EkLE.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-CvqEVn0B.js → SecretsConfig-D61IKcYt.js} +1 -1
- package/ui-dist/assets/{SessionsConfig-DHHcYznk.js → SessionsConfig-BRIxVTEv.js} +2 -2
- package/ui-dist/assets/chat-session-display-D0WpnuRZ.js +1 -0
- package/ui-dist/assets/{index-C6d0xmtm.js → index-BuwbBgmT.js} +2 -2
- package/ui-dist/assets/index-bZ8cqQIS.css +1 -0
- package/ui-dist/assets/{security-config-T5zpg16O.js → security-config-DbUyWcQz.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-Bs5Ll17m.js → useConfirmDialog-COwYXDKm.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/ChatPage-DWOU_8P6.js +0 -43
- package/ui-dist/assets/MarketplacePage-BfaTTqN6.js +0 -1
- package/ui-dist/assets/chat-session-display-VW6ZMvZP.js +0 -1
- package/ui-dist/assets/index-BlH4-cBw.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -698,9 +698,7 @@ function collectFiles(rootDir) {
|
|
|
698
698
|
}
|
|
699
699
|
function installBuiltinSkill(workdir, destinationDir, skillName) {
|
|
700
700
|
const loader = new SkillsLoader(workdir);
|
|
701
|
-
const workspaceSkill = loader.listSkills(false).find((skill) =>
|
|
702
|
-
return skill.name === skillName && skill.source === "workspace";
|
|
703
|
-
});
|
|
701
|
+
const workspaceSkill = loader.listSkills(false).find((skill) => skill.name === skillName && skill.source === "workspace");
|
|
704
702
|
if (!workspaceSkill) {
|
|
705
703
|
throw new Error(`Workspace skill not found in local installation: ${skillName}`);
|
|
706
704
|
}
|
|
@@ -5642,14 +5640,230 @@ import {
|
|
|
5642
5640
|
SessionsHistoryTool,
|
|
5643
5641
|
SessionsListTool,
|
|
5644
5642
|
SessionsSendTool,
|
|
5645
|
-
SpawnTool,
|
|
5646
|
-
SubagentManager,
|
|
5647
|
-
SubagentsTool,
|
|
5648
5643
|
ToolRegistry,
|
|
5649
5644
|
WebFetchTool,
|
|
5650
5645
|
WebSearchTool,
|
|
5651
5646
|
WriteFileTool
|
|
5652
5647
|
} from "@nextclaw/core";
|
|
5648
|
+
|
|
5649
|
+
// src/cli/commands/ncp/session-request/session-request.tool.ts
|
|
5650
|
+
import { Tool } from "@nextclaw/core";
|
|
5651
|
+
function readRequiredString(params, key) {
|
|
5652
|
+
const value = params[key];
|
|
5653
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
5654
|
+
throw new Error(`${key} must be a non-empty string.`);
|
|
5655
|
+
}
|
|
5656
|
+
return value.trim();
|
|
5657
|
+
}
|
|
5658
|
+
function readOptionalString2(params, key) {
|
|
5659
|
+
const value = params[key];
|
|
5660
|
+
if (typeof value !== "string") {
|
|
5661
|
+
return void 0;
|
|
5662
|
+
}
|
|
5663
|
+
const trimmed = value.trim();
|
|
5664
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
5665
|
+
}
|
|
5666
|
+
var SpawnChildSessionTool = class extends Tool {
|
|
5667
|
+
constructor(broker) {
|
|
5668
|
+
super();
|
|
5669
|
+
this.broker = broker;
|
|
5670
|
+
}
|
|
5671
|
+
sourceSessionId = "";
|
|
5672
|
+
sourceSessionMetadata = {};
|
|
5673
|
+
agentId;
|
|
5674
|
+
handoffDepth = 0;
|
|
5675
|
+
get name() {
|
|
5676
|
+
return "spawn";
|
|
5677
|
+
}
|
|
5678
|
+
get description() {
|
|
5679
|
+
return "Create a child session, delegate a task to it, and continue this session when it finishes.";
|
|
5680
|
+
}
|
|
5681
|
+
get parameters() {
|
|
5682
|
+
return {
|
|
5683
|
+
type: "object",
|
|
5684
|
+
properties: {
|
|
5685
|
+
task: { type: "string", description: "Task to run inside the child session." },
|
|
5686
|
+
label: { type: "string", description: "Optional child session title." },
|
|
5687
|
+
model: { type: "string", description: "Optional model override for the child session." }
|
|
5688
|
+
},
|
|
5689
|
+
required: ["task"]
|
|
5690
|
+
};
|
|
5691
|
+
}
|
|
5692
|
+
setContext = (params) => {
|
|
5693
|
+
this.sourceSessionId = params.sourceSessionId;
|
|
5694
|
+
this.sourceSessionMetadata = structuredClone(params.sourceSessionMetadata);
|
|
5695
|
+
this.agentId = params.agentId;
|
|
5696
|
+
this.handoffDepth = params.handoffDepth ?? 0;
|
|
5697
|
+
};
|
|
5698
|
+
execute = async (params, toolCallId) => {
|
|
5699
|
+
const task = readRequiredString(params, "task");
|
|
5700
|
+
return this.broker.spawnChildSessionAndRequest({
|
|
5701
|
+
sourceSessionId: this.sourceSessionId,
|
|
5702
|
+
sourceToolCallId: toolCallId,
|
|
5703
|
+
sourceSessionMetadata: this.sourceSessionMetadata,
|
|
5704
|
+
task,
|
|
5705
|
+
title: readOptionalString2(params, "label"),
|
|
5706
|
+
model: readOptionalString2(params, "model"),
|
|
5707
|
+
handoffDepth: this.handoffDepth,
|
|
5708
|
+
agentId: this.agentId
|
|
5709
|
+
});
|
|
5710
|
+
};
|
|
5711
|
+
};
|
|
5712
|
+
var SessionRequestTool = class extends Tool {
|
|
5713
|
+
constructor(broker) {
|
|
5714
|
+
super();
|
|
5715
|
+
this.broker = broker;
|
|
5716
|
+
}
|
|
5717
|
+
sourceSessionId = "";
|
|
5718
|
+
handoffDepth = 0;
|
|
5719
|
+
get name() {
|
|
5720
|
+
return "sessions_request";
|
|
5721
|
+
}
|
|
5722
|
+
get description() {
|
|
5723
|
+
return "Send one task to another session. Use this after sessions_spawn or to reuse an existing session, and optionally resume this session when the target final reply is ready.";
|
|
5724
|
+
}
|
|
5725
|
+
get parameters() {
|
|
5726
|
+
return {
|
|
5727
|
+
type: "object",
|
|
5728
|
+
properties: {
|
|
5729
|
+
target: {
|
|
5730
|
+
type: "object",
|
|
5731
|
+
description: 'Target session reference. Pass an object like {"session_id":"..."}, not a bare string.',
|
|
5732
|
+
properties: {
|
|
5733
|
+
session_id: {
|
|
5734
|
+
type: "string",
|
|
5735
|
+
description: "Existing target session id."
|
|
5736
|
+
}
|
|
5737
|
+
},
|
|
5738
|
+
required: ["session_id"]
|
|
5739
|
+
},
|
|
5740
|
+
task: {
|
|
5741
|
+
type: "string",
|
|
5742
|
+
description: "Task to send to the target session."
|
|
5743
|
+
},
|
|
5744
|
+
await: {
|
|
5745
|
+
type: "string",
|
|
5746
|
+
enum: ["final_reply"],
|
|
5747
|
+
description: "Phase 1 requires waiting for the target final reply."
|
|
5748
|
+
},
|
|
5749
|
+
delivery: {
|
|
5750
|
+
type: "string",
|
|
5751
|
+
enum: ["none", "resume_source"],
|
|
5752
|
+
description: "How the completion should be delivered back to the source session."
|
|
5753
|
+
},
|
|
5754
|
+
title: {
|
|
5755
|
+
type: "string",
|
|
5756
|
+
description: "Optional card title override."
|
|
5757
|
+
}
|
|
5758
|
+
},
|
|
5759
|
+
required: ["target", "task", "await", "delivery"]
|
|
5760
|
+
};
|
|
5761
|
+
}
|
|
5762
|
+
setContext = (params) => {
|
|
5763
|
+
this.sourceSessionId = params.sourceSessionId;
|
|
5764
|
+
this.handoffDepth = params.handoffDepth ?? 0;
|
|
5765
|
+
};
|
|
5766
|
+
execute = async (params, toolCallId) => {
|
|
5767
|
+
const target = params.target;
|
|
5768
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
5769
|
+
throw new Error("target must be an object.");
|
|
5770
|
+
}
|
|
5771
|
+
const task = readRequiredString(params, "task");
|
|
5772
|
+
const awaitMode = readRequiredString(params, "await");
|
|
5773
|
+
const deliveryMode = readRequiredString(params, "delivery");
|
|
5774
|
+
if (awaitMode !== "final_reply") {
|
|
5775
|
+
throw new Error('Phase 1 only supports await="final_reply".');
|
|
5776
|
+
}
|
|
5777
|
+
return this.broker.requestSession({
|
|
5778
|
+
sourceSessionId: this.sourceSessionId,
|
|
5779
|
+
sourceToolCallId: toolCallId,
|
|
5780
|
+
targetSessionId: readRequiredString(target, "session_id"),
|
|
5781
|
+
task,
|
|
5782
|
+
title: readOptionalString2(params, "title"),
|
|
5783
|
+
awaitMode: "final_reply",
|
|
5784
|
+
deliveryMode,
|
|
5785
|
+
handoffDepth: this.handoffDepth
|
|
5786
|
+
});
|
|
5787
|
+
};
|
|
5788
|
+
};
|
|
5789
|
+
|
|
5790
|
+
// src/cli/commands/ncp/session-request/session-spawn.tool.ts
|
|
5791
|
+
import { Tool as Tool2 } from "@nextclaw/core";
|
|
5792
|
+
function readRequiredString2(value, key) {
|
|
5793
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
5794
|
+
throw new Error(`${key} must be a non-empty string.`);
|
|
5795
|
+
}
|
|
5796
|
+
return value.trim();
|
|
5797
|
+
}
|
|
5798
|
+
function readOptionalString3(value) {
|
|
5799
|
+
if (typeof value !== "string") {
|
|
5800
|
+
return void 0;
|
|
5801
|
+
}
|
|
5802
|
+
const trimmed = value.trim();
|
|
5803
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
5804
|
+
}
|
|
5805
|
+
var SessionSpawnTool = class extends Tool2 {
|
|
5806
|
+
constructor(sessionCreationService) {
|
|
5807
|
+
super();
|
|
5808
|
+
this.sessionCreationService = sessionCreationService;
|
|
5809
|
+
}
|
|
5810
|
+
sourceSessionId = "";
|
|
5811
|
+
sourceSessionMetadata = {};
|
|
5812
|
+
agentId;
|
|
5813
|
+
get name() {
|
|
5814
|
+
return "sessions_spawn";
|
|
5815
|
+
}
|
|
5816
|
+
get description() {
|
|
5817
|
+
return "Create a standalone session. Usually follow this with sessions_request if you want the new session to start working immediately.";
|
|
5818
|
+
}
|
|
5819
|
+
get parameters() {
|
|
5820
|
+
return {
|
|
5821
|
+
type: "object",
|
|
5822
|
+
properties: {
|
|
5823
|
+
task: {
|
|
5824
|
+
type: "string",
|
|
5825
|
+
description: "Seed text used to title the new session."
|
|
5826
|
+
},
|
|
5827
|
+
title: {
|
|
5828
|
+
type: "string",
|
|
5829
|
+
description: "Optional explicit session title."
|
|
5830
|
+
},
|
|
5831
|
+
model: {
|
|
5832
|
+
type: "string",
|
|
5833
|
+
description: "Optional model override for the new session."
|
|
5834
|
+
}
|
|
5835
|
+
},
|
|
5836
|
+
required: ["task"]
|
|
5837
|
+
};
|
|
5838
|
+
}
|
|
5839
|
+
setContext = (params) => {
|
|
5840
|
+
this.sourceSessionId = params.sourceSessionId;
|
|
5841
|
+
this.sourceSessionMetadata = structuredClone(params.sourceSessionMetadata);
|
|
5842
|
+
this.agentId = params.agentId;
|
|
5843
|
+
};
|
|
5844
|
+
execute = async (params) => {
|
|
5845
|
+
const task = readRequiredString2(params.task, "task");
|
|
5846
|
+
const session = this.sessionCreationService.createSession({
|
|
5847
|
+
task,
|
|
5848
|
+
title: readOptionalString3(params.title),
|
|
5849
|
+
sourceSessionMetadata: this.sourceSessionMetadata,
|
|
5850
|
+
agentId: this.agentId,
|
|
5851
|
+
model: readOptionalString3(params.model)
|
|
5852
|
+
});
|
|
5853
|
+
return {
|
|
5854
|
+
kind: "nextclaw.session",
|
|
5855
|
+
sessionId: session.sessionId,
|
|
5856
|
+
...session.parentSessionId ? { parentSessionId: session.parentSessionId } : {},
|
|
5857
|
+
isChildSession: false,
|
|
5858
|
+
lifecycle: session.lifecycle,
|
|
5859
|
+
title: session.title,
|
|
5860
|
+
sessionType: session.sessionType,
|
|
5861
|
+
createdAt: session.createdAt
|
|
5862
|
+
};
|
|
5863
|
+
};
|
|
5864
|
+
};
|
|
5865
|
+
|
|
5866
|
+
// src/cli/commands/ncp/nextclaw-ncp-tool-registry.ts
|
|
5653
5867
|
function toToolParams(args) {
|
|
5654
5868
|
if (isRecord3(args)) {
|
|
5655
5869
|
return args;
|
|
@@ -5699,46 +5913,11 @@ var CoreToolNcpAdapter = class {
|
|
|
5699
5913
|
var NextclawNcpToolRegistry = class {
|
|
5700
5914
|
constructor(options) {
|
|
5701
5915
|
this.options = options;
|
|
5702
|
-
const initialConfig = this.options.getConfig();
|
|
5703
|
-
this.subagents = new SubagentManager({
|
|
5704
|
-
providerManager: this.options.providerManager,
|
|
5705
|
-
workspace: initialConfig.agents.defaults.workspace,
|
|
5706
|
-
bus: this.options.bus,
|
|
5707
|
-
model: initialConfig.agents.defaults.model,
|
|
5708
|
-
contextTokens: initialConfig.agents.defaults.contextTokens,
|
|
5709
|
-
searchConfig: initialConfig.search,
|
|
5710
|
-
execConfig: initialConfig.tools.exec,
|
|
5711
|
-
restrictToWorkspace: initialConfig.tools.restrictToWorkspace,
|
|
5712
|
-
completionSink: async (params) => {
|
|
5713
|
-
const sessionId = params.origin.sessionKey?.trim();
|
|
5714
|
-
if (!sessionId || !this.options.writeSubagentCompletionToSession) {
|
|
5715
|
-
return;
|
|
5716
|
-
}
|
|
5717
|
-
await this.options.writeSubagentCompletionToSession({
|
|
5718
|
-
sessionId,
|
|
5719
|
-
runId: params.runId,
|
|
5720
|
-
toolCallId: params.origin.toolCallId,
|
|
5721
|
-
label: params.label,
|
|
5722
|
-
task: params.task,
|
|
5723
|
-
result: params.result,
|
|
5724
|
-
status: params.status
|
|
5725
|
-
});
|
|
5726
|
-
}
|
|
5727
|
-
});
|
|
5728
5916
|
}
|
|
5729
|
-
subagents;
|
|
5730
5917
|
registry = new ToolRegistry();
|
|
5731
5918
|
tools = /* @__PURE__ */ new Map();
|
|
5732
5919
|
currentExtensionToolContext = {};
|
|
5733
|
-
prepareForRun(context) {
|
|
5734
|
-
this.subagents.updateRuntimeOptions({
|
|
5735
|
-
model: context.model,
|
|
5736
|
-
maxTokens: context.maxTokens,
|
|
5737
|
-
contextTokens: context.contextTokens,
|
|
5738
|
-
searchConfig: context.searchConfig,
|
|
5739
|
-
execConfig: { timeout: context.execTimeoutSeconds },
|
|
5740
|
-
restrictToWorkspace: context.restrictToWorkspace
|
|
5741
|
-
});
|
|
5920
|
+
prepareForRun = (context) => {
|
|
5742
5921
|
this.currentExtensionToolContext = {
|
|
5743
5922
|
config: context.config,
|
|
5744
5923
|
workspaceDir: context.workspace,
|
|
@@ -5752,30 +5931,30 @@ var NextclawNcpToolRegistry = class {
|
|
|
5752
5931
|
this.registerDefaultTools(context);
|
|
5753
5932
|
this.registerExtensionTools(context);
|
|
5754
5933
|
this.registerAdditionalTools(context);
|
|
5755
|
-
}
|
|
5756
|
-
listTools() {
|
|
5934
|
+
};
|
|
5935
|
+
listTools = () => {
|
|
5757
5936
|
return [...this.tools.values()].filter((tool) => this.isToolAvailable(tool.name));
|
|
5758
|
-
}
|
|
5759
|
-
getTool(name) {
|
|
5937
|
+
};
|
|
5938
|
+
getTool = (name) => {
|
|
5760
5939
|
if (!this.isToolAvailable(name)) {
|
|
5761
5940
|
return void 0;
|
|
5762
5941
|
}
|
|
5763
5942
|
return this.tools.get(name);
|
|
5764
|
-
}
|
|
5765
|
-
getToolDefinitions() {
|
|
5943
|
+
};
|
|
5944
|
+
getToolDefinitions = () => {
|
|
5766
5945
|
return this.listTools().map((tool) => ({
|
|
5767
5946
|
name: tool.name,
|
|
5768
5947
|
description: tool.description,
|
|
5769
5948
|
parameters: tool.parameters
|
|
5770
5949
|
}));
|
|
5771
|
-
}
|
|
5772
|
-
async
|
|
5950
|
+
};
|
|
5951
|
+
execute = async (toolCallId, toolName, args) => {
|
|
5773
5952
|
if (this.registry.has(toolName)) {
|
|
5774
5953
|
return this.registry.executeRaw(toolName, toToolParams(args), toolCallId);
|
|
5775
5954
|
}
|
|
5776
5955
|
return this.tools.get(toolName)?.execute(args);
|
|
5777
|
-
}
|
|
5778
|
-
registerDefaultTools(context) {
|
|
5956
|
+
};
|
|
5957
|
+
registerDefaultTools = (context) => {
|
|
5779
5958
|
const allowedDir = context.restrictToWorkspace ? context.workspace : void 0;
|
|
5780
5959
|
this.registerTool(new ReadFileTool(allowedDir));
|
|
5781
5960
|
this.registerTool(new WriteFileTool(allowedDir));
|
|
@@ -5795,15 +5974,31 @@ var NextclawNcpToolRegistry = class {
|
|
|
5795
5974
|
this.registerTool(new WebSearchTool(context.searchConfig));
|
|
5796
5975
|
this.registerTool(new WebFetchTool());
|
|
5797
5976
|
this.registerMessagingTools(context);
|
|
5798
|
-
const spawnTool = new
|
|
5799
|
-
spawnTool.setContext(
|
|
5800
|
-
context.
|
|
5801
|
-
context.
|
|
5802
|
-
context.
|
|
5803
|
-
context.
|
|
5804
|
-
|
|
5805
|
-
);
|
|
5977
|
+
const spawnTool = new SpawnChildSessionTool(this.options.sessionRequestBroker);
|
|
5978
|
+
spawnTool.setContext({
|
|
5979
|
+
sourceSessionId: context.sessionId,
|
|
5980
|
+
sourceSessionMetadata: context.metadata,
|
|
5981
|
+
agentId: context.agentId,
|
|
5982
|
+
handoffDepth: context.handoffDepth
|
|
5983
|
+
});
|
|
5806
5984
|
this.registerTool(spawnTool);
|
|
5985
|
+
const sessionsSpawnTool = new SessionSpawnTool(
|
|
5986
|
+
this.options.sessionCreationService
|
|
5987
|
+
);
|
|
5988
|
+
sessionsSpawnTool.setContext({
|
|
5989
|
+
sourceSessionId: context.sessionId,
|
|
5990
|
+
sourceSessionMetadata: context.metadata,
|
|
5991
|
+
agentId: context.agentId
|
|
5992
|
+
});
|
|
5993
|
+
this.registerTool(sessionsSpawnTool);
|
|
5994
|
+
const sessionsRequestTool = new SessionRequestTool(
|
|
5995
|
+
this.options.sessionRequestBroker
|
|
5996
|
+
);
|
|
5997
|
+
sessionsRequestTool.setContext({
|
|
5998
|
+
sourceSessionId: context.sessionId,
|
|
5999
|
+
handoffDepth: context.handoffDepth
|
|
6000
|
+
});
|
|
6001
|
+
this.registerTool(sessionsRequestTool);
|
|
5807
6002
|
this.registerTool(new SessionsListTool(this.options.sessionManager));
|
|
5808
6003
|
this.registerTool(new SessionsHistoryTool(this.options.sessionManager));
|
|
5809
6004
|
const sessionsSendTool = new SessionsSendTool(this.options.sessionManager, this.options.bus);
|
|
@@ -5818,12 +6013,11 @@ var NextclawNcpToolRegistry = class {
|
|
|
5818
6013
|
this.registerTool(sessionsSendTool);
|
|
5819
6014
|
this.registerTool(new MemorySearchTool(context.workspace));
|
|
5820
6015
|
this.registerTool(new MemoryGetTool(context.workspace));
|
|
5821
|
-
this.registerTool(new SubagentsTool(this.subagents));
|
|
5822
6016
|
const gatewayTool = new GatewayTool(this.options.gatewayController);
|
|
5823
6017
|
gatewayTool.setContext({ sessionKey: context.sessionId });
|
|
5824
6018
|
this.registerTool(gatewayTool);
|
|
5825
|
-
}
|
|
5826
|
-
registerMessagingTools(context) {
|
|
6019
|
+
};
|
|
6020
|
+
registerMessagingTools = (context) => {
|
|
5827
6021
|
const accountId = readMetadataAccountId(context.metadata, {});
|
|
5828
6022
|
const messageTool = new MessageTool((message) => this.options.bus.publishOutbound(message));
|
|
5829
6023
|
messageTool.setContext(context.channel, context.chatId, accountId ?? null);
|
|
@@ -5833,8 +6027,8 @@ var NextclawNcpToolRegistry = class {
|
|
|
5833
6027
|
cronTool.setContext(context.channel, context.chatId, accountId ?? null);
|
|
5834
6028
|
this.registerTool(cronTool);
|
|
5835
6029
|
}
|
|
5836
|
-
}
|
|
5837
|
-
registerExtensionTools(context) {
|
|
6030
|
+
};
|
|
6031
|
+
registerExtensionTools = (context) => {
|
|
5838
6032
|
const extensionRegistry = this.options.getExtensionRegistry?.();
|
|
5839
6033
|
if (!extensionRegistry || extensionRegistry.tools.length === 0) {
|
|
5840
6034
|
return;
|
|
@@ -5858,15 +6052,15 @@ var NextclawNcpToolRegistry = class {
|
|
|
5858
6052
|
);
|
|
5859
6053
|
}
|
|
5860
6054
|
}
|
|
5861
|
-
}
|
|
5862
|
-
registerTool(tool) {
|
|
6055
|
+
};
|
|
6056
|
+
registerTool = (tool) => {
|
|
5863
6057
|
this.registry.register(tool);
|
|
5864
6058
|
this.tools.set(
|
|
5865
6059
|
tool.name,
|
|
5866
6060
|
new CoreToolNcpAdapter(tool, async (toolName, args) => this.registry.execute(toolName, toToolParams(args)))
|
|
5867
6061
|
);
|
|
5868
|
-
}
|
|
5869
|
-
registerAdditionalTools(context) {
|
|
6062
|
+
};
|
|
6063
|
+
registerAdditionalTools = (context) => {
|
|
5870
6064
|
const tools = this.options.getAdditionalTools?.(context) ?? [];
|
|
5871
6065
|
for (const tool of tools) {
|
|
5872
6066
|
if (this.tools.has(tool.name)) {
|
|
@@ -5874,11 +6068,11 @@ var NextclawNcpToolRegistry = class {
|
|
|
5874
6068
|
}
|
|
5875
6069
|
this.tools.set(tool.name, tool);
|
|
5876
6070
|
}
|
|
5877
|
-
}
|
|
5878
|
-
isToolAvailable(name) {
|
|
6071
|
+
};
|
|
6072
|
+
isToolAvailable = (name) => {
|
|
5879
6073
|
const coreTool = this.registry.get(name);
|
|
5880
6074
|
return coreTool ? coreTool.isAvailable() : true;
|
|
5881
|
-
}
|
|
6075
|
+
};
|
|
5882
6076
|
};
|
|
5883
6077
|
function resolveAgentHandoffDepth(metadata) {
|
|
5884
6078
|
const rawDepth = Number(metadata.agent_handoff_depth ?? 0);
|
|
@@ -5986,6 +6180,19 @@ function prependRequestedSkills(content, requestedSkillNames) {
|
|
|
5986
6180
|
|
|
5987
6181
|
${content}`;
|
|
5988
6182
|
}
|
|
6183
|
+
function buildSessionOrchestrationSection() {
|
|
6184
|
+
return [
|
|
6185
|
+
"## Session Orchestration",
|
|
6186
|
+
"- `spawn` creates a child session for delegated sub-work that should report completion back into the current session.",
|
|
6187
|
+
"- Use `spawn` when the work is a subtask of the current flow and the user expects this session to continue after that child finishes.",
|
|
6188
|
+
"- `sessions_spawn` creates a standalone session. Use it when the work should live in its own thread, remain independently reviewable later, or continue outside the current flow.",
|
|
6189
|
+
"- `sessions_request` sends one task to another session. Use it to reuse an existing session, or immediately after `sessions_spawn` when a new standalone session should start working right away.",
|
|
6190
|
+
"- If the goal is 'open a new session and have it do something now', the usual sequence is: 1) call `sessions_spawn`; 2) call `sessions_request` with that returned `sessionId`.",
|
|
6191
|
+
'- `sessions_request.target` must be an object shaped like `{ "session_id": "<target-session-id>" }`. Do not pass a bare string.',
|
|
6192
|
+
'- Prefer `delivery="resume_source"` when the current session should continue after the target session produces its final reply. Use `delivery="none"` when you only want the target session to run independently.',
|
|
6193
|
+
"- Do not use `spawn` for long-lived independent threads when `sessions_spawn` plus `sessions_request` would match the user's intent better."
|
|
6194
|
+
].join("\n");
|
|
6195
|
+
}
|
|
5989
6196
|
function filterTools(toolDefinitions, requestedToolNames) {
|
|
5990
6197
|
if (toolDefinitions.length === 0) {
|
|
5991
6198
|
return void 0;
|
|
@@ -6076,6 +6283,7 @@ var NextclawNcpContextBuilder = class {
|
|
|
6076
6283
|
accountId: accountId ?? null
|
|
6077
6284
|
});
|
|
6078
6285
|
const toolDefinitions = this.options.toolRegistry.getToolDefinitions();
|
|
6286
|
+
const additionalSystemSections = [buildSessionOrchestrationSection()];
|
|
6079
6287
|
const contextBuilder = new ContextBuilder(
|
|
6080
6288
|
effectiveWorkspace,
|
|
6081
6289
|
config2.agents.context,
|
|
@@ -6097,7 +6305,8 @@ var NextclawNcpContextBuilder = class {
|
|
|
6097
6305
|
thinkingLevel: runtimeThinking,
|
|
6098
6306
|
skillNames: requestedSkillNames,
|
|
6099
6307
|
messageToolHints,
|
|
6100
|
-
availableTools: buildToolCatalogEntries(toolDefinitions)
|
|
6308
|
+
availableTools: buildToolCatalogEntries(toolDefinitions),
|
|
6309
|
+
additionalSystemSections
|
|
6101
6310
|
});
|
|
6102
6311
|
messages[messages.length - 1] = {
|
|
6103
6312
|
role: currentTurn.currentRole,
|
|
@@ -6455,102 +6664,6 @@ var NextclawAgentSessionStore = class {
|
|
|
6455
6664
|
};
|
|
6456
6665
|
};
|
|
6457
6666
|
|
|
6458
|
-
// src/cli/commands/ncp/ncp-subagent-completion-message.ts
|
|
6459
|
-
import {
|
|
6460
|
-
NCP_INTERNAL_VISIBILITY_METADATA_KEY
|
|
6461
|
-
} from "@nextclaw/ncp";
|
|
6462
|
-
function escapeXml(value) {
|
|
6463
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6464
|
-
}
|
|
6465
|
-
function buildSubagentCompletionFollowUpMessage(params) {
|
|
6466
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6467
|
-
const statusLabel = params.status === "ok" ? "completed" : "failed";
|
|
6468
|
-
return {
|
|
6469
|
-
id: `${params.sessionId}:system:subagent-follow-up:${timestamp}`,
|
|
6470
|
-
sessionId: params.sessionId,
|
|
6471
|
-
role: "user",
|
|
6472
|
-
status: "final",
|
|
6473
|
-
timestamp,
|
|
6474
|
-
parts: [
|
|
6475
|
-
{
|
|
6476
|
-
type: "text",
|
|
6477
|
-
text: [
|
|
6478
|
-
"<task-notification>",
|
|
6479
|
-
"<source>subagent_completion</source>",
|
|
6480
|
-
`<label>${escapeXml(params.label)}</label>`,
|
|
6481
|
-
`<status>${statusLabel}</status>`,
|
|
6482
|
-
`<delegated-task>${escapeXml(params.task)}</delegated-task>`,
|
|
6483
|
-
`<result>${escapeXml(params.result)}</result>`,
|
|
6484
|
-
"<instructions>This is an internal worker completion notification, not a new end-user message. Continue the parent task using this result. If the user's request is complete, answer directly. If more work is needed, continue reasoning and use tools. Do not mention this hidden notification unless the user explicitly asks about internal behavior.</instructions>",
|
|
6485
|
-
"</task-notification>"
|
|
6486
|
-
].join("\n")
|
|
6487
|
-
}
|
|
6488
|
-
],
|
|
6489
|
-
metadata: {
|
|
6490
|
-
[NCP_INTERNAL_VISIBILITY_METADATA_KEY]: "hidden",
|
|
6491
|
-
system_event_kind: "subagent_completion_follow_up",
|
|
6492
|
-
subagent_label: params.label,
|
|
6493
|
-
subagent_status: params.status,
|
|
6494
|
-
subagent_task: params.task
|
|
6495
|
-
}
|
|
6496
|
-
};
|
|
6497
|
-
}
|
|
6498
|
-
|
|
6499
|
-
// src/cli/commands/ncp/ncp-subagent-completion-follow-up.ts
|
|
6500
|
-
async function consumeAgentRun(events) {
|
|
6501
|
-
for await (const _event of events) {
|
|
6502
|
-
void _event;
|
|
6503
|
-
}
|
|
6504
|
-
}
|
|
6505
|
-
async function sleep(ms) {
|
|
6506
|
-
await new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
6507
|
-
}
|
|
6508
|
-
async function waitForSessionToBecomeIdle(params) {
|
|
6509
|
-
const timeoutMs = params.timeoutMs ?? 15e3;
|
|
6510
|
-
const intervalMs = params.intervalMs ?? 150;
|
|
6511
|
-
const deadline = Date.now() + timeoutMs;
|
|
6512
|
-
while (Date.now() <= deadline) {
|
|
6513
|
-
const summary = await params.backend.getSession(params.sessionId);
|
|
6514
|
-
if (!summary) {
|
|
6515
|
-
return false;
|
|
6516
|
-
}
|
|
6517
|
-
if (summary.status !== "running") {
|
|
6518
|
-
return true;
|
|
6519
|
-
}
|
|
6520
|
-
await sleep(intervalMs);
|
|
6521
|
-
}
|
|
6522
|
-
return false;
|
|
6523
|
-
}
|
|
6524
|
-
async function persistSubagentCompletionAndResumeParent(params) {
|
|
6525
|
-
const isIdle = await waitForSessionToBecomeIdle({
|
|
6526
|
-
backend: params.backend,
|
|
6527
|
-
sessionId: params.completion.sessionId
|
|
6528
|
-
});
|
|
6529
|
-
if (!isIdle) {
|
|
6530
|
-
return;
|
|
6531
|
-
}
|
|
6532
|
-
if (params.completion.toolCallId?.trim()) {
|
|
6533
|
-
await params.backend.updateToolCallResult(
|
|
6534
|
-
params.completion.sessionId,
|
|
6535
|
-
params.completion.toolCallId.trim(),
|
|
6536
|
-
{
|
|
6537
|
-
kind: "nextclaw.subagent_run",
|
|
6538
|
-
runId: params.completion.runId,
|
|
6539
|
-
label: params.completion.label,
|
|
6540
|
-
task: params.completion.task,
|
|
6541
|
-
status: params.completion.status === "ok" ? "completed" : "failed",
|
|
6542
|
-
result: params.completion.result
|
|
6543
|
-
}
|
|
6544
|
-
);
|
|
6545
|
-
}
|
|
6546
|
-
await consumeAgentRun(
|
|
6547
|
-
params.backend.send({
|
|
6548
|
-
sessionId: params.completion.sessionId,
|
|
6549
|
-
message: buildSubagentCompletionFollowUpMessage(params.completion)
|
|
6550
|
-
})
|
|
6551
|
-
);
|
|
6552
|
-
}
|
|
6553
|
-
|
|
6554
6667
|
// src/cli/commands/ncp/provider-manager-ncp-llm-api.ts
|
|
6555
6668
|
import { parseThinkingLevel as parseThinkingLevel3 } from "@nextclaw/core";
|
|
6556
6669
|
function normalizeModel(value) {
|
|
@@ -6668,6 +6781,569 @@ var ProviderManagerNcpLLMApi = class {
|
|
|
6668
6781
|
}
|
|
6669
6782
|
};
|
|
6670
6783
|
|
|
6784
|
+
// src/cli/commands/ncp/session-request/session-creation.service.ts
|
|
6785
|
+
import { randomUUID } from "crypto";
|
|
6786
|
+
var DEFAULT_SESSION_TYPE = "native";
|
|
6787
|
+
var DEFAULT_LIFECYCLE = "persistent";
|
|
6788
|
+
var SESSION_METADATA_LABEL_KEY = "label";
|
|
6789
|
+
var CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
|
|
6790
|
+
var CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
|
|
6791
|
+
var CHILD_SESSION_LIFECYCLE_METADATA_KEY = "session_lifecycle";
|
|
6792
|
+
var CHILD_SESSION_PROMOTED_METADATA_KEY = "child_session_promoted";
|
|
6793
|
+
function readOptionalString4(value) {
|
|
6794
|
+
if (typeof value !== "string") {
|
|
6795
|
+
return null;
|
|
6796
|
+
}
|
|
6797
|
+
const trimmed = value.trim();
|
|
6798
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
6799
|
+
}
|
|
6800
|
+
function summarizeTask(task) {
|
|
6801
|
+
const normalized = task.trim().replace(/\s+/g, " ");
|
|
6802
|
+
if (!normalized) {
|
|
6803
|
+
return "Session";
|
|
6804
|
+
}
|
|
6805
|
+
if (normalized.length <= 72) {
|
|
6806
|
+
return normalized;
|
|
6807
|
+
}
|
|
6808
|
+
return `${normalized.slice(0, 69)}...`;
|
|
6809
|
+
}
|
|
6810
|
+
function cloneInheritedMetadata(sourceMetadata) {
|
|
6811
|
+
const nextMetadata = {};
|
|
6812
|
+
const inheritedKeys = [
|
|
6813
|
+
"session_type",
|
|
6814
|
+
"preferred_model",
|
|
6815
|
+
"preferred_thinking",
|
|
6816
|
+
"project_root",
|
|
6817
|
+
"requested_skill_refs",
|
|
6818
|
+
"codex_runtime_backend",
|
|
6819
|
+
"reasoningNormalizationMode",
|
|
6820
|
+
"reasoning_normalization_mode"
|
|
6821
|
+
];
|
|
6822
|
+
for (const key of inheritedKeys) {
|
|
6823
|
+
if (!Object.prototype.hasOwnProperty.call(sourceMetadata, key)) {
|
|
6824
|
+
continue;
|
|
6825
|
+
}
|
|
6826
|
+
nextMetadata[key] = structuredClone(sourceMetadata[key]);
|
|
6827
|
+
}
|
|
6828
|
+
return nextMetadata;
|
|
6829
|
+
}
|
|
6830
|
+
function buildSessionId(agentId) {
|
|
6831
|
+
void agentId;
|
|
6832
|
+
return `ncp-${Date.now().toString(36)}-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
|
|
6833
|
+
}
|
|
6834
|
+
var SessionCreationService = class {
|
|
6835
|
+
constructor(sessionManager, onSessionUpdated) {
|
|
6836
|
+
this.sessionManager = sessionManager;
|
|
6837
|
+
this.onSessionUpdated = onSessionUpdated;
|
|
6838
|
+
}
|
|
6839
|
+
createSession = (params) => {
|
|
6840
|
+
const sessionId = buildSessionId(params.agentId);
|
|
6841
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6842
|
+
const session = this.sessionManager.getOrCreate(sessionId);
|
|
6843
|
+
const title = readOptionalString4(params.title) ?? summarizeTask(params.task);
|
|
6844
|
+
const metadata = cloneInheritedMetadata(params.sourceSessionMetadata);
|
|
6845
|
+
const parentSessionId = readOptionalString4(params.parentSessionId);
|
|
6846
|
+
const requestId = readOptionalString4(params.requestId);
|
|
6847
|
+
const sessionType = readOptionalString4(params.sessionType) ?? readOptionalString4(metadata.session_type) ?? DEFAULT_SESSION_TYPE;
|
|
6848
|
+
metadata.session_type = sessionType;
|
|
6849
|
+
metadata[SESSION_METADATA_LABEL_KEY] = title;
|
|
6850
|
+
metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = DEFAULT_LIFECYCLE;
|
|
6851
|
+
if (parentSessionId) {
|
|
6852
|
+
metadata[CHILD_SESSION_PARENT_METADATA_KEY] = parentSessionId;
|
|
6853
|
+
metadata[CHILD_SESSION_PROMOTED_METADATA_KEY] = false;
|
|
6854
|
+
}
|
|
6855
|
+
if (requestId) {
|
|
6856
|
+
metadata[CHILD_SESSION_REQUEST_METADATA_KEY] = requestId;
|
|
6857
|
+
}
|
|
6858
|
+
if (readOptionalString4(params.model)) {
|
|
6859
|
+
metadata.model = params.model?.trim();
|
|
6860
|
+
metadata.preferred_model = params.model?.trim();
|
|
6861
|
+
}
|
|
6862
|
+
if (readOptionalString4(params.thinkingLevel)) {
|
|
6863
|
+
metadata.thinking = params.thinkingLevel?.trim();
|
|
6864
|
+
metadata.preferred_thinking = params.thinkingLevel?.trim();
|
|
6865
|
+
}
|
|
6866
|
+
if (readOptionalString4(params.projectRoot)) {
|
|
6867
|
+
metadata.project_root = params.projectRoot?.trim();
|
|
6868
|
+
}
|
|
6869
|
+
session.metadata = metadata;
|
|
6870
|
+
session.updatedAt = new Date(now3);
|
|
6871
|
+
this.sessionManager.save(session);
|
|
6872
|
+
this.onSessionUpdated?.(sessionId);
|
|
6873
|
+
return {
|
|
6874
|
+
sessionId,
|
|
6875
|
+
sessionType,
|
|
6876
|
+
runtimeFamily: "native",
|
|
6877
|
+
...parentSessionId ? { parentSessionId } : {},
|
|
6878
|
+
...requestId ? { spawnedByRequestId: requestId } : {},
|
|
6879
|
+
lifecycle: DEFAULT_LIFECYCLE,
|
|
6880
|
+
title,
|
|
6881
|
+
metadata,
|
|
6882
|
+
createdAt: session.createdAt.toISOString(),
|
|
6883
|
+
updatedAt: session.updatedAt.toISOString()
|
|
6884
|
+
};
|
|
6885
|
+
};
|
|
6886
|
+
createChildSession = (params) => {
|
|
6887
|
+
return this.createSession(params);
|
|
6888
|
+
};
|
|
6889
|
+
promoteChildSession = (params) => {
|
|
6890
|
+
const session = this.sessionManager.getIfExists(params.sessionId);
|
|
6891
|
+
if (!session) {
|
|
6892
|
+
return false;
|
|
6893
|
+
}
|
|
6894
|
+
session.metadata = {
|
|
6895
|
+
...session.metadata,
|
|
6896
|
+
[CHILD_SESSION_PROMOTED_METADATA_KEY]: params.promoted
|
|
6897
|
+
};
|
|
6898
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
6899
|
+
this.sessionManager.save(session);
|
|
6900
|
+
this.onSessionUpdated?.(params.sessionId);
|
|
6901
|
+
return true;
|
|
6902
|
+
};
|
|
6903
|
+
isChildSessionRecord = (metadata) => {
|
|
6904
|
+
return Boolean(readOptionalString4(metadata?.[CHILD_SESSION_PARENT_METADATA_KEY]));
|
|
6905
|
+
};
|
|
6906
|
+
};
|
|
6907
|
+
|
|
6908
|
+
// src/cli/commands/ncp/session-request/session-request-broker.ts
|
|
6909
|
+
import {
|
|
6910
|
+
NcpEventType
|
|
6911
|
+
} from "@nextclaw/ncp";
|
|
6912
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6913
|
+
function readOptionalString5(value) {
|
|
6914
|
+
if (typeof value !== "string") {
|
|
6915
|
+
return null;
|
|
6916
|
+
}
|
|
6917
|
+
const trimmed = value.trim();
|
|
6918
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
6919
|
+
}
|
|
6920
|
+
function summarizeTask2(task) {
|
|
6921
|
+
const normalized = task.trim().replace(/\s+/g, " ");
|
|
6922
|
+
if (!normalized) {
|
|
6923
|
+
return "Session request";
|
|
6924
|
+
}
|
|
6925
|
+
if (normalized.length <= 72) {
|
|
6926
|
+
return normalized;
|
|
6927
|
+
}
|
|
6928
|
+
return `${normalized.slice(0, 69)}...`;
|
|
6929
|
+
}
|
|
6930
|
+
function buildUserMessage(params) {
|
|
6931
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6932
|
+
return {
|
|
6933
|
+
id: `${params.sessionId}:user:session-request:${params.requestId}`,
|
|
6934
|
+
sessionId: params.sessionId,
|
|
6935
|
+
role: "user",
|
|
6936
|
+
status: "final",
|
|
6937
|
+
timestamp,
|
|
6938
|
+
parts: [{ type: "text", text: params.task }],
|
|
6939
|
+
metadata: {
|
|
6940
|
+
session_request_id: params.requestId
|
|
6941
|
+
}
|
|
6942
|
+
};
|
|
6943
|
+
}
|
|
6944
|
+
function extractMessageText(message) {
|
|
6945
|
+
if (!message) {
|
|
6946
|
+
return void 0;
|
|
6947
|
+
}
|
|
6948
|
+
const parts = message.parts.flatMap((part) => {
|
|
6949
|
+
if (part.type === "text" || part.type === "rich-text") {
|
|
6950
|
+
return [part.text];
|
|
6951
|
+
}
|
|
6952
|
+
return [];
|
|
6953
|
+
}).map((part) => part.trim()).filter((part) => part.length > 0);
|
|
6954
|
+
if (parts.length === 0) {
|
|
6955
|
+
return void 0;
|
|
6956
|
+
}
|
|
6957
|
+
return parts.join("\n\n");
|
|
6958
|
+
}
|
|
6959
|
+
function findLatestAssistantMessage(messages) {
|
|
6960
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
6961
|
+
const message = messages[index];
|
|
6962
|
+
if (message?.role === "assistant") {
|
|
6963
|
+
return message;
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
return void 0;
|
|
6967
|
+
}
|
|
6968
|
+
function readParentSessionId(metadata) {
|
|
6969
|
+
return readOptionalString5(metadata?.[CHILD_SESSION_PARENT_METADATA_KEY]) ?? void 0;
|
|
6970
|
+
}
|
|
6971
|
+
function buildToolResult(params) {
|
|
6972
|
+
return {
|
|
6973
|
+
kind: "nextclaw.session_request",
|
|
6974
|
+
requestId: params.request.requestId,
|
|
6975
|
+
sessionId: params.request.targetSessionId,
|
|
6976
|
+
targetKind: params.isChildSession ? "child" : "session",
|
|
6977
|
+
...params.parentSessionId ? { parentSessionId: params.parentSessionId } : {},
|
|
6978
|
+
...params.spawnedByRequestId ? { spawnedByRequestId: params.spawnedByRequestId } : {},
|
|
6979
|
+
isChildSession: params.isChildSession,
|
|
6980
|
+
lifecycle: "persistent",
|
|
6981
|
+
...params.title.trim() ? { title: params.title } : {},
|
|
6982
|
+
task: params.task,
|
|
6983
|
+
status: params.request.status,
|
|
6984
|
+
awaitMode: params.request.awaitMode,
|
|
6985
|
+
deliveryMode: params.request.deliveryMode,
|
|
6986
|
+
...params.request.finalResponseText ? { finalResponseText: params.request.finalResponseText } : {},
|
|
6987
|
+
...params.request.error ? { error: params.request.error } : {},
|
|
6988
|
+
...params.message ? { message: params.message } : {}
|
|
6989
|
+
};
|
|
6990
|
+
}
|
|
6991
|
+
var SessionRequestBroker = class {
|
|
6992
|
+
constructor(sessionManager, sessionCreationService, deliveryService, resolveBackend, onSessionUpdated) {
|
|
6993
|
+
this.sessionManager = sessionManager;
|
|
6994
|
+
this.sessionCreationService = sessionCreationService;
|
|
6995
|
+
this.deliveryService = deliveryService;
|
|
6996
|
+
this.resolveBackend = resolveBackend;
|
|
6997
|
+
this.onSessionUpdated = onSessionUpdated;
|
|
6998
|
+
}
|
|
6999
|
+
spawnChildSessionAndRequest = async (params) => {
|
|
7000
|
+
const requestId = randomUUID2();
|
|
7001
|
+
const childSession = this.sessionCreationService.createChildSession({
|
|
7002
|
+
parentSessionId: params.sourceSessionId,
|
|
7003
|
+
task: params.task,
|
|
7004
|
+
title: params.title,
|
|
7005
|
+
sourceSessionMetadata: params.sourceSessionMetadata,
|
|
7006
|
+
agentId: params.agentId,
|
|
7007
|
+
model: params.model,
|
|
7008
|
+
thinkingLevel: params.thinkingLevel,
|
|
7009
|
+
sessionType: params.sessionType,
|
|
7010
|
+
projectRoot: params.projectRoot,
|
|
7011
|
+
requestId
|
|
7012
|
+
});
|
|
7013
|
+
return this.dispatchRequest({
|
|
7014
|
+
requestId,
|
|
7015
|
+
sourceSessionId: params.sourceSessionId,
|
|
7016
|
+
sourceToolCallId: params.sourceToolCallId,
|
|
7017
|
+
targetSessionId: childSession.sessionId,
|
|
7018
|
+
task: params.task,
|
|
7019
|
+
title: childSession.title ?? summarizeTask2(params.task),
|
|
7020
|
+
handoffDepth: params.handoffDepth ?? 0,
|
|
7021
|
+
awaitMode: "final_reply",
|
|
7022
|
+
deliveryMode: "resume_source",
|
|
7023
|
+
isChildSession: true,
|
|
7024
|
+
parentSessionId: params.sourceSessionId,
|
|
7025
|
+
spawnedByRequestId: requestId
|
|
7026
|
+
});
|
|
7027
|
+
};
|
|
7028
|
+
requestSession = async (params) => {
|
|
7029
|
+
if (params.targetSessionId.trim() === params.sourceSessionId.trim()) {
|
|
7030
|
+
throw new Error("sessions_request cannot target the current session.");
|
|
7031
|
+
}
|
|
7032
|
+
const backend = this.resolveBackend();
|
|
7033
|
+
if (!backend) {
|
|
7034
|
+
throw new Error("NCP backend is not ready for session requests.");
|
|
7035
|
+
}
|
|
7036
|
+
const targetSummary = await backend.getSession(params.targetSessionId.trim());
|
|
7037
|
+
if (!targetSummary) {
|
|
7038
|
+
throw new Error(`Target session not found: ${params.targetSessionId}`);
|
|
7039
|
+
}
|
|
7040
|
+
const parentSessionId = readParentSessionId(targetSummary.metadata);
|
|
7041
|
+
return this.dispatchRequest({
|
|
7042
|
+
requestId: randomUUID2(),
|
|
7043
|
+
sourceSessionId: params.sourceSessionId,
|
|
7044
|
+
sourceToolCallId: params.sourceToolCallId,
|
|
7045
|
+
targetSessionId: params.targetSessionId.trim(),
|
|
7046
|
+
task: params.task,
|
|
7047
|
+
title: readOptionalString5(params.title) ?? readOptionalString5(targetSummary.metadata?.label) ?? summarizeTask2(params.task),
|
|
7048
|
+
handoffDepth: params.handoffDepth ?? 0,
|
|
7049
|
+
awaitMode: params.awaitMode,
|
|
7050
|
+
deliveryMode: params.deliveryMode,
|
|
7051
|
+
isChildSession: Boolean(parentSessionId),
|
|
7052
|
+
parentSessionId: parentSessionId ?? void 0,
|
|
7053
|
+
spawnedByRequestId: void 0
|
|
7054
|
+
});
|
|
7055
|
+
};
|
|
7056
|
+
dispatchRequest = async (params) => {
|
|
7057
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7058
|
+
const request = {
|
|
7059
|
+
requestId: params.requestId,
|
|
7060
|
+
sourceSessionId: params.sourceSessionId,
|
|
7061
|
+
targetSessionId: params.targetSessionId,
|
|
7062
|
+
sourceToolCallId: params.sourceToolCallId,
|
|
7063
|
+
rootRequestId: params.requestId,
|
|
7064
|
+
handoffDepth: params.handoffDepth,
|
|
7065
|
+
awaitMode: params.awaitMode,
|
|
7066
|
+
deliveryMode: params.deliveryMode,
|
|
7067
|
+
status: "running",
|
|
7068
|
+
createdAt,
|
|
7069
|
+
startedAt: createdAt,
|
|
7070
|
+
metadata: {
|
|
7071
|
+
title: params.title,
|
|
7072
|
+
task: params.task,
|
|
7073
|
+
is_child_session: params.isChildSession,
|
|
7074
|
+
...params.parentSessionId ? { parent_session_id: params.parentSessionId } : {}
|
|
7075
|
+
}
|
|
7076
|
+
};
|
|
7077
|
+
void this.runRequest({
|
|
7078
|
+
request,
|
|
7079
|
+
task: params.task,
|
|
7080
|
+
title: params.title,
|
|
7081
|
+
isChildSession: params.isChildSession,
|
|
7082
|
+
parentSessionId: params.parentSessionId
|
|
7083
|
+
}).catch((error) => {
|
|
7084
|
+
console.error(
|
|
7085
|
+
`[session-request] Background request ${params.requestId} crashed: ${error instanceof Error ? error.message : String(error)}`
|
|
7086
|
+
);
|
|
7087
|
+
});
|
|
7088
|
+
return buildToolResult({
|
|
7089
|
+
request,
|
|
7090
|
+
task: params.task,
|
|
7091
|
+
title: params.title,
|
|
7092
|
+
isChildSession: params.isChildSession,
|
|
7093
|
+
parentSessionId: params.parentSessionId,
|
|
7094
|
+
spawnedByRequestId: params.spawnedByRequestId,
|
|
7095
|
+
message: `Session request started. You'll receive the final reply when it finishes.`
|
|
7096
|
+
});
|
|
7097
|
+
};
|
|
7098
|
+
runRequest = async (params) => {
|
|
7099
|
+
let completedMessage;
|
|
7100
|
+
try {
|
|
7101
|
+
const backend = this.resolveBackend();
|
|
7102
|
+
if (!backend) {
|
|
7103
|
+
throw new Error("NCP backend is not ready for session request execution.");
|
|
7104
|
+
}
|
|
7105
|
+
const message = buildUserMessage({
|
|
7106
|
+
sessionId: params.request.targetSessionId,
|
|
7107
|
+
requestId: params.request.requestId,
|
|
7108
|
+
task: params.task
|
|
7109
|
+
});
|
|
7110
|
+
for await (const event of backend.send({
|
|
7111
|
+
sessionId: params.request.targetSessionId,
|
|
7112
|
+
message
|
|
7113
|
+
})) {
|
|
7114
|
+
if (event.type === NcpEventType.MessageAccepted) {
|
|
7115
|
+
this.handleRequestEvent(params.request, event);
|
|
7116
|
+
continue;
|
|
7117
|
+
}
|
|
7118
|
+
if (event.type === NcpEventType.MessageFailed) {
|
|
7119
|
+
throw new Error(event.payload.error.message);
|
|
7120
|
+
}
|
|
7121
|
+
if (event.type === NcpEventType.RunError) {
|
|
7122
|
+
throw new Error(event.payload.error ?? "Session request failed.");
|
|
7123
|
+
}
|
|
7124
|
+
if (event.type === NcpEventType.MessageCompleted) {
|
|
7125
|
+
completedMessage = event.payload.message;
|
|
7126
|
+
}
|
|
7127
|
+
}
|
|
7128
|
+
if (!completedMessage) {
|
|
7129
|
+
const targetMessages = await backend.listSessionMessages(
|
|
7130
|
+
params.request.targetSessionId
|
|
7131
|
+
);
|
|
7132
|
+
completedMessage = findLatestAssistantMessage(targetMessages);
|
|
7133
|
+
}
|
|
7134
|
+
if (!completedMessage) {
|
|
7135
|
+
throw new Error("Session request completed without a final reply.");
|
|
7136
|
+
}
|
|
7137
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7138
|
+
const finalResponseText = extractMessageText(completedMessage);
|
|
7139
|
+
const completedRequest = {
|
|
7140
|
+
...params.request,
|
|
7141
|
+
status: "completed",
|
|
7142
|
+
completedAt,
|
|
7143
|
+
finalResponseMessageId: completedMessage?.id,
|
|
7144
|
+
finalResponseText
|
|
7145
|
+
};
|
|
7146
|
+
this.appendRequestEvent(params.request.sourceSessionId, "session.request.completed", completedRequest);
|
|
7147
|
+
this.appendRequestEvent(params.request.targetSessionId, "session.request.completed", completedRequest);
|
|
7148
|
+
const result = buildToolResult({
|
|
7149
|
+
request: completedRequest,
|
|
7150
|
+
task: params.task,
|
|
7151
|
+
title: params.title,
|
|
7152
|
+
isChildSession: params.isChildSession,
|
|
7153
|
+
parentSessionId: params.parentSessionId,
|
|
7154
|
+
spawnedByRequestId: params.spawnedByRequestId
|
|
7155
|
+
});
|
|
7156
|
+
await this.deliveryService.publishToolResult({
|
|
7157
|
+
request: completedRequest,
|
|
7158
|
+
result
|
|
7159
|
+
});
|
|
7160
|
+
if (completedRequest.deliveryMode === "resume_source") {
|
|
7161
|
+
await this.deliveryService.resumeSourceSession({
|
|
7162
|
+
request: completedRequest,
|
|
7163
|
+
result
|
|
7164
|
+
});
|
|
7165
|
+
}
|
|
7166
|
+
} catch (error) {
|
|
7167
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7168
|
+
const failedRequest = {
|
|
7169
|
+
...params.request,
|
|
7170
|
+
status: "failed",
|
|
7171
|
+
completedAt,
|
|
7172
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7173
|
+
};
|
|
7174
|
+
this.appendRequestEvent(params.request.sourceSessionId, "session.request.failed", failedRequest);
|
|
7175
|
+
this.appendRequestEvent(params.request.targetSessionId, "session.request.failed", failedRequest);
|
|
7176
|
+
const result = buildToolResult({
|
|
7177
|
+
request: failedRequest,
|
|
7178
|
+
task: params.task,
|
|
7179
|
+
title: params.title,
|
|
7180
|
+
isChildSession: params.isChildSession,
|
|
7181
|
+
parentSessionId: params.parentSessionId,
|
|
7182
|
+
spawnedByRequestId: params.spawnedByRequestId
|
|
7183
|
+
});
|
|
7184
|
+
await this.deliveryService.publishToolResult({
|
|
7185
|
+
request: failedRequest,
|
|
7186
|
+
result
|
|
7187
|
+
});
|
|
7188
|
+
if (failedRequest.deliveryMode === "resume_source") {
|
|
7189
|
+
await this.deliveryService.resumeSourceSession({
|
|
7190
|
+
request: failedRequest,
|
|
7191
|
+
result
|
|
7192
|
+
});
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
};
|
|
7196
|
+
handleRequestEvent = (request, event) => {
|
|
7197
|
+
if (event.type === NcpEventType.MessageAccepted) {
|
|
7198
|
+
const acceptedRequest = {
|
|
7199
|
+
...request,
|
|
7200
|
+
targetMessageId: event.payload.messageId
|
|
7201
|
+
};
|
|
7202
|
+
this.appendRequestEvent(request.sourceSessionId, "session.request.accepted", acceptedRequest);
|
|
7203
|
+
this.appendRequestEvent(request.targetSessionId, "session.request.accepted", acceptedRequest);
|
|
7204
|
+
}
|
|
7205
|
+
};
|
|
7206
|
+
appendRequestEvent = (sessionId, type, request) => {
|
|
7207
|
+
const session = this.sessionManager.getOrCreate(sessionId);
|
|
7208
|
+
this.sessionManager.appendEvent(session, {
|
|
7209
|
+
type,
|
|
7210
|
+
data: {
|
|
7211
|
+
request: structuredClone(request)
|
|
7212
|
+
}
|
|
7213
|
+
});
|
|
7214
|
+
this.sessionManager.save(session);
|
|
7215
|
+
this.onSessionUpdated?.(sessionId);
|
|
7216
|
+
};
|
|
7217
|
+
};
|
|
7218
|
+
|
|
7219
|
+
// src/cli/commands/ncp/session-request/session-request-delivery.service.ts
|
|
7220
|
+
import {
|
|
7221
|
+
NCP_INTERNAL_VISIBILITY_METADATA_KEY
|
|
7222
|
+
} from "@nextclaw/ncp";
|
|
7223
|
+
function escapeXml(value) {
|
|
7224
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
7225
|
+
}
|
|
7226
|
+
async function consumeAgentRun(events) {
|
|
7227
|
+
for await (const _event of events) {
|
|
7228
|
+
void _event;
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
function runDetached(task, label) {
|
|
7232
|
+
void task.catch((error) => {
|
|
7233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7234
|
+
console.warn(`[session-request] ${label} failed: ${message}`);
|
|
7235
|
+
});
|
|
7236
|
+
}
|
|
7237
|
+
function scheduleDetached(taskFactory, label) {
|
|
7238
|
+
setTimeout(() => {
|
|
7239
|
+
runDetached(taskFactory(), label);
|
|
7240
|
+
}, 0);
|
|
7241
|
+
}
|
|
7242
|
+
async function sleep(ms) {
|
|
7243
|
+
await new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
7244
|
+
}
|
|
7245
|
+
async function waitForSessionToBecomeIdle(params) {
|
|
7246
|
+
const timeoutMs = params.timeoutMs ?? 15e3;
|
|
7247
|
+
const intervalMs = params.intervalMs ?? 150;
|
|
7248
|
+
const deadline = Date.now() + timeoutMs;
|
|
7249
|
+
while (Date.now() <= deadline) {
|
|
7250
|
+
const summary = await params.backend.getSession(params.sessionId);
|
|
7251
|
+
if (!summary) {
|
|
7252
|
+
return false;
|
|
7253
|
+
}
|
|
7254
|
+
if (summary.status !== "running") {
|
|
7255
|
+
return true;
|
|
7256
|
+
}
|
|
7257
|
+
await sleep(intervalMs);
|
|
7258
|
+
}
|
|
7259
|
+
return false;
|
|
7260
|
+
}
|
|
7261
|
+
function buildSessionRequestCompletionMessage(params) {
|
|
7262
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7263
|
+
const status = params.result.status === "completed" ? "completed" : "failed";
|
|
7264
|
+
const responseText = params.result.finalResponseText ?? params.result.error ?? "";
|
|
7265
|
+
const targetKind = params.result.targetKind;
|
|
7266
|
+
const title = params.result.title ?? params.result.task;
|
|
7267
|
+
return {
|
|
7268
|
+
id: `${params.request.sourceSessionId}:system:session-request:${params.request.requestId}:${timestamp}`,
|
|
7269
|
+
sessionId: params.request.sourceSessionId,
|
|
7270
|
+
role: "user",
|
|
7271
|
+
status: "final",
|
|
7272
|
+
timestamp,
|
|
7273
|
+
parts: [
|
|
7274
|
+
{
|
|
7275
|
+
type: "text",
|
|
7276
|
+
text: [
|
|
7277
|
+
"<session-request-completion>",
|
|
7278
|
+
`<request-id>${escapeXml(params.request.requestId)}</request-id>`,
|
|
7279
|
+
`<target-session-id>${escapeXml(params.request.targetSessionId)}</target-session-id>`,
|
|
7280
|
+
`<target-kind>${escapeXml(targetKind)}</target-kind>`,
|
|
7281
|
+
`<title>${escapeXml(title)}</title>`,
|
|
7282
|
+
`<status>${escapeXml(status)}</status>`,
|
|
7283
|
+
`<final-response>${escapeXml(responseText)}</final-response>`,
|
|
7284
|
+
"<instructions>This is an internal delegated session completion notification. Continue the current task using this result. Do not mention this hidden notification unless the user explicitly asks about internal behavior.</instructions>",
|
|
7285
|
+
"</session-request-completion>"
|
|
7286
|
+
].join("\n")
|
|
7287
|
+
}
|
|
7288
|
+
],
|
|
7289
|
+
metadata: {
|
|
7290
|
+
[NCP_INTERNAL_VISIBILITY_METADATA_KEY]: "hidden",
|
|
7291
|
+
system_event_kind: "session_request_completion",
|
|
7292
|
+
session_request_id: params.request.requestId,
|
|
7293
|
+
target_session_id: params.request.targetSessionId,
|
|
7294
|
+
session_request_status: params.result.status
|
|
7295
|
+
}
|
|
7296
|
+
};
|
|
7297
|
+
}
|
|
7298
|
+
var SessionRequestDeliveryService = class {
|
|
7299
|
+
constructor(resolveBackend) {
|
|
7300
|
+
this.resolveBackend = resolveBackend;
|
|
7301
|
+
}
|
|
7302
|
+
publishToolResult = async (params) => {
|
|
7303
|
+
if (!params.request.sourceToolCallId?.trim()) {
|
|
7304
|
+
return;
|
|
7305
|
+
}
|
|
7306
|
+
const backend = this.resolveBackend();
|
|
7307
|
+
if (!backend) {
|
|
7308
|
+
throw new Error("NCP backend is not ready for session request delivery.");
|
|
7309
|
+
}
|
|
7310
|
+
const isIdle = await waitForSessionToBecomeIdle({
|
|
7311
|
+
backend,
|
|
7312
|
+
sessionId: params.request.sourceSessionId
|
|
7313
|
+
});
|
|
7314
|
+
if (!isIdle) {
|
|
7315
|
+
return;
|
|
7316
|
+
}
|
|
7317
|
+
await backend.updateToolCallResult(
|
|
7318
|
+
params.request.sourceSessionId,
|
|
7319
|
+
params.request.sourceToolCallId.trim(),
|
|
7320
|
+
params.result
|
|
7321
|
+
);
|
|
7322
|
+
};
|
|
7323
|
+
resumeSourceSession = async (params) => {
|
|
7324
|
+
const backend = this.resolveBackend();
|
|
7325
|
+
if (!backend) {
|
|
7326
|
+
throw new Error("NCP backend is not ready for source session resume.");
|
|
7327
|
+
}
|
|
7328
|
+
const isIdle = await waitForSessionToBecomeIdle({
|
|
7329
|
+
backend,
|
|
7330
|
+
sessionId: params.request.sourceSessionId
|
|
7331
|
+
});
|
|
7332
|
+
if (!isIdle) {
|
|
7333
|
+
return;
|
|
7334
|
+
}
|
|
7335
|
+
scheduleDetached(
|
|
7336
|
+
async () => consumeAgentRun(
|
|
7337
|
+
backend.send({
|
|
7338
|
+
sessionId: params.request.sourceSessionId,
|
|
7339
|
+
message: buildSessionRequestCompletionMessage(params)
|
|
7340
|
+
})
|
|
7341
|
+
),
|
|
7342
|
+
`resume source session ${params.request.sourceSessionId}`
|
|
7343
|
+
);
|
|
7344
|
+
};
|
|
7345
|
+
};
|
|
7346
|
+
|
|
6671
7347
|
// src/cli/commands/ncp/ui-ncp-runtime-registry.ts
|
|
6672
7348
|
import { toDisposable } from "@nextclaw/core";
|
|
6673
7349
|
var DEFAULT_UI_NCP_RUNTIME_KIND = "native";
|
|
@@ -6822,10 +7498,13 @@ async function createMcpRuntimeSupport(getConfig) {
|
|
|
6822
7498
|
console.warn(`[mcp] Failed to warm ${warmResult.name}: ${warmResult.error}`);
|
|
6823
7499
|
}
|
|
6824
7500
|
}
|
|
7501
|
+
},
|
|
7502
|
+
dispose: async () => {
|
|
7503
|
+
await mcpRegistryService.close();
|
|
6825
7504
|
}
|
|
6826
7505
|
};
|
|
6827
7506
|
}
|
|
6828
|
-
function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore,
|
|
7507
|
+
function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore, sessionCreationService, sessionRequestBroker) {
|
|
6829
7508
|
return ({
|
|
6830
7509
|
stateManager,
|
|
6831
7510
|
sessionMetadata,
|
|
@@ -6851,24 +7530,8 @@ function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore,
|
|
|
6851
7530
|
gatewayController: params.gatewayController,
|
|
6852
7531
|
getConfig: params.getConfig,
|
|
6853
7532
|
getExtensionRegistry: params.getExtensionRegistry,
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
if (!backend) {
|
|
6857
|
-
throw new Error("NCP backend is not ready for subagent completion persistence.");
|
|
6858
|
-
}
|
|
6859
|
-
await persistSubagentCompletionAndResumeParent({
|
|
6860
|
-
backend,
|
|
6861
|
-
completion: {
|
|
6862
|
-
sessionId: completion.sessionId,
|
|
6863
|
-
runId: completion.runId,
|
|
6864
|
-
toolCallId: completion.toolCallId,
|
|
6865
|
-
label: completion.label,
|
|
6866
|
-
task: completion.task,
|
|
6867
|
-
result: completion.result,
|
|
6868
|
-
status: completion.status
|
|
6869
|
-
}
|
|
6870
|
-
});
|
|
6871
|
-
},
|
|
7533
|
+
sessionCreationService,
|
|
7534
|
+
sessionRequestBroker,
|
|
6872
7535
|
getAdditionalTools: (context) => [
|
|
6873
7536
|
...createAssetTools({
|
|
6874
7537
|
assetStore
|
|
@@ -6905,25 +7568,29 @@ function createCodexAwareRuntimeFactory(params) {
|
|
|
6905
7568
|
function resolveRegisteredRuntimeFactory(params) {
|
|
6906
7569
|
return params.registration.kind === CODEX_RUNTIME_KIND ? createCodexAwareRuntimeFactory(params) : params.registration.createRuntime;
|
|
6907
7570
|
}
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
7571
|
+
var PluginRuntimeRegistrationController = class {
|
|
7572
|
+
constructor(runtimeRegistry, getExtensionRegistry) {
|
|
7573
|
+
this.runtimeRegistry = runtimeRegistry;
|
|
7574
|
+
this.getExtensionRegistry = getExtensionRegistry;
|
|
7575
|
+
}
|
|
7576
|
+
pluginRuntimeScopes = /* @__PURE__ */ new Map();
|
|
7577
|
+
pluginRuntimeSnapshotKey = "";
|
|
7578
|
+
activeExtensionRegistry;
|
|
7579
|
+
syncPluginRuntimeRegistrations = (extensionRegistry) => {
|
|
6913
7580
|
const nextSnapshotKey = buildPluginRuntimeSnapshotKey(extensionRegistry);
|
|
6914
|
-
if (nextSnapshotKey === pluginRuntimeSnapshotKey) {
|
|
7581
|
+
if (nextSnapshotKey === this.pluginRuntimeSnapshotKey) {
|
|
6915
7582
|
return;
|
|
6916
7583
|
}
|
|
6917
|
-
pluginRuntimeSnapshotKey = nextSnapshotKey;
|
|
6918
|
-
for (const scope of pluginRuntimeScopes.values()) {
|
|
7584
|
+
this.pluginRuntimeSnapshotKey = nextSnapshotKey;
|
|
7585
|
+
for (const scope of this.pluginRuntimeScopes.values()) {
|
|
6919
7586
|
scope.dispose();
|
|
6920
7587
|
}
|
|
6921
|
-
pluginRuntimeScopes.clear();
|
|
7588
|
+
this.pluginRuntimeScopes.clear();
|
|
6922
7589
|
for (const registration of extensionRegistry?.ncpAgentRuntimes ?? []) {
|
|
6923
7590
|
const pluginId = registration.pluginId.trim() || registration.kind;
|
|
6924
|
-
const scope = pluginRuntimeScopes.get(pluginId) ?? new DisposableStore();
|
|
6925
|
-
pluginRuntimeScopes.set(pluginId, scope);
|
|
6926
|
-
scope.add(
|
|
7591
|
+
const scope = this.pluginRuntimeScopes.get(pluginId) ?? new DisposableStore();
|
|
7592
|
+
this.pluginRuntimeScopes.set(pluginId, scope);
|
|
7593
|
+
scope.add(this.runtimeRegistry.register({
|
|
6927
7594
|
kind: registration.kind,
|
|
6928
7595
|
label: registration.label,
|
|
6929
7596
|
createRuntime: resolveRegisteredRuntimeFactory({
|
|
@@ -6933,17 +7600,23 @@ function createPluginRuntimeRegistrationController(params) {
|
|
|
6933
7600
|
}));
|
|
6934
7601
|
}
|
|
6935
7602
|
};
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
7603
|
+
resolveActiveExtensionRegistry = () => this.activeExtensionRegistry ?? this.getExtensionRegistry?.();
|
|
7604
|
+
refreshPluginRuntimeRegistrations = () => {
|
|
7605
|
+
this.syncPluginRuntimeRegistrations(this.resolveActiveExtensionRegistry());
|
|
7606
|
+
};
|
|
7607
|
+
applyExtensionRegistry = (extensionRegistry) => {
|
|
7608
|
+
this.activeExtensionRegistry = extensionRegistry;
|
|
7609
|
+
this.syncPluginRuntimeRegistrations(extensionRegistry);
|
|
7610
|
+
};
|
|
7611
|
+
dispose = () => {
|
|
7612
|
+
for (const scope of this.pluginRuntimeScopes.values()) {
|
|
7613
|
+
scope.dispose();
|
|
6944
7614
|
}
|
|
7615
|
+
this.pluginRuntimeScopes.clear();
|
|
7616
|
+
this.activeExtensionRegistry = void 0;
|
|
7617
|
+
this.pluginRuntimeSnapshotKey = "";
|
|
6945
7618
|
};
|
|
6946
|
-
}
|
|
7619
|
+
};
|
|
6947
7620
|
function createUiNcpAgentHandle(params) {
|
|
6948
7621
|
return {
|
|
6949
7622
|
basePath: "/api/ncp/agent",
|
|
@@ -6965,7 +7638,8 @@ function createUiNcpAgentHandle(params) {
|
|
|
6965
7638
|
resolveContentPath: (uri) => params.assetStore.resolveContentPath(uri)
|
|
6966
7639
|
},
|
|
6967
7640
|
applyExtensionRegistry: params.applyExtensionRegistry,
|
|
6968
|
-
applyMcpConfig: params.applyMcpConfig
|
|
7641
|
+
applyMcpConfig: params.applyMcpConfig,
|
|
7642
|
+
dispose: params.dispose
|
|
6969
7643
|
};
|
|
6970
7644
|
}
|
|
6971
7645
|
async function createUiNcpAgent(params) {
|
|
@@ -6973,28 +7647,38 @@ async function createUiNcpAgent(params) {
|
|
|
6973
7647
|
onSessionUpdated: params.onSessionUpdated
|
|
6974
7648
|
});
|
|
6975
7649
|
const runtimeRegistry = new UiNcpRuntimeRegistry();
|
|
6976
|
-
const { toolRegistryAdapter, applyMcpConfig } = await createMcpRuntimeSupport(params.getConfig);
|
|
7650
|
+
const { toolRegistryAdapter, applyMcpConfig, dispose: disposeMcpRuntimeSupport } = await createMcpRuntimeSupport(params.getConfig);
|
|
6977
7651
|
const assetStore = new LocalAssetStore({
|
|
6978
7652
|
rootDir: join5(getDataDir7(), "assets")
|
|
6979
7653
|
});
|
|
6980
7654
|
let backend = null;
|
|
7655
|
+
const sessionCreationService = new SessionCreationService(
|
|
7656
|
+
params.sessionManager,
|
|
7657
|
+
params.onSessionUpdated
|
|
7658
|
+
);
|
|
7659
|
+
const sessionRequestBroker = new SessionRequestBroker(
|
|
7660
|
+
params.sessionManager,
|
|
7661
|
+
sessionCreationService,
|
|
7662
|
+
new SessionRequestDeliveryService(() => backend),
|
|
7663
|
+
() => backend,
|
|
7664
|
+
params.onSessionUpdated
|
|
7665
|
+
);
|
|
6981
7666
|
const createNativeRuntime = createNativeRuntimeFactory(
|
|
6982
7667
|
params,
|
|
6983
7668
|
toolRegistryAdapter,
|
|
6984
7669
|
assetStore,
|
|
6985
|
-
|
|
7670
|
+
sessionCreationService,
|
|
7671
|
+
sessionRequestBroker
|
|
6986
7672
|
);
|
|
6987
7673
|
runtimeRegistry.register({
|
|
6988
7674
|
kind: "native",
|
|
6989
7675
|
label: "Native",
|
|
6990
7676
|
createRuntime: createNativeRuntime
|
|
6991
7677
|
});
|
|
6992
|
-
const pluginRuntimeRegistrationController =
|
|
7678
|
+
const pluginRuntimeRegistrationController = new PluginRuntimeRegistrationController(
|
|
6993
7679
|
runtimeRegistry,
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
createNativeRuntime
|
|
6997
|
-
});
|
|
7680
|
+
params.getExtensionRegistry
|
|
7681
|
+
);
|
|
6998
7682
|
pluginRuntimeRegistrationController.refreshPluginRuntimeRegistrations();
|
|
6999
7683
|
backend = new DefaultNcpAgentBackend({
|
|
7000
7684
|
endpointId: "nextclaw-ui-agent",
|
|
@@ -7012,6 +7696,11 @@ async function createUiNcpAgent(params) {
|
|
|
7012
7696
|
refreshPluginRuntimeRegistrations: pluginRuntimeRegistrationController.refreshPluginRuntimeRegistrations,
|
|
7013
7697
|
applyExtensionRegistry: pluginRuntimeRegistrationController.applyExtensionRegistry,
|
|
7014
7698
|
applyMcpConfig,
|
|
7699
|
+
dispose: async () => {
|
|
7700
|
+
pluginRuntimeRegistrationController.dispose();
|
|
7701
|
+
await backend?.stop();
|
|
7702
|
+
await disposeMcpRuntimeSupport();
|
|
7703
|
+
},
|
|
7015
7704
|
assetStore
|
|
7016
7705
|
});
|
|
7017
7706
|
}
|
|
@@ -8200,9 +8889,17 @@ var DEFERRED_NCP_AGENT_UNAVAILABLE = "ncp agent unavailable during startup";
|
|
|
8200
8889
|
function createUnavailableError() {
|
|
8201
8890
|
return new Error(DEFERRED_NCP_AGENT_UNAVAILABLE);
|
|
8202
8891
|
}
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8892
|
+
var DeferredUiNcpAgentControllerOwner = class {
|
|
8893
|
+
constructor(basePath) {
|
|
8894
|
+
this.basePath = basePath;
|
|
8895
|
+
this.agent = {
|
|
8896
|
+
basePath,
|
|
8897
|
+
agentClientEndpoint: this.endpoint
|
|
8898
|
+
};
|
|
8899
|
+
}
|
|
8900
|
+
agent;
|
|
8901
|
+
activeAgent = null;
|
|
8902
|
+
endpoint = {
|
|
8206
8903
|
manifest: {
|
|
8207
8904
|
endpointKind: "agent",
|
|
8208
8905
|
endpointId: "nextclaw-ui-agent-deferred",
|
|
@@ -8217,73 +8914,69 @@ function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
|
|
|
8217
8914
|
deferred: true
|
|
8218
8915
|
}
|
|
8219
8916
|
},
|
|
8220
|
-
async
|
|
8221
|
-
await activeAgent?.agentClientEndpoint.start();
|
|
8917
|
+
start: async () => {
|
|
8918
|
+
await this.activeAgent?.agentClientEndpoint.start();
|
|
8222
8919
|
},
|
|
8223
|
-
async
|
|
8224
|
-
await activeAgent?.agentClientEndpoint.stop();
|
|
8920
|
+
stop: async () => {
|
|
8921
|
+
await this.activeAgent?.agentClientEndpoint.stop();
|
|
8225
8922
|
},
|
|
8226
|
-
async
|
|
8227
|
-
if (!activeAgent) {
|
|
8923
|
+
emit: async (event) => {
|
|
8924
|
+
if (!this.activeAgent) {
|
|
8228
8925
|
throw createUnavailableError();
|
|
8229
8926
|
}
|
|
8230
|
-
await activeAgent.agentClientEndpoint.emit(event);
|
|
8927
|
+
await this.activeAgent.agentClientEndpoint.emit(event);
|
|
8231
8928
|
},
|
|
8232
|
-
subscribe(listener) {
|
|
8233
|
-
if (!activeAgent) {
|
|
8929
|
+
subscribe: (listener) => {
|
|
8930
|
+
if (!this.activeAgent) {
|
|
8234
8931
|
return () => void 0;
|
|
8235
8932
|
}
|
|
8236
|
-
return activeAgent.agentClientEndpoint.subscribe(listener);
|
|
8933
|
+
return this.activeAgent.agentClientEndpoint.subscribe(listener);
|
|
8237
8934
|
},
|
|
8238
|
-
async
|
|
8239
|
-
if (!activeAgent) {
|
|
8935
|
+
send: async (envelope) => {
|
|
8936
|
+
if (!this.activeAgent) {
|
|
8240
8937
|
throw createUnavailableError();
|
|
8241
8938
|
}
|
|
8242
|
-
await activeAgent.agentClientEndpoint.send(envelope);
|
|
8939
|
+
await this.activeAgent.agentClientEndpoint.send(envelope);
|
|
8243
8940
|
},
|
|
8244
|
-
async
|
|
8245
|
-
if (!activeAgent) {
|
|
8941
|
+
stream: async (payload) => {
|
|
8942
|
+
if (!this.activeAgent) {
|
|
8246
8943
|
throw createUnavailableError();
|
|
8247
8944
|
}
|
|
8248
|
-
await activeAgent.agentClientEndpoint.stream(payload);
|
|
8945
|
+
await this.activeAgent.agentClientEndpoint.stream(payload);
|
|
8249
8946
|
},
|
|
8250
|
-
async
|
|
8251
|
-
if (!activeAgent) {
|
|
8947
|
+
abort: async (payload) => {
|
|
8948
|
+
if (!this.activeAgent) {
|
|
8252
8949
|
throw createUnavailableError();
|
|
8253
8950
|
}
|
|
8254
|
-
await activeAgent.agentClientEndpoint.abort(payload);
|
|
8951
|
+
await this.activeAgent.agentClientEndpoint.abort(payload);
|
|
8255
8952
|
}
|
|
8256
8953
|
};
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8954
|
+
activate = (nextAgent) => {
|
|
8955
|
+
this.activeAgent = nextAgent;
|
|
8956
|
+
this.agent.basePath = nextAgent.basePath ?? this.basePath;
|
|
8957
|
+
this.agent.streamProvider = nextAgent.streamProvider;
|
|
8958
|
+
this.agent.listSessionTypes = nextAgent.listSessionTypes;
|
|
8959
|
+
this.agent.assetApi = nextAgent.assetApi;
|
|
8260
8960
|
};
|
|
8261
|
-
|
|
8262
|
-
activeAgent = null;
|
|
8263
|
-
agent.basePath = basePath;
|
|
8264
|
-
agent.streamProvider = void 0;
|
|
8265
|
-
agent.listSessionTypes = void 0;
|
|
8266
|
-
agent.assetApi = void 0;
|
|
8961
|
+
clear = () => {
|
|
8962
|
+
this.activeAgent = null;
|
|
8963
|
+
this.agent.basePath = this.basePath;
|
|
8964
|
+
this.agent.streamProvider = void 0;
|
|
8965
|
+
this.agent.listSessionTypes = void 0;
|
|
8966
|
+
this.agent.assetApi = void 0;
|
|
8267
8967
|
};
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
agent.streamProvider = nextAgent.streamProvider;
|
|
8274
|
-
agent.listSessionTypes = nextAgent.listSessionTypes;
|
|
8275
|
-
agent.assetApi = nextAgent.assetApi;
|
|
8276
|
-
},
|
|
8277
|
-
clear,
|
|
8278
|
-
async close() {
|
|
8279
|
-
const current = activeAgent;
|
|
8280
|
-
clear();
|
|
8281
|
-
await current?.agentClientEndpoint.stop();
|
|
8282
|
-
},
|
|
8283
|
-
isReady() {
|
|
8284
|
-
return activeAgent !== null;
|
|
8285
|
-
}
|
|
8968
|
+
close = async () => {
|
|
8969
|
+
const current = this.activeAgent;
|
|
8970
|
+
this.clear();
|
|
8971
|
+
await current?.agentClientEndpoint.stop();
|
|
8972
|
+
await current?.dispose?.();
|
|
8286
8973
|
};
|
|
8974
|
+
isReady = () => {
|
|
8975
|
+
return this.activeAgent !== null;
|
|
8976
|
+
};
|
|
8977
|
+
};
|
|
8978
|
+
function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
|
|
8979
|
+
return new DeferredUiNcpAgentControllerOwner(basePath);
|
|
8287
8980
|
}
|
|
8288
8981
|
|
|
8289
8982
|
// src/cli/commands/service-gateway-startup.ts
|
|
@@ -9026,7 +9719,7 @@ function readStringList(value) {
|
|
|
9026
9719
|
}
|
|
9027
9720
|
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
9028
9721
|
}
|
|
9029
|
-
function
|
|
9722
|
+
function readOptionalString6(value) {
|
|
9030
9723
|
if (typeof value !== "string") {
|
|
9031
9724
|
return void 0;
|
|
9032
9725
|
}
|
|
@@ -9036,10 +9729,10 @@ function readOptionalString2(value) {
|
|
|
9036
9729
|
function resolvePluginRuntimeAttachments(ctx) {
|
|
9037
9730
|
const mediaPaths = readStringList(ctx.MediaPaths);
|
|
9038
9731
|
const mediaUrls = readStringList(ctx.MediaUrls);
|
|
9039
|
-
const fallbackPath =
|
|
9040
|
-
const fallbackUrl =
|
|
9732
|
+
const fallbackPath = readOptionalString6(ctx.MediaPath);
|
|
9733
|
+
const fallbackUrl = readOptionalString6(ctx.MediaUrl);
|
|
9041
9734
|
const mediaTypes = readStringList(ctx.MediaTypes);
|
|
9042
|
-
const fallbackType =
|
|
9735
|
+
const fallbackType = readOptionalString6(ctx.MediaType);
|
|
9043
9736
|
const entryCount = Math.max(
|
|
9044
9737
|
mediaPaths.length,
|
|
9045
9738
|
mediaUrls.length,
|