nextclaw 0.16.31 → 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 +1031 -318
- package/package.json +11 -11
- package/ui-dist/assets/{ChannelsList-ByHWHkQS.js → ChannelsList-DVDu1xvz.js} +6 -6
- package/ui-dist/assets/ChatPage-Z9tRzm_n.js +43 -0
- package/ui-dist/assets/DocBrowser-B9OaZjmg.js +1 -0
- package/ui-dist/assets/{DocBrowser-3y_NHZ71.js → DocBrowser-BmtBLFU0.js} +1 -1
- package/ui-dist/assets/{DocBrowserContext-CVJuwCcw.js → DocBrowserContext-YIKkPb76.js} +1 -1
- package/ui-dist/assets/{LogoBadge-D8fyilO-.js → LogoBadge-F7ZWdxLT.js} +1 -1
- package/ui-dist/assets/{MarketplacePage-CmhsZXr1.js → MarketplacePage-Buo9HrOz.js} +2 -2
- package/ui-dist/assets/MarketplacePage-D6rVQEQR.js +1 -0
- package/ui-dist/assets/{McpMarketplacePage-C7PkCYbp.js → McpMarketplacePage-JnkYwK7p.js} +2 -2
- package/ui-dist/assets/ModelConfig-BYRhgp0c.js +1 -0
- package/ui-dist/assets/ProvidersList-DmLyyHvX.js +1 -0
- package/ui-dist/assets/RemoteAccessPage-CDSSvH7Z.js +1 -0
- package/ui-dist/assets/RuntimeConfig-v7a7Fe3x.js +1 -0
- package/ui-dist/assets/{SearchConfig-Dm7r2yfp.js → SearchConfig-D5f1EkLE.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-BBP_mbQh.js → SecretsConfig-D61IKcYt.js} +2 -2
- package/ui-dist/assets/{SessionsConfig-6wNJloZN.js → SessionsConfig-BRIxVTEv.js} +2 -2
- package/ui-dist/assets/{book-open-B26jGBjY.js → book-open-CXoF5nQC.js} +1 -1
- package/ui-dist/assets/chat-session-display-D0WpnuRZ.js +1 -0
- package/ui-dist/assets/{chunk-JZWAC4HX-B-4B29RN.js → chunk-JZWAC4HX-CvRWvTy5.js} +1 -1
- package/ui-dist/assets/{config-BaC29Qf-.js → config-DJswxxE8.js} +1 -1
- package/ui-dist/assets/{createLucideIcon-DiFAvXmK.js → createLucideIcon-CjGHOWb6.js} +1 -1
- package/ui-dist/assets/{dist-pCfWPG1A.js → dist-Cl2QB-2y.js} +1 -1
- package/ui-dist/assets/{dist-kW_O3kyZ.js → dist-nqTTbVdA.js} +1 -1
- package/ui-dist/assets/{external-link-D5-p-Gmm.js → external-link-tIO7zING.js} +1 -1
- package/ui-dist/assets/{hash-BlwrSV0q.js → hash-JWUyl1pT.js} +1 -1
- package/ui-dist/assets/i18n-CDHMXlRZ.js +1 -0
- package/ui-dist/assets/{index-DvKS3L9j.js → index-BuwbBgmT.js} +3 -3
- package/ui-dist/assets/index-bZ8cqQIS.css +1 -0
- package/ui-dist/assets/{label-RyXfZqkP.js → label-BIpeNu4r.js} +1 -1
- package/ui-dist/assets/loader-circle-Cs8XVFTw.js +1 -0
- package/ui-dist/assets/{logos-Bpl8QTgI.js → logos-DThdM9lk.js} +1 -1
- package/ui-dist/assets/{page-layout--S0YBU0W.js → page-layout-D3Xo605Z.js} +1 -1
- package/ui-dist/assets/plus-PHf8q-Ct.js +1 -0
- package/ui-dist/assets/{popover-BEjfbEwy.js → popover-BJRUGA_H.js} +1 -1
- package/ui-dist/assets/provider-models-bz5y28rq.js +1 -0
- package/ui-dist/assets/{react-BuSP2-8B.js → react-7ZHqQtEV.js} +1 -1
- package/ui-dist/assets/refresh-ccw-CC6-_QuL.js +1 -0
- package/ui-dist/assets/{save-DPPPpD_c.js → save-DJM5RRWW.js} +1 -1
- package/ui-dist/assets/search-C91yH_6y.js +1 -0
- package/ui-dist/assets/{security-config-6t78Ph-I.js → security-config-DbUyWcQz.js} +1 -1
- package/ui-dist/assets/{select-CT50pzod.js → select-DSkTc61S.js} +1 -1
- package/ui-dist/assets/skeleton-Dzg-HOiN.js +1 -0
- package/ui-dist/assets/{status-dot-BbBqRHfh.js → status-dot-LNBlDu3q.js} +1 -1
- package/ui-dist/assets/{switch-D3l6AcCk.js → switch-Bo-Y46HZ.js} +1 -1
- package/ui-dist/assets/tabs-custom-DXv507_2.js +1 -0
- package/ui-dist/assets/{trash-2-B2_AGVE3.js → trash-2-DFZmW6Gg.js} +1 -1
- package/ui-dist/assets/useConfirmDialog-COwYXDKm.js +1 -0
- package/ui-dist/assets/{useMutation-BzCrO8j-.js → useMutation-DrZrOgVL.js} +1 -1
- package/ui-dist/assets/x-D7Q1yqSF.js +1 -0
- package/ui-dist/index.html +18 -18
- package/ui-dist/assets/ChatPage-FdT3pDnw.js +0 -42
- package/ui-dist/assets/DocBrowser-CMdPdbZj.js +0 -1
- package/ui-dist/assets/MarketplacePage-9oKmxN2n.js +0 -1
- package/ui-dist/assets/ModelConfig-DmCY6jWM.js +0 -1
- package/ui-dist/assets/ProvidersList-ClT-34aX.js +0 -1
- package/ui-dist/assets/RemoteAccessPage-B6hUZl1O.js +0 -1
- package/ui-dist/assets/RuntimeConfig-C5aqliGk.js +0 -1
- package/ui-dist/assets/chat-session-display-Bjmn4aIZ.js +0 -1
- package/ui-dist/assets/i18n-CSytxMFI.js +0 -1
- package/ui-dist/assets/index-CUy6doWo.css +0 -1
- package/ui-dist/assets/loader-circle-B2J777gj.js +0 -1
- package/ui-dist/assets/plus-CM9XJ0Tf.js +0 -1
- package/ui-dist/assets/provider-models-C8JQUd1E.js +0 -1
- package/ui-dist/assets/search-Ctaw34Kp.js +0 -1
- package/ui-dist/assets/skeleton-Bycyb0zU.js +0 -1
- package/ui-dist/assets/tabs-custom-TZQ5WPWP.js +0 -1
- package/ui-dist/assets/useConfirmDialog-BDpdjfIO.js +0 -1
- package/ui-dist/assets/x-CHOBE-63.js +0 -1
- /package/ui-dist/assets/{config-hints-fGnUjDe9.js → config-hints-WtpHP_DW.js} +0 -0
- /package/ui-dist/assets/{config-layout-B-7erZRN.js → config-layout-LQ10ozRC.js} +0 -0
- /package/ui-dist/assets/{marketplace-localization-CXeGRf6E.js → marketplace-localization-CxSTG9wr.js} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -698,11 +698,11 @@ function collectFiles(rootDir) {
|
|
|
698
698
|
}
|
|
699
699
|
function installBuiltinSkill(workdir, destinationDir, skillName) {
|
|
700
700
|
const loader = new SkillsLoader(workdir);
|
|
701
|
-
const
|
|
702
|
-
if (!
|
|
703
|
-
throw new Error(`
|
|
701
|
+
const workspaceSkill = loader.listSkills(false).find((skill) => skill.name === skillName && skill.source === "workspace");
|
|
702
|
+
if (!workspaceSkill) {
|
|
703
|
+
throw new Error(`Workspace skill not found in local installation: ${skillName}`);
|
|
704
704
|
}
|
|
705
|
-
cpSync(dirname(
|
|
705
|
+
cpSync(dirname(workspaceSkill.path), destinationDir, { recursive: true, force: true });
|
|
706
706
|
}
|
|
707
707
|
async function fetchMarketplaceSkillItem(apiBase, slug) {
|
|
708
708
|
return runWithMarketplaceNetworkRetry(async () => {
|
|
@@ -5261,6 +5261,8 @@ import {
|
|
|
5261
5261
|
InputBudgetPruner,
|
|
5262
5262
|
getWorkspacePath as getWorkspacePath7,
|
|
5263
5263
|
parseThinkingLevel as parseThinkingLevel2,
|
|
5264
|
+
readSessionProjectRoot,
|
|
5265
|
+
resolveSessionWorkspacePath,
|
|
5264
5266
|
resolveThinkingLevel
|
|
5265
5267
|
} from "@nextclaw/core";
|
|
5266
5268
|
|
|
@@ -5638,14 +5640,230 @@ import {
|
|
|
5638
5640
|
SessionsHistoryTool,
|
|
5639
5641
|
SessionsListTool,
|
|
5640
5642
|
SessionsSendTool,
|
|
5641
|
-
SpawnTool,
|
|
5642
|
-
SubagentManager,
|
|
5643
|
-
SubagentsTool,
|
|
5644
5643
|
ToolRegistry,
|
|
5645
5644
|
WebFetchTool,
|
|
5646
5645
|
WebSearchTool,
|
|
5647
5646
|
WriteFileTool
|
|
5648
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
|
|
5649
5867
|
function toToolParams(args) {
|
|
5650
5868
|
if (isRecord3(args)) {
|
|
5651
5869
|
return args;
|
|
@@ -5695,46 +5913,11 @@ var CoreToolNcpAdapter = class {
|
|
|
5695
5913
|
var NextclawNcpToolRegistry = class {
|
|
5696
5914
|
constructor(options) {
|
|
5697
5915
|
this.options = options;
|
|
5698
|
-
const initialConfig = this.options.getConfig();
|
|
5699
|
-
this.subagents = new SubagentManager({
|
|
5700
|
-
providerManager: this.options.providerManager,
|
|
5701
|
-
workspace: initialConfig.agents.defaults.workspace,
|
|
5702
|
-
bus: this.options.bus,
|
|
5703
|
-
model: initialConfig.agents.defaults.model,
|
|
5704
|
-
contextTokens: initialConfig.agents.defaults.contextTokens,
|
|
5705
|
-
searchConfig: initialConfig.search,
|
|
5706
|
-
execConfig: initialConfig.tools.exec,
|
|
5707
|
-
restrictToWorkspace: initialConfig.tools.restrictToWorkspace,
|
|
5708
|
-
completionSink: async (params) => {
|
|
5709
|
-
const sessionId = params.origin.sessionKey?.trim();
|
|
5710
|
-
if (!sessionId || !this.options.writeSubagentCompletionToSession) {
|
|
5711
|
-
return;
|
|
5712
|
-
}
|
|
5713
|
-
await this.options.writeSubagentCompletionToSession({
|
|
5714
|
-
sessionId,
|
|
5715
|
-
runId: params.runId,
|
|
5716
|
-
toolCallId: params.origin.toolCallId,
|
|
5717
|
-
label: params.label,
|
|
5718
|
-
task: params.task,
|
|
5719
|
-
result: params.result,
|
|
5720
|
-
status: params.status
|
|
5721
|
-
});
|
|
5722
|
-
}
|
|
5723
|
-
});
|
|
5724
5916
|
}
|
|
5725
|
-
subagents;
|
|
5726
5917
|
registry = new ToolRegistry();
|
|
5727
5918
|
tools = /* @__PURE__ */ new Map();
|
|
5728
5919
|
currentExtensionToolContext = {};
|
|
5729
|
-
prepareForRun(context) {
|
|
5730
|
-
this.subagents.updateRuntimeOptions({
|
|
5731
|
-
model: context.model,
|
|
5732
|
-
maxTokens: context.maxTokens,
|
|
5733
|
-
contextTokens: context.contextTokens,
|
|
5734
|
-
searchConfig: context.searchConfig,
|
|
5735
|
-
execConfig: { timeout: context.execTimeoutSeconds },
|
|
5736
|
-
restrictToWorkspace: context.restrictToWorkspace
|
|
5737
|
-
});
|
|
5920
|
+
prepareForRun = (context) => {
|
|
5738
5921
|
this.currentExtensionToolContext = {
|
|
5739
5922
|
config: context.config,
|
|
5740
5923
|
workspaceDir: context.workspace,
|
|
@@ -5748,30 +5931,30 @@ var NextclawNcpToolRegistry = class {
|
|
|
5748
5931
|
this.registerDefaultTools(context);
|
|
5749
5932
|
this.registerExtensionTools(context);
|
|
5750
5933
|
this.registerAdditionalTools(context);
|
|
5751
|
-
}
|
|
5752
|
-
listTools() {
|
|
5934
|
+
};
|
|
5935
|
+
listTools = () => {
|
|
5753
5936
|
return [...this.tools.values()].filter((tool) => this.isToolAvailable(tool.name));
|
|
5754
|
-
}
|
|
5755
|
-
getTool(name) {
|
|
5937
|
+
};
|
|
5938
|
+
getTool = (name) => {
|
|
5756
5939
|
if (!this.isToolAvailable(name)) {
|
|
5757
5940
|
return void 0;
|
|
5758
5941
|
}
|
|
5759
5942
|
return this.tools.get(name);
|
|
5760
|
-
}
|
|
5761
|
-
getToolDefinitions() {
|
|
5943
|
+
};
|
|
5944
|
+
getToolDefinitions = () => {
|
|
5762
5945
|
return this.listTools().map((tool) => ({
|
|
5763
5946
|
name: tool.name,
|
|
5764
5947
|
description: tool.description,
|
|
5765
5948
|
parameters: tool.parameters
|
|
5766
5949
|
}));
|
|
5767
|
-
}
|
|
5768
|
-
async
|
|
5950
|
+
};
|
|
5951
|
+
execute = async (toolCallId, toolName, args) => {
|
|
5769
5952
|
if (this.registry.has(toolName)) {
|
|
5770
5953
|
return this.registry.executeRaw(toolName, toToolParams(args), toolCallId);
|
|
5771
5954
|
}
|
|
5772
5955
|
return this.tools.get(toolName)?.execute(args);
|
|
5773
|
-
}
|
|
5774
|
-
registerDefaultTools(context) {
|
|
5956
|
+
};
|
|
5957
|
+
registerDefaultTools = (context) => {
|
|
5775
5958
|
const allowedDir = context.restrictToWorkspace ? context.workspace : void 0;
|
|
5776
5959
|
this.registerTool(new ReadFileTool(allowedDir));
|
|
5777
5960
|
this.registerTool(new WriteFileTool(allowedDir));
|
|
@@ -5791,15 +5974,31 @@ var NextclawNcpToolRegistry = class {
|
|
|
5791
5974
|
this.registerTool(new WebSearchTool(context.searchConfig));
|
|
5792
5975
|
this.registerTool(new WebFetchTool());
|
|
5793
5976
|
this.registerMessagingTools(context);
|
|
5794
|
-
const spawnTool = new
|
|
5795
|
-
spawnTool.setContext(
|
|
5796
|
-
context.
|
|
5797
|
-
context.
|
|
5798
|
-
context.
|
|
5799
|
-
context.
|
|
5800
|
-
|
|
5801
|
-
);
|
|
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
|
+
});
|
|
5802
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);
|
|
5803
6002
|
this.registerTool(new SessionsListTool(this.options.sessionManager));
|
|
5804
6003
|
this.registerTool(new SessionsHistoryTool(this.options.sessionManager));
|
|
5805
6004
|
const sessionsSendTool = new SessionsSendTool(this.options.sessionManager, this.options.bus);
|
|
@@ -5814,12 +6013,11 @@ var NextclawNcpToolRegistry = class {
|
|
|
5814
6013
|
this.registerTool(sessionsSendTool);
|
|
5815
6014
|
this.registerTool(new MemorySearchTool(context.workspace));
|
|
5816
6015
|
this.registerTool(new MemoryGetTool(context.workspace));
|
|
5817
|
-
this.registerTool(new SubagentsTool(this.subagents));
|
|
5818
6016
|
const gatewayTool = new GatewayTool(this.options.gatewayController);
|
|
5819
6017
|
gatewayTool.setContext({ sessionKey: context.sessionId });
|
|
5820
6018
|
this.registerTool(gatewayTool);
|
|
5821
|
-
}
|
|
5822
|
-
registerMessagingTools(context) {
|
|
6019
|
+
};
|
|
6020
|
+
registerMessagingTools = (context) => {
|
|
5823
6021
|
const accountId = readMetadataAccountId(context.metadata, {});
|
|
5824
6022
|
const messageTool = new MessageTool((message) => this.options.bus.publishOutbound(message));
|
|
5825
6023
|
messageTool.setContext(context.channel, context.chatId, accountId ?? null);
|
|
@@ -5829,8 +6027,8 @@ var NextclawNcpToolRegistry = class {
|
|
|
5829
6027
|
cronTool.setContext(context.channel, context.chatId, accountId ?? null);
|
|
5830
6028
|
this.registerTool(cronTool);
|
|
5831
6029
|
}
|
|
5832
|
-
}
|
|
5833
|
-
registerExtensionTools(context) {
|
|
6030
|
+
};
|
|
6031
|
+
registerExtensionTools = (context) => {
|
|
5834
6032
|
const extensionRegistry = this.options.getExtensionRegistry?.();
|
|
5835
6033
|
if (!extensionRegistry || extensionRegistry.tools.length === 0) {
|
|
5836
6034
|
return;
|
|
@@ -5854,15 +6052,15 @@ var NextclawNcpToolRegistry = class {
|
|
|
5854
6052
|
);
|
|
5855
6053
|
}
|
|
5856
6054
|
}
|
|
5857
|
-
}
|
|
5858
|
-
registerTool(tool) {
|
|
6055
|
+
};
|
|
6056
|
+
registerTool = (tool) => {
|
|
5859
6057
|
this.registry.register(tool);
|
|
5860
6058
|
this.tools.set(
|
|
5861
6059
|
tool.name,
|
|
5862
6060
|
new CoreToolNcpAdapter(tool, async (toolName, args) => this.registry.execute(toolName, toToolParams(args)))
|
|
5863
6061
|
);
|
|
5864
|
-
}
|
|
5865
|
-
registerAdditionalTools(context) {
|
|
6062
|
+
};
|
|
6063
|
+
registerAdditionalTools = (context) => {
|
|
5866
6064
|
const tools = this.options.getAdditionalTools?.(context) ?? [];
|
|
5867
6065
|
for (const tool of tools) {
|
|
5868
6066
|
if (this.tools.has(tool.name)) {
|
|
@@ -5870,11 +6068,11 @@ var NextclawNcpToolRegistry = class {
|
|
|
5870
6068
|
}
|
|
5871
6069
|
this.tools.set(tool.name, tool);
|
|
5872
6070
|
}
|
|
5873
|
-
}
|
|
5874
|
-
isToolAvailable(name) {
|
|
6071
|
+
};
|
|
6072
|
+
isToolAvailable = (name) => {
|
|
5875
6073
|
const coreTool = this.registry.get(name);
|
|
5876
6074
|
return coreTool ? coreTool.isAvailable() : true;
|
|
5877
|
-
}
|
|
6075
|
+
};
|
|
5878
6076
|
};
|
|
5879
6077
|
function resolveAgentHandoffDepth(metadata) {
|
|
5880
6078
|
const rawDepth = Number(metadata.agent_handoff_depth ?? 0);
|
|
@@ -5982,6 +6180,19 @@ function prependRequestedSkills(content, requestedSkillNames) {
|
|
|
5982
6180
|
|
|
5983
6181
|
${content}`;
|
|
5984
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
|
+
}
|
|
5985
6196
|
function filterTools(toolDefinitions, requestedToolNames) {
|
|
5986
6197
|
if (toolDefinitions.length === 0) {
|
|
5987
6198
|
return void 0;
|
|
@@ -6011,7 +6222,7 @@ var NextclawNcpContextBuilder = class {
|
|
|
6011
6222
|
this.options = options;
|
|
6012
6223
|
}
|
|
6013
6224
|
inputBudgetPruner = new InputBudgetPruner();
|
|
6014
|
-
prepare(input, _options) {
|
|
6225
|
+
prepare = (input, _options) => {
|
|
6015
6226
|
const config2 = this.options.getConfig();
|
|
6016
6227
|
const profile = resolvePrimaryAgentProfile(config2);
|
|
6017
6228
|
const requestMetadata = mergeInputMetadata(input);
|
|
@@ -6021,6 +6232,10 @@ var NextclawNcpContextBuilder = class {
|
|
|
6021
6232
|
requestMetadata,
|
|
6022
6233
|
fallbackModel: profile.model
|
|
6023
6234
|
});
|
|
6235
|
+
const effectiveWorkspace = resolveSessionWorkspacePath({
|
|
6236
|
+
sessionMetadata: session.metadata,
|
|
6237
|
+
workspace: profile.workspace
|
|
6238
|
+
});
|
|
6024
6239
|
syncSessionThinkingPreference({ session, requestMetadata });
|
|
6025
6240
|
const { channel, chatId } = resolveSessionChannelContext({
|
|
6026
6241
|
session,
|
|
@@ -6058,7 +6273,7 @@ var NextclawNcpContextBuilder = class {
|
|
|
6058
6273
|
model: effectiveModel,
|
|
6059
6274
|
restrictToWorkspace: profile.restrictToWorkspace,
|
|
6060
6275
|
searchConfig: profile.searchConfig,
|
|
6061
|
-
workspace:
|
|
6276
|
+
workspace: effectiveWorkspace
|
|
6062
6277
|
});
|
|
6063
6278
|
const accountId = readAccountIdForHints(requestMetadata, session.metadata);
|
|
6064
6279
|
const messageToolHints = this.options.resolveMessageToolHints?.({
|
|
@@ -6068,7 +6283,15 @@ var NextclawNcpContextBuilder = class {
|
|
|
6068
6283
|
accountId: accountId ?? null
|
|
6069
6284
|
});
|
|
6070
6285
|
const toolDefinitions = this.options.toolRegistry.getToolDefinitions();
|
|
6071
|
-
const
|
|
6286
|
+
const additionalSystemSections = [buildSessionOrchestrationSection()];
|
|
6287
|
+
const contextBuilder = new ContextBuilder(
|
|
6288
|
+
effectiveWorkspace,
|
|
6289
|
+
config2.agents.context,
|
|
6290
|
+
{
|
|
6291
|
+
hostWorkspace: profile.workspace,
|
|
6292
|
+
sessionProjectRoot: readSessionProjectRoot(session.metadata)
|
|
6293
|
+
}
|
|
6294
|
+
);
|
|
6072
6295
|
const sessionMessages = _options?.sessionMessages ?? [];
|
|
6073
6296
|
const messages = contextBuilder.buildMessages({
|
|
6074
6297
|
history: toLegacyMessages([...sessionMessages], {
|
|
@@ -6082,7 +6305,8 @@ var NextclawNcpContextBuilder = class {
|
|
|
6082
6305
|
thinkingLevel: runtimeThinking,
|
|
6083
6306
|
skillNames: requestedSkillNames,
|
|
6084
6307
|
messageToolHints,
|
|
6085
|
-
availableTools: buildToolCatalogEntries(toolDefinitions)
|
|
6308
|
+
availableTools: buildToolCatalogEntries(toolDefinitions),
|
|
6309
|
+
additionalSystemSections
|
|
6086
6310
|
});
|
|
6087
6311
|
messages[messages.length - 1] = {
|
|
6088
6312
|
role: currentTurn.currentRole,
|
|
@@ -6098,11 +6322,20 @@ var NextclawNcpContextBuilder = class {
|
|
|
6098
6322
|
model: effectiveModel,
|
|
6099
6323
|
thinkingLevel: runtimeThinking
|
|
6100
6324
|
};
|
|
6101
|
-
}
|
|
6325
|
+
};
|
|
6102
6326
|
};
|
|
6103
6327
|
|
|
6104
6328
|
// src/cli/commands/ncp/nextclaw-agent-session-store.ts
|
|
6105
6329
|
import { sanitizeAssistantReplyTags as sanitizeAssistantReplyTags2 } from "@nextclaw/ncp";
|
|
6330
|
+
|
|
6331
|
+
// src/cli/commands/ncp/nextclaw-agent-session-metadata.utils.ts
|
|
6332
|
+
function resolvePersistedSessionMetadata(params) {
|
|
6333
|
+
const messageMetadata = extractMessageMetadata(params.sessionRecord.messages);
|
|
6334
|
+
const nextMetadata = params.preserveExistingMetadata ? mergeSessionMetadata(params.currentMetadata, messageMetadata) : mergeSessionMetadata({}, messageMetadata);
|
|
6335
|
+
return mergeSessionMetadata(nextMetadata, cloneMetadata(params.sessionRecord.metadata));
|
|
6336
|
+
}
|
|
6337
|
+
|
|
6338
|
+
// src/cli/commands/ncp/nextclaw-agent-session-store.ts
|
|
6106
6339
|
function tryParseJson(value) {
|
|
6107
6340
|
try {
|
|
6108
6341
|
return JSON.parse(value);
|
|
@@ -6182,8 +6415,7 @@ function parseLegacyToolCalls(value) {
|
|
|
6182
6415
|
}).filter((entry) => entry !== null);
|
|
6183
6416
|
}
|
|
6184
6417
|
function createMessageId(sessionId, index, role, timestamp) {
|
|
6185
|
-
|
|
6186
|
-
return `${sessionId}:${safeRole}:${index}:${timestamp}`;
|
|
6418
|
+
return `${sessionId}:${role.trim().toLowerCase() || "message"}:${index}:${timestamp}`;
|
|
6187
6419
|
}
|
|
6188
6420
|
function isNcpMessagePart(value) {
|
|
6189
6421
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.type === "string";
|
|
@@ -6360,19 +6592,14 @@ var NextclawAgentSessionStore = class {
|
|
|
6360
6592
|
this.sessionManager = sessionManager;
|
|
6361
6593
|
this.options = options;
|
|
6362
6594
|
}
|
|
6363
|
-
async
|
|
6595
|
+
getSession = async (sessionId) => {
|
|
6364
6596
|
const session = this.sessionManager.getIfExists(sessionId);
|
|
6365
6597
|
if (!session) {
|
|
6366
6598
|
return null;
|
|
6367
6599
|
}
|
|
6368
|
-
return {
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
updatedAt: session.updatedAt.toISOString(),
|
|
6372
|
-
metadata: structuredClone(session.metadata)
|
|
6373
|
-
};
|
|
6374
|
-
}
|
|
6375
|
-
async listSessions() {
|
|
6600
|
+
return { sessionId, messages: toNcpMessages(sessionId, session.messages), updatedAt: session.updatedAt.toISOString(), metadata: structuredClone(session.metadata) };
|
|
6601
|
+
};
|
|
6602
|
+
listSessions = async () => {
|
|
6376
6603
|
const records = this.sessionManager.listSessions();
|
|
6377
6604
|
const sessions = [];
|
|
6378
6605
|
for (const record of records) {
|
|
@@ -6384,27 +6611,22 @@ var NextclawAgentSessionStore = class {
|
|
|
6384
6611
|
if (!session) {
|
|
6385
6612
|
continue;
|
|
6386
6613
|
}
|
|
6387
|
-
sessions.push({
|
|
6388
|
-
sessionId,
|
|
6389
|
-
messages: toNcpMessages(sessionId, session.messages),
|
|
6390
|
-
updatedAt: session.updatedAt.toISOString(),
|
|
6391
|
-
metadata: structuredClone(session.metadata)
|
|
6392
|
-
});
|
|
6614
|
+
sessions.push({ sessionId, messages: toNcpMessages(sessionId, session.messages), updatedAt: session.updatedAt.toISOString(), metadata: structuredClone(session.metadata) });
|
|
6393
6615
|
}
|
|
6394
6616
|
sessions.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
6395
6617
|
return sessions;
|
|
6396
|
-
}
|
|
6397
|
-
async
|
|
6618
|
+
};
|
|
6619
|
+
persistSession = async (sessionRecord, options) => {
|
|
6398
6620
|
if (this.options.writeMode === "runtime-owned") {
|
|
6399
6621
|
return;
|
|
6400
6622
|
}
|
|
6401
6623
|
const session = this.sessionManager.getIfExists(sessionRecord.sessionId) ?? this.sessionManager.getOrCreate(sessionRecord.sessionId);
|
|
6402
6624
|
const legacyMessages = toLegacyMessages(sessionRecord.messages);
|
|
6403
|
-
|
|
6404
|
-
session.metadata,
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6625
|
+
session.metadata = resolvePersistedSessionMetadata({
|
|
6626
|
+
currentMetadata: session.metadata,
|
|
6627
|
+
sessionRecord,
|
|
6628
|
+
preserveExistingMetadata: options.preserveExistingMetadata
|
|
6629
|
+
});
|
|
6408
6630
|
this.sessionManager.clear(session);
|
|
6409
6631
|
for (const message of legacyMessages) {
|
|
6410
6632
|
this.sessionManager.appendEvent(session, {
|
|
@@ -6420,8 +6642,18 @@ var NextclawAgentSessionStore = class {
|
|
|
6420
6642
|
}
|
|
6421
6643
|
this.sessionManager.save(session);
|
|
6422
6644
|
this.options.onSessionUpdated?.(sessionRecord.sessionId);
|
|
6423
|
-
}
|
|
6424
|
-
async
|
|
6645
|
+
};
|
|
6646
|
+
saveSession = async (sessionRecord) => {
|
|
6647
|
+
await this.persistSession(sessionRecord, {
|
|
6648
|
+
preserveExistingMetadata: true
|
|
6649
|
+
});
|
|
6650
|
+
};
|
|
6651
|
+
replaceSession = async (sessionRecord) => {
|
|
6652
|
+
await this.persistSession(sessionRecord, {
|
|
6653
|
+
preserveExistingMetadata: false
|
|
6654
|
+
});
|
|
6655
|
+
};
|
|
6656
|
+
deleteSession = async (sessionId) => {
|
|
6425
6657
|
const existing = await this.getSession(sessionId);
|
|
6426
6658
|
if (!existing) {
|
|
6427
6659
|
return null;
|
|
@@ -6429,104 +6661,8 @@ var NextclawAgentSessionStore = class {
|
|
|
6429
6661
|
this.sessionManager.delete(sessionId);
|
|
6430
6662
|
this.options.onSessionUpdated?.(sessionId);
|
|
6431
6663
|
return existing;
|
|
6432
|
-
}
|
|
6433
|
-
};
|
|
6434
|
-
|
|
6435
|
-
// src/cli/commands/ncp/ncp-subagent-completion-message.ts
|
|
6436
|
-
import {
|
|
6437
|
-
NCP_INTERNAL_VISIBILITY_METADATA_KEY
|
|
6438
|
-
} from "@nextclaw/ncp";
|
|
6439
|
-
function escapeXml(value) {
|
|
6440
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6441
|
-
}
|
|
6442
|
-
function buildSubagentCompletionFollowUpMessage(params) {
|
|
6443
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6444
|
-
const statusLabel = params.status === "ok" ? "completed" : "failed";
|
|
6445
|
-
return {
|
|
6446
|
-
id: `${params.sessionId}:system:subagent-follow-up:${timestamp}`,
|
|
6447
|
-
sessionId: params.sessionId,
|
|
6448
|
-
role: "user",
|
|
6449
|
-
status: "final",
|
|
6450
|
-
timestamp,
|
|
6451
|
-
parts: [
|
|
6452
|
-
{
|
|
6453
|
-
type: "text",
|
|
6454
|
-
text: [
|
|
6455
|
-
"<task-notification>",
|
|
6456
|
-
"<source>subagent_completion</source>",
|
|
6457
|
-
`<label>${escapeXml(params.label)}</label>`,
|
|
6458
|
-
`<status>${statusLabel}</status>`,
|
|
6459
|
-
`<delegated-task>${escapeXml(params.task)}</delegated-task>`,
|
|
6460
|
-
`<result>${escapeXml(params.result)}</result>`,
|
|
6461
|
-
"<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>",
|
|
6462
|
-
"</task-notification>"
|
|
6463
|
-
].join("\n")
|
|
6464
|
-
}
|
|
6465
|
-
],
|
|
6466
|
-
metadata: {
|
|
6467
|
-
[NCP_INTERNAL_VISIBILITY_METADATA_KEY]: "hidden",
|
|
6468
|
-
system_event_kind: "subagent_completion_follow_up",
|
|
6469
|
-
subagent_label: params.label,
|
|
6470
|
-
subagent_status: params.status,
|
|
6471
|
-
subagent_task: params.task
|
|
6472
|
-
}
|
|
6473
6664
|
};
|
|
6474
|
-
}
|
|
6475
|
-
|
|
6476
|
-
// src/cli/commands/ncp/ncp-subagent-completion-follow-up.ts
|
|
6477
|
-
async function consumeAgentRun(events) {
|
|
6478
|
-
for await (const _event of events) {
|
|
6479
|
-
void _event;
|
|
6480
|
-
}
|
|
6481
|
-
}
|
|
6482
|
-
async function sleep(ms) {
|
|
6483
|
-
await new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
6484
|
-
}
|
|
6485
|
-
async function waitForSessionToBecomeIdle(params) {
|
|
6486
|
-
const timeoutMs = params.timeoutMs ?? 15e3;
|
|
6487
|
-
const intervalMs = params.intervalMs ?? 150;
|
|
6488
|
-
const deadline = Date.now() + timeoutMs;
|
|
6489
|
-
while (Date.now() <= deadline) {
|
|
6490
|
-
const summary = await params.backend.getSession(params.sessionId);
|
|
6491
|
-
if (!summary) {
|
|
6492
|
-
return false;
|
|
6493
|
-
}
|
|
6494
|
-
if (summary.status !== "running") {
|
|
6495
|
-
return true;
|
|
6496
|
-
}
|
|
6497
|
-
await sleep(intervalMs);
|
|
6498
|
-
}
|
|
6499
|
-
return false;
|
|
6500
|
-
}
|
|
6501
|
-
async function persistSubagentCompletionAndResumeParent(params) {
|
|
6502
|
-
const isIdle = await waitForSessionToBecomeIdle({
|
|
6503
|
-
backend: params.backend,
|
|
6504
|
-
sessionId: params.completion.sessionId
|
|
6505
|
-
});
|
|
6506
|
-
if (!isIdle) {
|
|
6507
|
-
return;
|
|
6508
|
-
}
|
|
6509
|
-
if (params.completion.toolCallId?.trim()) {
|
|
6510
|
-
await params.backend.updateToolCallResult(
|
|
6511
|
-
params.completion.sessionId,
|
|
6512
|
-
params.completion.toolCallId.trim(),
|
|
6513
|
-
{
|
|
6514
|
-
kind: "nextclaw.subagent_run",
|
|
6515
|
-
runId: params.completion.runId,
|
|
6516
|
-
label: params.completion.label,
|
|
6517
|
-
task: params.completion.task,
|
|
6518
|
-
status: params.completion.status === "ok" ? "completed" : "failed",
|
|
6519
|
-
result: params.completion.result
|
|
6520
|
-
}
|
|
6521
|
-
);
|
|
6522
|
-
}
|
|
6523
|
-
await consumeAgentRun(
|
|
6524
|
-
params.backend.send({
|
|
6525
|
-
sessionId: params.completion.sessionId,
|
|
6526
|
-
message: buildSubagentCompletionFollowUpMessage(params.completion)
|
|
6527
|
-
})
|
|
6528
|
-
);
|
|
6529
|
-
}
|
|
6665
|
+
};
|
|
6530
6666
|
|
|
6531
6667
|
// src/cli/commands/ncp/provider-manager-ncp-llm-api.ts
|
|
6532
6668
|
import { parseThinkingLevel as parseThinkingLevel3 } from "@nextclaw/core";
|
|
@@ -6645,6 +6781,569 @@ var ProviderManagerNcpLLMApi = class {
|
|
|
6645
6781
|
}
|
|
6646
6782
|
};
|
|
6647
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
|
+
|
|
6648
7347
|
// src/cli/commands/ncp/ui-ncp-runtime-registry.ts
|
|
6649
7348
|
import { toDisposable } from "@nextclaw/core";
|
|
6650
7349
|
var DEFAULT_UI_NCP_RUNTIME_KIND = "native";
|
|
@@ -6799,10 +7498,13 @@ async function createMcpRuntimeSupport(getConfig) {
|
|
|
6799
7498
|
console.warn(`[mcp] Failed to warm ${warmResult.name}: ${warmResult.error}`);
|
|
6800
7499
|
}
|
|
6801
7500
|
}
|
|
7501
|
+
},
|
|
7502
|
+
dispose: async () => {
|
|
7503
|
+
await mcpRegistryService.close();
|
|
6802
7504
|
}
|
|
6803
7505
|
};
|
|
6804
7506
|
}
|
|
6805
|
-
function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore,
|
|
7507
|
+
function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore, sessionCreationService, sessionRequestBroker) {
|
|
6806
7508
|
return ({
|
|
6807
7509
|
stateManager,
|
|
6808
7510
|
sessionMetadata,
|
|
@@ -6828,24 +7530,8 @@ function createNativeRuntimeFactory(params, mcpToolRegistryAdapter, assetStore,
|
|
|
6828
7530
|
gatewayController: params.gatewayController,
|
|
6829
7531
|
getConfig: params.getConfig,
|
|
6830
7532
|
getExtensionRegistry: params.getExtensionRegistry,
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
if (!backend) {
|
|
6834
|
-
throw new Error("NCP backend is not ready for subagent completion persistence.");
|
|
6835
|
-
}
|
|
6836
|
-
await persistSubagentCompletionAndResumeParent({
|
|
6837
|
-
backend,
|
|
6838
|
-
completion: {
|
|
6839
|
-
sessionId: completion.sessionId,
|
|
6840
|
-
runId: completion.runId,
|
|
6841
|
-
toolCallId: completion.toolCallId,
|
|
6842
|
-
label: completion.label,
|
|
6843
|
-
task: completion.task,
|
|
6844
|
-
result: completion.result,
|
|
6845
|
-
status: completion.status
|
|
6846
|
-
}
|
|
6847
|
-
});
|
|
6848
|
-
},
|
|
7533
|
+
sessionCreationService,
|
|
7534
|
+
sessionRequestBroker,
|
|
6849
7535
|
getAdditionalTools: (context) => [
|
|
6850
7536
|
...createAssetTools({
|
|
6851
7537
|
assetStore
|
|
@@ -6882,25 +7568,29 @@ function createCodexAwareRuntimeFactory(params) {
|
|
|
6882
7568
|
function resolveRegisteredRuntimeFactory(params) {
|
|
6883
7569
|
return params.registration.kind === CODEX_RUNTIME_KIND ? createCodexAwareRuntimeFactory(params) : params.registration.createRuntime;
|
|
6884
7570
|
}
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
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) => {
|
|
6890
7580
|
const nextSnapshotKey = buildPluginRuntimeSnapshotKey(extensionRegistry);
|
|
6891
|
-
if (nextSnapshotKey === pluginRuntimeSnapshotKey) {
|
|
7581
|
+
if (nextSnapshotKey === this.pluginRuntimeSnapshotKey) {
|
|
6892
7582
|
return;
|
|
6893
7583
|
}
|
|
6894
|
-
pluginRuntimeSnapshotKey = nextSnapshotKey;
|
|
6895
|
-
for (const scope of pluginRuntimeScopes.values()) {
|
|
7584
|
+
this.pluginRuntimeSnapshotKey = nextSnapshotKey;
|
|
7585
|
+
for (const scope of this.pluginRuntimeScopes.values()) {
|
|
6896
7586
|
scope.dispose();
|
|
6897
7587
|
}
|
|
6898
|
-
pluginRuntimeScopes.clear();
|
|
7588
|
+
this.pluginRuntimeScopes.clear();
|
|
6899
7589
|
for (const registration of extensionRegistry?.ncpAgentRuntimes ?? []) {
|
|
6900
7590
|
const pluginId = registration.pluginId.trim() || registration.kind;
|
|
6901
|
-
const scope = pluginRuntimeScopes.get(pluginId) ?? new DisposableStore();
|
|
6902
|
-
pluginRuntimeScopes.set(pluginId, scope);
|
|
6903
|
-
scope.add(
|
|
7591
|
+
const scope = this.pluginRuntimeScopes.get(pluginId) ?? new DisposableStore();
|
|
7592
|
+
this.pluginRuntimeScopes.set(pluginId, scope);
|
|
7593
|
+
scope.add(this.runtimeRegistry.register({
|
|
6904
7594
|
kind: registration.kind,
|
|
6905
7595
|
label: registration.label,
|
|
6906
7596
|
createRuntime: resolveRegisteredRuntimeFactory({
|
|
@@ -6910,17 +7600,23 @@ function createPluginRuntimeRegistrationController(params) {
|
|
|
6910
7600
|
}));
|
|
6911
7601
|
}
|
|
6912
7602
|
};
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
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();
|
|
6921
7614
|
}
|
|
7615
|
+
this.pluginRuntimeScopes.clear();
|
|
7616
|
+
this.activeExtensionRegistry = void 0;
|
|
7617
|
+
this.pluginRuntimeSnapshotKey = "";
|
|
6922
7618
|
};
|
|
6923
|
-
}
|
|
7619
|
+
};
|
|
6924
7620
|
function createUiNcpAgentHandle(params) {
|
|
6925
7621
|
return {
|
|
6926
7622
|
basePath: "/api/ncp/agent",
|
|
@@ -6942,7 +7638,8 @@ function createUiNcpAgentHandle(params) {
|
|
|
6942
7638
|
resolveContentPath: (uri) => params.assetStore.resolveContentPath(uri)
|
|
6943
7639
|
},
|
|
6944
7640
|
applyExtensionRegistry: params.applyExtensionRegistry,
|
|
6945
|
-
applyMcpConfig: params.applyMcpConfig
|
|
7641
|
+
applyMcpConfig: params.applyMcpConfig,
|
|
7642
|
+
dispose: params.dispose
|
|
6946
7643
|
};
|
|
6947
7644
|
}
|
|
6948
7645
|
async function createUiNcpAgent(params) {
|
|
@@ -6950,28 +7647,38 @@ async function createUiNcpAgent(params) {
|
|
|
6950
7647
|
onSessionUpdated: params.onSessionUpdated
|
|
6951
7648
|
});
|
|
6952
7649
|
const runtimeRegistry = new UiNcpRuntimeRegistry();
|
|
6953
|
-
const { toolRegistryAdapter, applyMcpConfig } = await createMcpRuntimeSupport(params.getConfig);
|
|
7650
|
+
const { toolRegistryAdapter, applyMcpConfig, dispose: disposeMcpRuntimeSupport } = await createMcpRuntimeSupport(params.getConfig);
|
|
6954
7651
|
const assetStore = new LocalAssetStore({
|
|
6955
7652
|
rootDir: join5(getDataDir7(), "assets")
|
|
6956
7653
|
});
|
|
6957
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
|
+
);
|
|
6958
7666
|
const createNativeRuntime = createNativeRuntimeFactory(
|
|
6959
7667
|
params,
|
|
6960
7668
|
toolRegistryAdapter,
|
|
6961
7669
|
assetStore,
|
|
6962
|
-
|
|
7670
|
+
sessionCreationService,
|
|
7671
|
+
sessionRequestBroker
|
|
6963
7672
|
);
|
|
6964
7673
|
runtimeRegistry.register({
|
|
6965
7674
|
kind: "native",
|
|
6966
7675
|
label: "Native",
|
|
6967
7676
|
createRuntime: createNativeRuntime
|
|
6968
7677
|
});
|
|
6969
|
-
const pluginRuntimeRegistrationController =
|
|
7678
|
+
const pluginRuntimeRegistrationController = new PluginRuntimeRegistrationController(
|
|
6970
7679
|
runtimeRegistry,
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
createNativeRuntime
|
|
6974
|
-
});
|
|
7680
|
+
params.getExtensionRegistry
|
|
7681
|
+
);
|
|
6975
7682
|
pluginRuntimeRegistrationController.refreshPluginRuntimeRegistrations();
|
|
6976
7683
|
backend = new DefaultNcpAgentBackend({
|
|
6977
7684
|
endpointId: "nextclaw-ui-agent",
|
|
@@ -6989,6 +7696,11 @@ async function createUiNcpAgent(params) {
|
|
|
6989
7696
|
refreshPluginRuntimeRegistrations: pluginRuntimeRegistrationController.refreshPluginRuntimeRegistrations,
|
|
6990
7697
|
applyExtensionRegistry: pluginRuntimeRegistrationController.applyExtensionRegistry,
|
|
6991
7698
|
applyMcpConfig,
|
|
7699
|
+
dispose: async () => {
|
|
7700
|
+
pluginRuntimeRegistrationController.dispose();
|
|
7701
|
+
await backend?.stop();
|
|
7702
|
+
await disposeMcpRuntimeSupport();
|
|
7703
|
+
},
|
|
6992
7704
|
assetStore
|
|
6993
7705
|
});
|
|
6994
7706
|
}
|
|
@@ -8177,9 +8889,17 @@ var DEFERRED_NCP_AGENT_UNAVAILABLE = "ncp agent unavailable during startup";
|
|
|
8177
8889
|
function createUnavailableError() {
|
|
8178
8890
|
return new Error(DEFERRED_NCP_AGENT_UNAVAILABLE);
|
|
8179
8891
|
}
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
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 = {
|
|
8183
8903
|
manifest: {
|
|
8184
8904
|
endpointKind: "agent",
|
|
8185
8905
|
endpointId: "nextclaw-ui-agent-deferred",
|
|
@@ -8194,73 +8914,69 @@ function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
|
|
|
8194
8914
|
deferred: true
|
|
8195
8915
|
}
|
|
8196
8916
|
},
|
|
8197
|
-
async
|
|
8198
|
-
await activeAgent?.agentClientEndpoint.start();
|
|
8917
|
+
start: async () => {
|
|
8918
|
+
await this.activeAgent?.agentClientEndpoint.start();
|
|
8199
8919
|
},
|
|
8200
|
-
async
|
|
8201
|
-
await activeAgent?.agentClientEndpoint.stop();
|
|
8920
|
+
stop: async () => {
|
|
8921
|
+
await this.activeAgent?.agentClientEndpoint.stop();
|
|
8202
8922
|
},
|
|
8203
|
-
async
|
|
8204
|
-
if (!activeAgent) {
|
|
8923
|
+
emit: async (event) => {
|
|
8924
|
+
if (!this.activeAgent) {
|
|
8205
8925
|
throw createUnavailableError();
|
|
8206
8926
|
}
|
|
8207
|
-
await activeAgent.agentClientEndpoint.emit(event);
|
|
8927
|
+
await this.activeAgent.agentClientEndpoint.emit(event);
|
|
8208
8928
|
},
|
|
8209
|
-
subscribe(listener) {
|
|
8210
|
-
if (!activeAgent) {
|
|
8929
|
+
subscribe: (listener) => {
|
|
8930
|
+
if (!this.activeAgent) {
|
|
8211
8931
|
return () => void 0;
|
|
8212
8932
|
}
|
|
8213
|
-
return activeAgent.agentClientEndpoint.subscribe(listener);
|
|
8933
|
+
return this.activeAgent.agentClientEndpoint.subscribe(listener);
|
|
8214
8934
|
},
|
|
8215
|
-
async
|
|
8216
|
-
if (!activeAgent) {
|
|
8935
|
+
send: async (envelope) => {
|
|
8936
|
+
if (!this.activeAgent) {
|
|
8217
8937
|
throw createUnavailableError();
|
|
8218
8938
|
}
|
|
8219
|
-
await activeAgent.agentClientEndpoint.send(envelope);
|
|
8939
|
+
await this.activeAgent.agentClientEndpoint.send(envelope);
|
|
8220
8940
|
},
|
|
8221
|
-
async
|
|
8222
|
-
if (!activeAgent) {
|
|
8941
|
+
stream: async (payload) => {
|
|
8942
|
+
if (!this.activeAgent) {
|
|
8223
8943
|
throw createUnavailableError();
|
|
8224
8944
|
}
|
|
8225
|
-
await activeAgent.agentClientEndpoint.stream(payload);
|
|
8945
|
+
await this.activeAgent.agentClientEndpoint.stream(payload);
|
|
8226
8946
|
},
|
|
8227
|
-
async
|
|
8228
|
-
if (!activeAgent) {
|
|
8947
|
+
abort: async (payload) => {
|
|
8948
|
+
if (!this.activeAgent) {
|
|
8229
8949
|
throw createUnavailableError();
|
|
8230
8950
|
}
|
|
8231
|
-
await activeAgent.agentClientEndpoint.abort(payload);
|
|
8951
|
+
await this.activeAgent.agentClientEndpoint.abort(payload);
|
|
8232
8952
|
}
|
|
8233
8953
|
};
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
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;
|
|
8237
8960
|
};
|
|
8238
|
-
|
|
8239
|
-
activeAgent = null;
|
|
8240
|
-
agent.basePath = basePath;
|
|
8241
|
-
agent.streamProvider = void 0;
|
|
8242
|
-
agent.listSessionTypes = void 0;
|
|
8243
|
-
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;
|
|
8244
8967
|
};
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
agent.streamProvider = nextAgent.streamProvider;
|
|
8251
|
-
agent.listSessionTypes = nextAgent.listSessionTypes;
|
|
8252
|
-
agent.assetApi = nextAgent.assetApi;
|
|
8253
|
-
},
|
|
8254
|
-
clear,
|
|
8255
|
-
async close() {
|
|
8256
|
-
const current = activeAgent;
|
|
8257
|
-
clear();
|
|
8258
|
-
await current?.agentClientEndpoint.stop();
|
|
8259
|
-
},
|
|
8260
|
-
isReady() {
|
|
8261
|
-
return activeAgent !== null;
|
|
8262
|
-
}
|
|
8968
|
+
close = async () => {
|
|
8969
|
+
const current = this.activeAgent;
|
|
8970
|
+
this.clear();
|
|
8971
|
+
await current?.agentClientEndpoint.stop();
|
|
8972
|
+
await current?.dispose?.();
|
|
8263
8973
|
};
|
|
8974
|
+
isReady = () => {
|
|
8975
|
+
return this.activeAgent !== null;
|
|
8976
|
+
};
|
|
8977
|
+
};
|
|
8978
|
+
function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
|
|
8979
|
+
return new DeferredUiNcpAgentControllerOwner(basePath);
|
|
8264
8980
|
}
|
|
8265
8981
|
|
|
8266
8982
|
// src/cli/commands/service-gateway-startup.ts
|
|
@@ -8448,7 +9164,7 @@ var UiSessionService = class {
|
|
|
8448
9164
|
onSessionUpdated: options.onSessionUpdated
|
|
8449
9165
|
});
|
|
8450
9166
|
}
|
|
8451
|
-
async
|
|
9167
|
+
listSessions = async (options) => {
|
|
8452
9168
|
const sessions = await this.sessionStore.listSessions();
|
|
8453
9169
|
return applyLimit(
|
|
8454
9170
|
sessions.map(
|
|
@@ -8462,15 +9178,15 @@ var UiSessionService = class {
|
|
|
8462
9178
|
),
|
|
8463
9179
|
options?.limit
|
|
8464
9180
|
);
|
|
8465
|
-
}
|
|
8466
|
-
async
|
|
9181
|
+
};
|
|
9182
|
+
listSessionMessages = async (sessionId, options) => {
|
|
8467
9183
|
const session = await this.sessionStore.getSession(sessionId);
|
|
8468
9184
|
if (!session) {
|
|
8469
9185
|
return [];
|
|
8470
9186
|
}
|
|
8471
9187
|
return applyLimit(session.messages.map((message) => structuredClone(message)), options?.limit);
|
|
8472
|
-
}
|
|
8473
|
-
async
|
|
9188
|
+
};
|
|
9189
|
+
getSession = async (sessionId) => {
|
|
8474
9190
|
const session = await this.sessionStore.getSession(sessionId);
|
|
8475
9191
|
if (!session) {
|
|
8476
9192
|
return null;
|
|
@@ -8482,26 +9198,23 @@ var UiSessionService = class {
|
|
|
8482
9198
|
status: "idle",
|
|
8483
9199
|
metadata: session.metadata
|
|
8484
9200
|
});
|
|
8485
|
-
}
|
|
8486
|
-
async
|
|
9201
|
+
};
|
|
9202
|
+
updateSession = async (sessionId, patch) => {
|
|
8487
9203
|
const session = await this.sessionStore.getSession(sessionId);
|
|
8488
|
-
|
|
8489
|
-
return null;
|
|
8490
|
-
}
|
|
8491
|
-
await this.sessionStore.saveSession({
|
|
9204
|
+
await this.sessionStore.replaceSession({
|
|
8492
9205
|
sessionId,
|
|
8493
|
-
messages: session.messages.map((message) => structuredClone(message)),
|
|
9206
|
+
messages: session ? session.messages.map((message) => structuredClone(message)) : [],
|
|
8494
9207
|
updatedAt: now(),
|
|
8495
9208
|
metadata: buildUpdatedMetadata({
|
|
8496
|
-
existingMetadata: session
|
|
9209
|
+
existingMetadata: session?.metadata,
|
|
8497
9210
|
patch
|
|
8498
9211
|
})
|
|
8499
9212
|
});
|
|
8500
9213
|
return await this.getSession(sessionId);
|
|
8501
|
-
}
|
|
8502
|
-
async
|
|
9214
|
+
};
|
|
9215
|
+
deleteSession = async (sessionId) => {
|
|
8503
9216
|
await this.sessionStore.deleteSession(sessionId);
|
|
8504
|
-
}
|
|
9217
|
+
};
|
|
8505
9218
|
};
|
|
8506
9219
|
|
|
8507
9220
|
// src/cli/commands/service-deferred-ncp-session-service.ts
|
|
@@ -9006,7 +9719,7 @@ function readStringList(value) {
|
|
|
9006
9719
|
}
|
|
9007
9720
|
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
9008
9721
|
}
|
|
9009
|
-
function
|
|
9722
|
+
function readOptionalString6(value) {
|
|
9010
9723
|
if (typeof value !== "string") {
|
|
9011
9724
|
return void 0;
|
|
9012
9725
|
}
|
|
@@ -9016,10 +9729,10 @@ function readOptionalString2(value) {
|
|
|
9016
9729
|
function resolvePluginRuntimeAttachments(ctx) {
|
|
9017
9730
|
const mediaPaths = readStringList(ctx.MediaPaths);
|
|
9018
9731
|
const mediaUrls = readStringList(ctx.MediaUrls);
|
|
9019
|
-
const fallbackPath =
|
|
9020
|
-
const fallbackUrl =
|
|
9732
|
+
const fallbackPath = readOptionalString6(ctx.MediaPath);
|
|
9733
|
+
const fallbackUrl = readOptionalString6(ctx.MediaUrl);
|
|
9021
9734
|
const mediaTypes = readStringList(ctx.MediaTypes);
|
|
9022
|
-
const fallbackType =
|
|
9735
|
+
const fallbackType = readOptionalString6(ctx.MediaType);
|
|
9023
9736
|
const entryCount = Math.max(
|
|
9024
9737
|
mediaPaths.length,
|
|
9025
9738
|
mediaUrls.length,
|