nextclaw 0.17.1 → 0.17.3
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 +394 -194
- package/package.json +10 -10
- package/resources/USAGE.md +48 -30
- package/ui-dist/assets/ChannelsList-uKmkpD25.js +8 -0
- package/ui-dist/assets/ChatPage-CslhBPfT.js +43 -0
- package/ui-dist/assets/{DocBrowser-DxdSujSc.js → DocBrowser-C7-1sXqo.js} +1 -1
- package/ui-dist/assets/DocBrowser-DQjtSsY3.js +1 -0
- package/ui-dist/assets/{DocBrowserContext-CQ-8jMha.js → DocBrowserContext-DN5tjUoS.js} +1 -1
- package/ui-dist/assets/{LogoBadge-D-KQIN4U.js → LogoBadge-DDS1sU_U.js} +1 -1
- package/ui-dist/assets/MarketplacePage-BZQW70ti.js +1 -0
- package/ui-dist/assets/{MarketplacePage-CNeXwz7q.js → MarketplacePage-DE0QjYVv.js} +1 -1
- package/ui-dist/assets/{McpMarketplacePage-BGv2wwuk.js → McpMarketplacePage-CeLvv1xy.js} +1 -1
- package/ui-dist/assets/ModelConfig-D1JtGtQv.js +1 -0
- package/ui-dist/assets/ProviderScopedModelInput-SAJH6nkC.js +1 -0
- package/ui-dist/assets/{ProvidersList-BXeGOw0M.js → ProvidersList-1rKi3aQT.js} +1 -1
- package/ui-dist/assets/{RemoteAccessPage-vlBOKnMP.js → RemoteAccessPage-bIAKxDky.js} +1 -1
- package/ui-dist/assets/RuntimeConfig-BTk9319O.js +1 -0
- package/ui-dist/assets/SearchConfig-EjeszXbv.js +1 -0
- package/ui-dist/assets/{SecretsConfig-Jduzy42Q.js → SecretsConfig-cnAXvREZ.js} +1 -1
- package/ui-dist/assets/{SessionsConfig-CIwqHwvE.js → SessionsConfig-BIXiDaK2.js} +1 -1
- package/ui-dist/assets/{book-open-FnK2xCQd.js → book-open-DvWqOode.js} +1 -1
- package/ui-dist/assets/chat-session-display-D4bYa0b8.js +1 -0
- package/ui-dist/assets/{chunk-JZWAC4HX-B5l0hr_u.js → chunk-JZWAC4HX-CxfKRD7X.js} +1 -1
- package/ui-dist/assets/{config-JKmXfZ3q.js → config-BeGwf2Ao.js} +1 -1
- package/ui-dist/assets/{createLucideIcon-o1WWhwhd.js → createLucideIcon-C7MmdIX3.js} +1 -1
- package/ui-dist/assets/{dist-C_moWYv7.js → dist-B6VMuIQN.js} +1 -1
- package/ui-dist/assets/{dist-DazA6Wd_.js → dist-RWNFhxvR.js} +1 -1
- package/ui-dist/assets/{external-link-BKje3SiD.js → external-link-U86Acd1t.js} +1 -1
- package/ui-dist/assets/{hash-DfW4DT8O.js → hash-D-OVfV3Z.js} +1 -1
- package/ui-dist/assets/i18n-hM3v-3YG.js +1 -0
- package/ui-dist/assets/{index-BuTtTFjM.js → index-8XNPYwJu.js} +3 -3
- package/ui-dist/assets/index-CpxuJa9o.css +1 -0
- package/ui-dist/assets/{label-BzDWmdOe.js → label-CHJ1ATds.js} +1 -1
- package/ui-dist/assets/loader-circle-C8cpaL0w.js +1 -0
- package/ui-dist/assets/{logos-CTLlde_T.js → logos-U1_qDA3U.js} +1 -1
- package/ui-dist/assets/{page-layout-BagR3t59.js → page-layout-Z1klaUFW.js} +1 -1
- package/ui-dist/assets/plus-CrkO1kob.js +1 -0
- package/ui-dist/assets/{popover-5DWhNfd4.js → popover-xWbqMnIN.js} +1 -1
- package/ui-dist/assets/{react-C3yu5yge.js → react-3YE87-lE.js} +1 -1
- package/ui-dist/assets/{refresh-ccw-BAJf-h7w.js → refresh-ccw-JQh1lwq-.js} +1 -1
- package/ui-dist/assets/{save-aa6z4GJL.js → save-4VRlzkii.js} +1 -1
- package/ui-dist/assets/search-EX-Papzl.js +1 -0
- package/ui-dist/assets/{security-config-BrmOzvpp.js → security-config-CGazBahs.js} +1 -1
- package/ui-dist/assets/{select-BHJPiJWt.js → select-DF-AUoie.js} +1 -1
- package/ui-dist/assets/skeleton-B0mmt1vo.js +1 -0
- package/ui-dist/assets/{status-dot-DUwsTIdv.js → status-dot-Bq_8Ojvv.js} +1 -1
- package/ui-dist/assets/{switch-B6nCfcOB.js → switch-D7JF_RZ-.js} +1 -1
- package/ui-dist/assets/{tabs-custom-B57SMElx.js → tabs-custom-CLksZ2bO.js} +1 -1
- package/ui-dist/assets/{trash-2-CrjYH5ok.js → trash-2-VV8jvziy.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-RlLbo0NU.js → useConfirmDialog-D6HxybcM.js} +1 -1
- package/ui-dist/assets/{useMutation-oTTWXgLG.js → useMutation-DBTWPbTg.js} +1 -1
- package/ui-dist/assets/x-B4sxJkGY.js +1 -0
- package/ui-dist/index.html +18 -18
- package/ui-dist/assets/ChannelsList-BXuFNDBh.js +0 -8
- package/ui-dist/assets/ChatPage-CEuFBJgC.js +0 -43
- package/ui-dist/assets/DocBrowser-C8b2uPgL.js +0 -1
- package/ui-dist/assets/MarketplacePage-BGPF2sDu.js +0 -1
- package/ui-dist/assets/ModelConfig-CoNm556G.js +0 -1
- package/ui-dist/assets/RuntimeConfig-CEkrhzg7.js +0 -1
- package/ui-dist/assets/SearchConfig-BK0dn4Zp.js +0 -1
- package/ui-dist/assets/chat-session-display-BD_AN71I.js +0 -1
- package/ui-dist/assets/i18n-BK1w-oBy.js +0 -1
- package/ui-dist/assets/index-DaR9igPC.css +0 -1
- package/ui-dist/assets/loader-circle-DdZPxBUz.js +0 -1
- package/ui-dist/assets/plus-DP2PSCPO.js +0 -1
- package/ui-dist/assets/provider-models-DJ29qHuA.js +0 -1
- package/ui-dist/assets/search-pD6ZwQYF.js +0 -1
- package/ui-dist/assets/skeleton-D6kCk9Y6.js +0 -1
- package/ui-dist/assets/x-CTIQHUuD.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as NextclawCore from "@nextclaw/core";
|
|
4
|
-
import { APP_NAME, APP_TAGLINE, AgentLoop, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, ChannelManager, CommandRegistry, ConfigSchema, ContextBuilder, CronService, CronTool, DEFAULT_WORKSPACE_DIR, DEFAULT_WORKSPACE_PATH, DisposableStore, EditFileTool, ExecTool, ExtensionToolAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, NativeAgentEngine, ProviderManager, ReadFileTool, SessionsHistoryTool, SessionsListTool,
|
|
4
|
+
import { APP_NAME, APP_TAGLINE, AgentLoop, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, ChannelManager, CommandRegistry, ConfigSchema, ContextBuilder, CronService, CronTool, DEFAULT_WORKSPACE_DIR, DEFAULT_WORKSPACE_PATH, DisposableStore, EditFileTool, ExecTool, ExtensionToolAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, NativeAgentEngine, ProviderManager, ReadFileTool, SessionsHistoryTool, SessionsListTool, SkillsLoader, Tool, ToolRegistry, WebFetchTool, WebSearchTool, WriteFileTool, buildConfigSchema, buildReloadPlan, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createExternalCommandEnv, diffConfigPaths, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getPackageVersion, getWorkspacePath, hasSecretRef, loadConfig, normalizeInlineSecretRefs, parseAgentScopedSessionKey, parseThinkingLevel, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, toDisposable, updateAgentProfile } from "@nextclaw/core";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { RemoteConnector, RemotePlatformClient, RemoteRuntimeActions, RemoteServiceModule, RemoteStatusStore, buildConfiguredRemoteState, readPlatformSessionTokenState, registerRemoteCommands, resolveRemoteStatusSnapshot } from "@nextclaw/remote";
|
|
7
7
|
import { addPluginLoadPath, buildPluginStatusReport, disablePluginInConfig, discoverPluginStatusReport, enablePluginInConfig, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadOpenClawPluginsProgressively, mergePluginConfigView, recordPluginInstall, resolvePluginChannelMessageToolHints, resolveUninstallDirectoryTargets, setPluginRuntimeBridge, startPluginChannelGateways, stopPluginChannelGateways, toPluginConfigView, toPluginConfigView as toPluginConfigView$1, uninstallPlugin } from "@nextclaw/openclaw-compat";
|
|
@@ -983,7 +983,6 @@ const RESERVED_TOOL_NAMES = [
|
|
|
983
983
|
"spawn",
|
|
984
984
|
"sessions_list",
|
|
985
985
|
"sessions_history",
|
|
986
|
-
"sessions_send",
|
|
987
986
|
"memory_search",
|
|
988
987
|
"memory_get",
|
|
989
988
|
"subagents",
|
|
@@ -2487,41 +2486,79 @@ var ChannelCommands = class {
|
|
|
2487
2486
|
function createCronService() {
|
|
2488
2487
|
return new CronService(join(getDataDir(), "cron", "jobs.json"));
|
|
2489
2488
|
}
|
|
2489
|
+
function readTrimmed(value) {
|
|
2490
|
+
if (typeof value !== "string") return;
|
|
2491
|
+
return value.trim() || void 0;
|
|
2492
|
+
}
|
|
2490
2493
|
function toSchedule(opts) {
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2494
|
+
const every = readTrimmed(opts.every);
|
|
2495
|
+
const cron = readTrimmed(opts.cron);
|
|
2496
|
+
const at = readTrimmed(opts.at);
|
|
2497
|
+
if ([
|
|
2498
|
+
every,
|
|
2499
|
+
cron,
|
|
2500
|
+
at
|
|
2501
|
+
].filter((value) => value !== void 0).length !== 1) return { error: "Error: Must specify exactly one of --every, --cron, or --at" };
|
|
2502
|
+
if (every) {
|
|
2503
|
+
const everySeconds = Number(every);
|
|
2504
|
+
if (!Number.isFinite(everySeconds) || everySeconds <= 0) return { error: "Error: --every must be a positive number of seconds" };
|
|
2505
|
+
return { schedule: {
|
|
2506
|
+
kind: "every",
|
|
2507
|
+
everyMs: everySeconds * 1e3
|
|
2508
|
+
} };
|
|
2509
|
+
}
|
|
2510
|
+
if (cron) return { schedule: {
|
|
2496
2511
|
kind: "cron",
|
|
2497
|
-
expr:
|
|
2498
|
-
};
|
|
2499
|
-
|
|
2512
|
+
expr: cron
|
|
2513
|
+
} };
|
|
2514
|
+
const atMs = Date.parse(String(at));
|
|
2515
|
+
if (!Number.isFinite(atMs)) return { error: "Error: --at must be a valid ISO datetime" };
|
|
2516
|
+
return { schedule: {
|
|
2500
2517
|
kind: "at",
|
|
2501
|
-
atMs
|
|
2502
|
-
};
|
|
2503
|
-
|
|
2518
|
+
atMs
|
|
2519
|
+
} };
|
|
2520
|
+
}
|
|
2521
|
+
function createCronCreateRequest(opts) {
|
|
2522
|
+
const name = readTrimmed(opts.name);
|
|
2523
|
+
const message = readTrimmed(opts.message);
|
|
2524
|
+
if (!name || !message) return { error: "Error: name and message are required" };
|
|
2525
|
+
const schedule = toSchedule(opts);
|
|
2526
|
+
if (!schedule.schedule) return { error: schedule.error ?? "Error: Must specify --every, --cron, or --at" };
|
|
2527
|
+
return { request: {
|
|
2528
|
+
name,
|
|
2529
|
+
message,
|
|
2530
|
+
schedule: schedule.schedule,
|
|
2531
|
+
agentId: readTrimmed(opts.agent),
|
|
2532
|
+
deliver: Boolean(opts.deliver),
|
|
2533
|
+
channel: readTrimmed(opts.channel),
|
|
2534
|
+
to: readTrimmed(opts.to),
|
|
2535
|
+
accountId: readTrimmed(opts.account)
|
|
2536
|
+
} };
|
|
2504
2537
|
}
|
|
2505
2538
|
var CronLocalService = class {
|
|
2506
2539
|
list = (all) => {
|
|
2507
2540
|
return createCronService().listJobs(all);
|
|
2508
2541
|
};
|
|
2542
|
+
addRequest = (request) => {
|
|
2543
|
+
return createCronService().addJob({
|
|
2544
|
+
name: request.name,
|
|
2545
|
+
schedule: request.schedule,
|
|
2546
|
+
message: request.message,
|
|
2547
|
+
agentId: request.agentId ?? void 0,
|
|
2548
|
+
deliver: request.deliver === true,
|
|
2549
|
+
channel: request.channel ?? void 0,
|
|
2550
|
+
to: request.to ?? void 0,
|
|
2551
|
+
accountId: request.accountId ?? void 0,
|
|
2552
|
+
deleteAfterRun: request.deleteAfterRun === true
|
|
2553
|
+
});
|
|
2554
|
+
};
|
|
2509
2555
|
add = (opts) => {
|
|
2510
|
-
const
|
|
2511
|
-
if (!
|
|
2556
|
+
const request = createCronCreateRequest(opts);
|
|
2557
|
+
if (!request.request) return {
|
|
2512
2558
|
job: null,
|
|
2513
|
-
error:
|
|
2559
|
+
error: request.error
|
|
2514
2560
|
};
|
|
2515
|
-
return { job:
|
|
2516
|
-
name: opts.name,
|
|
2517
|
-
schedule,
|
|
2518
|
-
message: opts.message,
|
|
2519
|
-
agentId: opts.agent,
|
|
2520
|
-
deliver: Boolean(opts.deliver),
|
|
2521
|
-
channel: opts.channel,
|
|
2522
|
-
to: opts.to,
|
|
2523
|
-
accountId: opts.account
|
|
2524
|
-
}) };
|
|
2561
|
+
return { job: this.addRequest(request.request) };
|
|
2525
2562
|
};
|
|
2526
2563
|
remove = (jobId) => {
|
|
2527
2564
|
return createCronService().removeJob(jobId);
|
|
@@ -2551,9 +2588,11 @@ function printCronJobs(jobs) {
|
|
|
2551
2588
|
//#region src/cli/commands/shared/ui-bridge-api.service.ts
|
|
2552
2589
|
function resolveManagedApiBase() {
|
|
2553
2590
|
const state = readServiceState();
|
|
2554
|
-
if (!state?.
|
|
2591
|
+
if (!state?.pid) return null;
|
|
2555
2592
|
if (!isProcessRunning(state.pid)) return null;
|
|
2556
|
-
return state.
|
|
2593
|
+
if (typeof state.uiUrl === "string" && state.uiUrl.trim().length > 0) return state.uiUrl.replace(/\/+$/, "");
|
|
2594
|
+
if (typeof state.apiUrl === "string" && state.apiUrl.trim().length > 0) return state.apiUrl.replace(/\/api\/?$/, "").replace(/\/+$/, "");
|
|
2595
|
+
return null;
|
|
2557
2596
|
}
|
|
2558
2597
|
var UiBridgeApiClient = class {
|
|
2559
2598
|
cookie;
|
|
@@ -2591,6 +2630,10 @@ var UiBridgeApiClient = class {
|
|
|
2591
2630
|
};
|
|
2592
2631
|
//#endregion
|
|
2593
2632
|
//#region src/cli/commands/cron.ts
|
|
2633
|
+
function readErrorMessage(error) {
|
|
2634
|
+
if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
|
|
2635
|
+
return String(error ?? "unknown error");
|
|
2636
|
+
}
|
|
2594
2637
|
var CronCommands = class {
|
|
2595
2638
|
constructor(local = new CronLocalService()) {
|
|
2596
2639
|
this.local = local;
|
|
@@ -2611,12 +2654,26 @@ var CronCommands = class {
|
|
|
2611
2654
|
printCronJobs(this.local.list(includeDisabled));
|
|
2612
2655
|
};
|
|
2613
2656
|
cronAdd = async (opts) => {
|
|
2614
|
-
const
|
|
2615
|
-
if (!
|
|
2616
|
-
console.error(
|
|
2657
|
+
const request = createCronCreateRequest(opts);
|
|
2658
|
+
if (!request.request) {
|
|
2659
|
+
console.error(request.error ?? "Error: Failed to add job");
|
|
2617
2660
|
return;
|
|
2618
2661
|
}
|
|
2619
|
-
|
|
2662
|
+
const apiClient = this.createApiClient();
|
|
2663
|
+
if (apiClient) try {
|
|
2664
|
+
const data = await apiClient.request({
|
|
2665
|
+
path: "/api/cron",
|
|
2666
|
+
method: "POST",
|
|
2667
|
+
body: request.request
|
|
2668
|
+
});
|
|
2669
|
+
console.log(`✓ Added job '${data.job.name}' (${data.job.id})`);
|
|
2670
|
+
return;
|
|
2671
|
+
} catch (error) {
|
|
2672
|
+
console.error(`Error: ${readErrorMessage(error)}`);
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
const job = this.local.addRequest(request.request);
|
|
2676
|
+
console.log(`✓ Added job '${job.name}' (${job.id})`);
|
|
2620
2677
|
};
|
|
2621
2678
|
cronRemove = async (jobId) => {
|
|
2622
2679
|
const apiClient = this.createApiClient();
|
|
@@ -2628,7 +2685,10 @@ var CronCommands = class {
|
|
|
2628
2685
|
console.log(`✓ Removed job ${jobId}`);
|
|
2629
2686
|
return;
|
|
2630
2687
|
}
|
|
2631
|
-
} catch {
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
console.error(`Error: ${readErrorMessage(error)}`);
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2632
2692
|
if (this.local.remove(jobId)) console.log(`✓ Removed job ${jobId}`);
|
|
2633
2693
|
else console.log(`Job ${jobId} not found`);
|
|
2634
2694
|
};
|
|
@@ -2645,7 +2705,10 @@ var CronCommands = class {
|
|
|
2645
2705
|
console.log(`✓ Job '${data.job.name}' ${opts.disable ? "disabled" : "enabled"}`);
|
|
2646
2706
|
return;
|
|
2647
2707
|
}
|
|
2648
|
-
} catch {
|
|
2708
|
+
} catch (error) {
|
|
2709
|
+
console.error(`Error: ${readErrorMessage(error)}`);
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2649
2712
|
const job = this.local.enable(jobId, enabled);
|
|
2650
2713
|
if (job) console.log(`✓ Job '${job.name}' ${opts.disable ? "disabled" : "enabled"}`);
|
|
2651
2714
|
else console.log(`Job ${jobId} not found`);
|
|
@@ -2660,12 +2723,128 @@ var CronCommands = class {
|
|
|
2660
2723
|
});
|
|
2661
2724
|
console.log(data.executed ? "✓ Job executed" : `Failed to run job ${jobId}`);
|
|
2662
2725
|
return;
|
|
2663
|
-
} catch {
|
|
2726
|
+
} catch (error) {
|
|
2727
|
+
console.error(`Error: ${readErrorMessage(error)}`);
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2664
2730
|
const ok = await this.local.run(jobId, Boolean(opts.force));
|
|
2665
2731
|
console.log(ok ? "✓ Job executed" : `Failed to run job ${jobId}`);
|
|
2666
2732
|
};
|
|
2667
2733
|
};
|
|
2668
2734
|
//#endregion
|
|
2735
|
+
//#region src/cli/commands/ncp/ui-ncp-runtime-registry.ts
|
|
2736
|
+
const DEFAULT_UI_NCP_RUNTIME_KIND = "native";
|
|
2737
|
+
function normalizeRuntimeKind(value) {
|
|
2738
|
+
if (typeof value !== "string") return null;
|
|
2739
|
+
const normalized = value.trim().toLowerCase();
|
|
2740
|
+
return normalized.length > 0 ? normalized : null;
|
|
2741
|
+
}
|
|
2742
|
+
function readRequestedRuntimeKind(sessionMetadata) {
|
|
2743
|
+
return normalizeRuntimeKind(sessionMetadata.runtime) ?? normalizeRuntimeKind(sessionMetadata.session_type) ?? normalizeRuntimeKind(sessionMetadata.sessionType) ?? null;
|
|
2744
|
+
}
|
|
2745
|
+
var UiNcpRuntimeRegistry = class {
|
|
2746
|
+
registrations = /* @__PURE__ */ new Map();
|
|
2747
|
+
constructor(defaultKind = DEFAULT_UI_NCP_RUNTIME_KIND) {
|
|
2748
|
+
this.defaultKind = defaultKind;
|
|
2749
|
+
}
|
|
2750
|
+
register(registration) {
|
|
2751
|
+
const normalizedKind = normalizeRuntimeKind(registration.kind);
|
|
2752
|
+
if (!normalizedKind) throw new Error("ui ncp runtime kind must be a non-empty string");
|
|
2753
|
+
const token = Symbol(normalizedKind);
|
|
2754
|
+
this.registrations.set(normalizedKind, {
|
|
2755
|
+
...registration,
|
|
2756
|
+
kind: normalizedKind,
|
|
2757
|
+
token
|
|
2758
|
+
});
|
|
2759
|
+
return toDisposable(() => {
|
|
2760
|
+
const current = this.registrations.get(normalizedKind);
|
|
2761
|
+
if (!current || current.token !== token) return;
|
|
2762
|
+
this.registrations.delete(normalizedKind);
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
createRuntime(params) {
|
|
2766
|
+
const requestedKind = readRequestedRuntimeKind(params.sessionMetadata) ?? this.defaultKind;
|
|
2767
|
+
const registration = this.registrations.get(requestedKind);
|
|
2768
|
+
if (!registration) throw new Error(`ncp runtime unavailable: ${requestedKind}`);
|
|
2769
|
+
const nextSessionMetadata = {
|
|
2770
|
+
...params.sessionMetadata,
|
|
2771
|
+
session_type: registration.kind
|
|
2772
|
+
};
|
|
2773
|
+
params.setSessionMetadata(nextSessionMetadata);
|
|
2774
|
+
return registration.createRuntime({
|
|
2775
|
+
...params,
|
|
2776
|
+
sessionMetadata: nextSessionMetadata
|
|
2777
|
+
});
|
|
2778
|
+
}
|
|
2779
|
+
async listSessionTypes(params) {
|
|
2780
|
+
const options = await Promise.all([...this.registrations.values()].map(async (registration) => {
|
|
2781
|
+
const descriptor = await registration.describeSessionType?.(params);
|
|
2782
|
+
return {
|
|
2783
|
+
value: registration.kind,
|
|
2784
|
+
label: registration.label,
|
|
2785
|
+
ready: descriptor?.ready ?? true,
|
|
2786
|
+
reason: descriptor?.reason ?? null,
|
|
2787
|
+
reasonMessage: descriptor?.reasonMessage ?? null,
|
|
2788
|
+
recommendedModel: descriptor?.recommendedModel ?? null,
|
|
2789
|
+
cta: descriptor?.cta ?? null,
|
|
2790
|
+
...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
|
|
2791
|
+
};
|
|
2792
|
+
}));
|
|
2793
|
+
return {
|
|
2794
|
+
defaultType: this.defaultKind,
|
|
2795
|
+
options: options.sort((left, right) => {
|
|
2796
|
+
if (left.value === this.defaultKind) return -1;
|
|
2797
|
+
if (right.value === this.defaultKind) return 1;
|
|
2798
|
+
return left.value.localeCompare(right.value);
|
|
2799
|
+
})
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
//#endregion
|
|
2804
|
+
//#region src/cli/commands/agent/agent-runtime.ts
|
|
2805
|
+
function createUnusedRuntime(_params) {
|
|
2806
|
+
throw new Error("runtime creation is not available during runtime listing");
|
|
2807
|
+
}
|
|
2808
|
+
async function listAvailableAgentRuntimes(params) {
|
|
2809
|
+
const config = loadConfig();
|
|
2810
|
+
const pluginRegistry = loadPluginRegistry(config, getWorkspacePath(config.agents.defaults.workspace));
|
|
2811
|
+
logPluginDiagnostics(pluginRegistry);
|
|
2812
|
+
const extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
2813
|
+
const runtimeRegistry = new UiNcpRuntimeRegistry(DEFAULT_UI_NCP_RUNTIME_KIND);
|
|
2814
|
+
const runtimeSourceByKind = /* @__PURE__ */ new Map();
|
|
2815
|
+
runtimeRegistry.register({
|
|
2816
|
+
kind: DEFAULT_UI_NCP_RUNTIME_KIND,
|
|
2817
|
+
label: "Native",
|
|
2818
|
+
createRuntime: createUnusedRuntime
|
|
2819
|
+
});
|
|
2820
|
+
runtimeSourceByKind.set(DEFAULT_UI_NCP_RUNTIME_KIND, { source: "builtin" });
|
|
2821
|
+
for (const registration of extensionRegistry.ncpAgentRuntimes) {
|
|
2822
|
+
runtimeRegistry.register({
|
|
2823
|
+
kind: registration.kind,
|
|
2824
|
+
label: registration.label,
|
|
2825
|
+
createRuntime: registration.createRuntime,
|
|
2826
|
+
describeSessionType: registration.describeSessionType
|
|
2827
|
+
});
|
|
2828
|
+
runtimeSourceByKind.set(registration.kind, {
|
|
2829
|
+
source: "plugin",
|
|
2830
|
+
pluginId: registration.pluginId
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
const listed = await runtimeRegistry.listSessionTypes(params);
|
|
2834
|
+
return {
|
|
2835
|
+
defaultRuntime: listed.defaultType,
|
|
2836
|
+
runtimes: listed.options.map((runtime) => {
|
|
2837
|
+
const source = runtimeSourceByKind.get(runtime.value);
|
|
2838
|
+
return {
|
|
2839
|
+
...runtime,
|
|
2840
|
+
default: runtime.value === listed.defaultType,
|
|
2841
|
+
source: source?.source ?? "plugin",
|
|
2842
|
+
...source?.pluginId ? { pluginId: source.pluginId } : {}
|
|
2843
|
+
};
|
|
2844
|
+
})
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
//#endregion
|
|
2669
2848
|
//#region src/cli/commands/agents.ts
|
|
2670
2849
|
var AgentCommands = class {
|
|
2671
2850
|
constructor(deps) {
|
|
@@ -2684,6 +2863,31 @@ var AgentCommands = class {
|
|
|
2684
2863
|
console.log(` description: ${agent.description ?? "-"}`);
|
|
2685
2864
|
console.log(` home: ${agent.workspace}`);
|
|
2686
2865
|
console.log(` avatar: ${agent.avatar ?? "-"}`);
|
|
2866
|
+
console.log(` runtime: ${agent.runtime ?? "-"}`);
|
|
2867
|
+
}
|
|
2868
|
+
};
|
|
2869
|
+
agentsRuntimes = async (opts = {}) => {
|
|
2870
|
+
const describeMode = opts.probe ? "probe" : "observation";
|
|
2871
|
+
const listed = await listAvailableAgentRuntimes({ describeMode });
|
|
2872
|
+
if (opts.json) {
|
|
2873
|
+
console.log(JSON.stringify({
|
|
2874
|
+
defaultRuntime: listed.defaultRuntime,
|
|
2875
|
+
describeMode,
|
|
2876
|
+
runtimes: listed.runtimes
|
|
2877
|
+
}, null, 2));
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2880
|
+
for (const runtime of listed.runtimes) {
|
|
2881
|
+
const head = runtime.default ? `${runtime.value} (default)` : runtime.value;
|
|
2882
|
+
console.log(head);
|
|
2883
|
+
console.log(` label: ${runtime.label}`);
|
|
2884
|
+
console.log(` source: ${runtime.source}`);
|
|
2885
|
+
if (runtime.pluginId) console.log(` pluginId: ${runtime.pluginId}`);
|
|
2886
|
+
console.log(` ready: ${runtime.ready === false ? "no" : "yes"}`);
|
|
2887
|
+
console.log(` reason: ${runtime.reason ?? "-"}`);
|
|
2888
|
+
console.log(` reasonMessage: ${runtime.reasonMessage ?? "-"}`);
|
|
2889
|
+
console.log(` recommendedModel: ${runtime.recommendedModel ?? "-"}`);
|
|
2890
|
+
console.log(` supportedModels: ${runtime.supportedModels?.join(", ") ?? "-"}`);
|
|
2687
2891
|
}
|
|
2688
2892
|
};
|
|
2689
2893
|
agentsNew = async (agentId, opts = {}) => {
|
|
@@ -2692,48 +2896,38 @@ var AgentCommands = class {
|
|
|
2692
2896
|
displayName: opts.name,
|
|
2693
2897
|
description: opts.description,
|
|
2694
2898
|
avatar: opts.avatar,
|
|
2695
|
-
home: opts.home
|
|
2899
|
+
home: opts.home,
|
|
2900
|
+
runtime: opts.runtime
|
|
2696
2901
|
}, { initializeHomeDirectory: this.deps.initializeAgentHomeDirectory });
|
|
2697
2902
|
if (opts.json) {
|
|
2698
|
-
console.log(JSON.stringify({
|
|
2699
|
-
agent: created,
|
|
2700
|
-
restartRequired: true
|
|
2701
|
-
}, null, 2));
|
|
2903
|
+
console.log(JSON.stringify({ agent: created }, null, 2));
|
|
2702
2904
|
return;
|
|
2703
2905
|
}
|
|
2704
|
-
await this.deps.requestRestart({
|
|
2705
|
-
reason: "agents-updated",
|
|
2706
|
-
manualMessage: `Created agent '${created.id}'. Restart ${this.deps.appName} to apply agent runtime changes.`
|
|
2707
|
-
});
|
|
2708
2906
|
console.log(`✓ Created agent ${created.id}`);
|
|
2709
2907
|
console.log(` name: ${created.displayName ?? "-"}`);
|
|
2710
2908
|
console.log(` description: ${created.description ?? "-"}`);
|
|
2711
2909
|
console.log(` home: ${created.workspace}`);
|
|
2712
2910
|
console.log(` avatar: ${created.avatar ?? "-"}`);
|
|
2911
|
+
console.log(` runtime: ${created.runtime ?? created.engine ?? "-"}`);
|
|
2713
2912
|
};
|
|
2714
2913
|
agentsUpdate = async (agentId, opts = {}) => {
|
|
2715
2914
|
const updated = updateAgentProfile({
|
|
2716
2915
|
id: agentId,
|
|
2717
2916
|
displayName: opts.name,
|
|
2718
2917
|
description: opts.description,
|
|
2719
|
-
avatar: opts.avatar
|
|
2918
|
+
avatar: opts.avatar,
|
|
2919
|
+
runtime: opts.runtime
|
|
2720
2920
|
});
|
|
2721
2921
|
if (opts.json) {
|
|
2722
|
-
console.log(JSON.stringify({
|
|
2723
|
-
agent: updated,
|
|
2724
|
-
restartRequired: true
|
|
2725
|
-
}, null, 2));
|
|
2922
|
+
console.log(JSON.stringify({ agent: updated }, null, 2));
|
|
2726
2923
|
return;
|
|
2727
2924
|
}
|
|
2728
|
-
await this.deps.requestRestart({
|
|
2729
|
-
reason: "agents-updated",
|
|
2730
|
-
manualMessage: `Updated agent '${updated.id}'. Restart ${this.deps.appName} to apply agent runtime changes.`
|
|
2731
|
-
});
|
|
2732
2925
|
console.log(`✓ Updated agent ${updated.id}`);
|
|
2733
2926
|
console.log(` name: ${updated.displayName ?? "-"}`);
|
|
2734
2927
|
console.log(` description: ${updated.description ?? "-"}`);
|
|
2735
2928
|
console.log(` home: ${updated.workspace}`);
|
|
2736
2929
|
console.log(` avatar: ${updated.avatar ?? "-"}`);
|
|
2930
|
+
console.log(` runtime: ${updated.runtime ?? updated.engine ?? "-"}`);
|
|
2737
2931
|
};
|
|
2738
2932
|
agentsRemove = async (agentId, opts = {}) => {
|
|
2739
2933
|
if (agentId.trim().toLowerCase() === BUILTIN_MAIN_AGENT_ID) throw new Error(`agent id '${BUILTIN_MAIN_AGENT_ID}' is reserved`);
|
|
@@ -2741,15 +2935,10 @@ var AgentCommands = class {
|
|
|
2741
2935
|
if (opts.json) {
|
|
2742
2936
|
console.log(JSON.stringify({
|
|
2743
2937
|
removed: true,
|
|
2744
|
-
agentId
|
|
2745
|
-
restartRequired: true
|
|
2938
|
+
agentId
|
|
2746
2939
|
}, null, 2));
|
|
2747
2940
|
return;
|
|
2748
2941
|
}
|
|
2749
|
-
await this.deps.requestRestart({
|
|
2750
|
-
reason: "agents-updated",
|
|
2751
|
-
manualMessage: `Removed agent '${agentId}'. Restart ${this.deps.appName} to apply agent runtime changes.`
|
|
2752
|
-
});
|
|
2753
2942
|
console.log(`✓ Removed agent ${agentId}`);
|
|
2754
2943
|
};
|
|
2755
2944
|
toAgentListEntry = (agent) => {
|
|
@@ -2759,6 +2948,7 @@ var AgentCommands = class {
|
|
|
2759
2948
|
description: agent.description ?? null,
|
|
2760
2949
|
avatar: agent.avatar ?? null,
|
|
2761
2950
|
workspace: agent.workspace,
|
|
2951
|
+
runtime: agent.runtime ?? agent.engine ?? null,
|
|
2762
2952
|
builtIn: agent.builtIn === true
|
|
2763
2953
|
};
|
|
2764
2954
|
};
|
|
@@ -4185,13 +4375,14 @@ async function startGatewaySupportServices(params) {
|
|
|
4185
4375
|
}
|
|
4186
4376
|
function watchCronStoreFile(params) {
|
|
4187
4377
|
const cronStorePath = resolve(params.cronStorePath);
|
|
4188
|
-
chokidar.watch(cronStorePath, {
|
|
4378
|
+
const watcher = chokidar.watch(cronStorePath, {
|
|
4189
4379
|
ignoreInitial: true,
|
|
4190
4380
|
awaitWriteFinish: {
|
|
4191
4381
|
stabilityThreshold: 200,
|
|
4192
4382
|
pollInterval: 50
|
|
4193
4383
|
}
|
|
4194
|
-
})
|
|
4384
|
+
});
|
|
4385
|
+
watcher.on("all", (event, changedPath) => {
|
|
4195
4386
|
if (resolve(changedPath) !== cronStorePath) return;
|
|
4196
4387
|
if (event === "add" || event === "change" || event === "unlink") try {
|
|
4197
4388
|
params.reloadCronStore();
|
|
@@ -4199,6 +4390,85 @@ function watchCronStoreFile(params) {
|
|
|
4199
4390
|
console.error(`Cron store reload failed (${event}): ${String(error)}`);
|
|
4200
4391
|
}
|
|
4201
4392
|
});
|
|
4393
|
+
return watcher;
|
|
4394
|
+
}
|
|
4395
|
+
function watchServiceConfigFile(params) {
|
|
4396
|
+
const configPath = resolve(params.configPath);
|
|
4397
|
+
const watcher = chokidar.watch(configPath, {
|
|
4398
|
+
ignoreInitial: true,
|
|
4399
|
+
awaitWriteFinish: {
|
|
4400
|
+
stabilityThreshold: 200,
|
|
4401
|
+
pollInterval: 50
|
|
4402
|
+
}
|
|
4403
|
+
});
|
|
4404
|
+
params.watcherRegistry.remember(watcher);
|
|
4405
|
+
watcher.on("all", (event, changedPath) => {
|
|
4406
|
+
if (resolve(changedPath) !== configPath) return;
|
|
4407
|
+
if (event === "add") {
|
|
4408
|
+
params.scheduleReload("config add");
|
|
4409
|
+
return;
|
|
4410
|
+
}
|
|
4411
|
+
if (event === "change") {
|
|
4412
|
+
params.scheduleReload("config change");
|
|
4413
|
+
return;
|
|
4414
|
+
}
|
|
4415
|
+
if (event === "unlink") params.scheduleReload("config unlink");
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
var ServiceFileWatcherRegistry = class {
|
|
4419
|
+
watchers = [];
|
|
4420
|
+
remember = (watcher) => {
|
|
4421
|
+
this.watchers.push(watcher);
|
|
4422
|
+
};
|
|
4423
|
+
clear = async () => {
|
|
4424
|
+
const watchers = this.watchers.splice(0);
|
|
4425
|
+
await Promise.allSettled(watchers.map(async (watcher) => {
|
|
4426
|
+
try {
|
|
4427
|
+
await watcher.close();
|
|
4428
|
+
} catch {}
|
|
4429
|
+
}));
|
|
4430
|
+
};
|
|
4431
|
+
};
|
|
4432
|
+
function writeLocalServiceDiscoveryState(uiConfig, pid = process.pid) {
|
|
4433
|
+
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
4434
|
+
const apiUrl = `${uiUrl}/api`;
|
|
4435
|
+
const existing = readServiceState();
|
|
4436
|
+
writeServiceState({
|
|
4437
|
+
pid,
|
|
4438
|
+
startedAt: existing?.pid === pid && typeof existing.startedAt === "string" ? existing.startedAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
4439
|
+
uiUrl,
|
|
4440
|
+
apiUrl,
|
|
4441
|
+
uiHost: uiConfig.host,
|
|
4442
|
+
uiPort: uiConfig.port,
|
|
4443
|
+
logPath: existing?.logPath ?? resolveServiceLogPath(),
|
|
4444
|
+
startupState: existing?.startupState ?? "ready",
|
|
4445
|
+
startupLastProbeError: existing?.startupLastProbeError ?? null,
|
|
4446
|
+
startupTimeoutMs: existing?.startupTimeoutMs,
|
|
4447
|
+
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4448
|
+
...existing?.remote ? { remote: existing.remote } : {}
|
|
4449
|
+
});
|
|
4450
|
+
}
|
|
4451
|
+
function clearOwnedServiceState(pid = process.pid) {
|
|
4452
|
+
if (readServiceState()?.pid === pid) clearServiceState();
|
|
4453
|
+
}
|
|
4454
|
+
function finalizeLocalUiStartup(params) {
|
|
4455
|
+
const { setUiEventPublisher, uiConfig, uiStartup } = params;
|
|
4456
|
+
setUiEventPublisher(uiStartup?.publish);
|
|
4457
|
+
if (uiStartup) writeLocalServiceDiscoveryState(uiConfig);
|
|
4458
|
+
}
|
|
4459
|
+
async function startGatewayRuntimeSupport(params) {
|
|
4460
|
+
const { cronJobs, cronStorePath, reloadCronStore, remoteModule, startCron, startHeartbeat, watchConfigFile, watcherRegistry } = params;
|
|
4461
|
+
await startGatewaySupportServices({
|
|
4462
|
+
cronJobs,
|
|
4463
|
+
remoteModule,
|
|
4464
|
+
watchConfigFile,
|
|
4465
|
+
startCron,
|
|
4466
|
+
startHeartbeat
|
|
4467
|
+
});
|
|
4468
|
+
watcherRegistry.remember(watchCronStoreFile({
|
|
4469
|
+
cronStorePath,
|
|
4470
|
+
reloadCronStore
|
|
4471
|
+
}));
|
|
4202
4472
|
}
|
|
4203
4473
|
//#endregion
|
|
4204
4474
|
//#region src/cli/commands/remote-support/remote-access-service-control.ts
|
|
@@ -4981,7 +5251,7 @@ var SpawnChildSessionTool = class extends Tool {
|
|
|
4981
5251
|
return "spawn";
|
|
4982
5252
|
}
|
|
4983
5253
|
get description() {
|
|
4984
|
-
return "Create a child session,
|
|
5254
|
+
return "Create a child session, start the delegated task in the background, return a running child-session handle immediately, and automatically continue this session with the child's final reply when it finishes.";
|
|
4985
5255
|
}
|
|
4986
5256
|
get parameters() {
|
|
4987
5257
|
return {
|
|
@@ -4989,19 +5259,23 @@ var SpawnChildSessionTool = class extends Tool {
|
|
|
4989
5259
|
properties: {
|
|
4990
5260
|
task: {
|
|
4991
5261
|
type: "string",
|
|
4992
|
-
description: "Task to
|
|
5262
|
+
description: "Task to start immediately inside the child session."
|
|
4993
5263
|
},
|
|
4994
5264
|
label: {
|
|
4995
5265
|
type: "string",
|
|
4996
|
-
description: "Optional child session title."
|
|
5266
|
+
description: "Optional child session title shown in the session list and tool card."
|
|
4997
5267
|
},
|
|
4998
5268
|
model: {
|
|
4999
5269
|
type: "string",
|
|
5000
|
-
description: "Optional model override for the child session."
|
|
5270
|
+
description: "Optional model override used only for the child session."
|
|
5271
|
+
},
|
|
5272
|
+
runtime: {
|
|
5273
|
+
type: "string",
|
|
5274
|
+
description: "Optional runtime override used only for the child session, for example native or codex."
|
|
5001
5275
|
},
|
|
5002
5276
|
agentId: {
|
|
5003
5277
|
type: "string",
|
|
5004
|
-
description: "Optional target agent id for the child session. Omit to use the default agent."
|
|
5278
|
+
description: "Optional target agent id for the child session. Omit to use the default agent for that child."
|
|
5005
5279
|
}
|
|
5006
5280
|
},
|
|
5007
5281
|
required: ["task"]
|
|
@@ -5021,6 +5295,7 @@ var SpawnChildSessionTool = class extends Tool {
|
|
|
5021
5295
|
task,
|
|
5022
5296
|
title: readOptionalString$4(params, "label"),
|
|
5023
5297
|
model: readOptionalString$4(params, "model"),
|
|
5298
|
+
runtime: readOptionalString$4(params, "runtime"),
|
|
5024
5299
|
handoffDepth: this.handoffDepth,
|
|
5025
5300
|
agentId: readOptionalString$4(params, "agentId")
|
|
5026
5301
|
});
|
|
@@ -5142,6 +5417,10 @@ var SessionSpawnTool = class extends Tool {
|
|
|
5142
5417
|
type: "string",
|
|
5143
5418
|
description: "Optional model override for the new session."
|
|
5144
5419
|
},
|
|
5420
|
+
runtime: {
|
|
5421
|
+
type: "string",
|
|
5422
|
+
description: "Optional runtime override for the new session, for example native or codex."
|
|
5423
|
+
},
|
|
5145
5424
|
agentId: {
|
|
5146
5425
|
type: "string",
|
|
5147
5426
|
description: "Optional target agent id for the new session. Omit to use the default agent."
|
|
@@ -5161,7 +5440,8 @@ var SessionSpawnTool = class extends Tool {
|
|
|
5161
5440
|
title: readOptionalString$3(params.title),
|
|
5162
5441
|
sourceSessionMetadata: this.sourceSessionMetadata,
|
|
5163
5442
|
agentId: readOptionalString$3(params.agentId),
|
|
5164
|
-
model: readOptionalString$3(params.model)
|
|
5443
|
+
model: readOptionalString$3(params.model),
|
|
5444
|
+
runtime: readOptionalString$3(params.runtime)
|
|
5165
5445
|
});
|
|
5166
5446
|
return {
|
|
5167
5447
|
kind: "nextclaw.session",
|
|
@@ -5298,16 +5578,6 @@ var NextclawNcpToolRegistry = class {
|
|
|
5298
5578
|
this.registerTool(sessionsRequestTool);
|
|
5299
5579
|
this.registerTool(new SessionsListTool(this.options.sessionManager));
|
|
5300
5580
|
this.registerTool(new SessionsHistoryTool(this.options.sessionManager));
|
|
5301
|
-
const sessionsSendTool = new SessionsSendTool(this.options.sessionManager, this.options.bus);
|
|
5302
|
-
sessionsSendTool.setContext({
|
|
5303
|
-
currentSessionKey: context.sessionId,
|
|
5304
|
-
currentAgentId: context.agentId,
|
|
5305
|
-
channel: context.channel,
|
|
5306
|
-
chatId: context.chatId,
|
|
5307
|
-
maxPingPongTurns: context.config.session?.agentToAgent?.maxPingPongTurns ?? 0,
|
|
5308
|
-
currentHandoffDepth: context.handoffDepth
|
|
5309
|
-
});
|
|
5310
|
-
this.registerTool(sessionsSendTool);
|
|
5311
5581
|
this.registerTool(new MemorySearchTool(context.workspace));
|
|
5312
5582
|
this.registerTool(new MemoryGetTool(context.workspace));
|
|
5313
5583
|
const gatewayTool = new GatewayTool(this.options.gatewayController);
|
|
@@ -5440,14 +5710,16 @@ function prependRequestedSkills(content, requestedSkillNames) {
|
|
|
5440
5710
|
function buildSessionOrchestrationSection() {
|
|
5441
5711
|
return [
|
|
5442
5712
|
"## Session Orchestration",
|
|
5443
|
-
"- `
|
|
5444
|
-
"-
|
|
5713
|
+
"- Before passing a non-default `runtime` to `spawn`, `sessions_spawn`, or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
|
|
5714
|
+
"- `spawn` creates a child session, starts the delegated task there immediately, and returns a running child-session handle right away instead of waiting for the child to finish.",
|
|
5715
|
+
"- When that child reaches its final reply, `spawn` writes the completed result back into the original tool call and resumes the current session with the child's result.",
|
|
5716
|
+
"- Use `spawn` when the work is a subtask of the current flow and the user expects this session to pause now and then continue after that child finishes.",
|
|
5445
5717
|
"- `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.",
|
|
5446
5718
|
"- `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.",
|
|
5447
5719
|
"- 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`.",
|
|
5448
5720
|
"- `sessions_request.target` must be an object shaped like `{ \"session_id\": \"<target-session-id>\" }`. Do not pass a bare string.",
|
|
5449
5721
|
"- 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.",
|
|
5450
|
-
"- Do not use `spawn` for long-lived independent threads
|
|
5722
|
+
"- Do not use `spawn` for long-lived independent threads, fire-and-forget work, or work that should not automatically resume the current session; in those cases use `sessions_spawn` plus `sessions_request` instead."
|
|
5451
5723
|
].join("\n");
|
|
5452
5724
|
}
|
|
5453
5725
|
function filterTools(toolDefinitions, requestedToolNames) {
|
|
@@ -5956,6 +6228,7 @@ function summarizeTask(task) {
|
|
|
5956
6228
|
function cloneInheritedMetadata(sourceMetadata) {
|
|
5957
6229
|
const nextMetadata = {};
|
|
5958
6230
|
for (const key of [
|
|
6231
|
+
"runtime",
|
|
5959
6232
|
"session_type",
|
|
5960
6233
|
"preferred_model",
|
|
5961
6234
|
"preferred_thinking",
|
|
@@ -5980,10 +6253,11 @@ function resolveSessionTitle(params) {
|
|
|
5980
6253
|
return readOptionalString$2(params.title) ?? summarizeTask(params.task);
|
|
5981
6254
|
}
|
|
5982
6255
|
function resolveSessionType(params) {
|
|
5983
|
-
return readOptionalString$2(params.sessionType) ?? readOptionalString$2(params.metadata.session_type) ?? DEFAULT_SESSION_TYPE;
|
|
6256
|
+
return readOptionalString$2(params.runtime) ?? readOptionalString$2(params.metadata.runtime) ?? readOptionalString$2(params.sessionType) ?? readOptionalString$2(params.metadata.session_type) ?? DEFAULT_SESSION_TYPE;
|
|
5984
6257
|
}
|
|
5985
6258
|
function applySessionOverrides(params) {
|
|
5986
6259
|
params.metadata.session_type = params.sessionType;
|
|
6260
|
+
params.metadata.runtime = params.sessionType;
|
|
5987
6261
|
params.metadata[SESSION_METADATA_LABEL_KEY] = params.title;
|
|
5988
6262
|
params.metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = params.lifecycle;
|
|
5989
6263
|
if (params.parentSessionId) {
|
|
@@ -6023,6 +6297,7 @@ var SessionCreationService = class {
|
|
|
6023
6297
|
const parentSessionId = readOptionalString$2(params.parentSessionId);
|
|
6024
6298
|
const requestId = readOptionalString$2(params.requestId);
|
|
6025
6299
|
const sessionType = resolveSessionType({
|
|
6300
|
+
runtime: params.runtime,
|
|
6026
6301
|
sessionType: params.sessionType,
|
|
6027
6302
|
metadata
|
|
6028
6303
|
});
|
|
@@ -6046,7 +6321,7 @@ var SessionCreationService = class {
|
|
|
6046
6321
|
sessionId,
|
|
6047
6322
|
agentId: resolvedAgentId,
|
|
6048
6323
|
sessionType,
|
|
6049
|
-
runtimeFamily: "native",
|
|
6324
|
+
runtimeFamily: sessionType === DEFAULT_SESSION_TYPE ? "native" : "external",
|
|
6050
6325
|
...parentSessionId ? { parentSessionId } : {},
|
|
6051
6326
|
...requestId ? { spawnedByRequestId: requestId } : {},
|
|
6052
6327
|
lifecycle: DEFAULT_LIFECYCLE,
|
|
@@ -6199,7 +6474,7 @@ var SessionRequestBroker = class {
|
|
|
6199
6474
|
this.onSessionUpdated = onSessionUpdated;
|
|
6200
6475
|
}
|
|
6201
6476
|
spawnChildSessionAndRequest = async (params) => {
|
|
6202
|
-
const { sourceSessionId, sourceToolCallId, sourceSessionMetadata, task, title, model, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId } = params;
|
|
6477
|
+
const { sourceSessionId, sourceToolCallId, sourceSessionMetadata, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId } = params;
|
|
6203
6478
|
const requestId = randomUUID();
|
|
6204
6479
|
const childSession = this.sessionCreationService.createChildSession({
|
|
6205
6480
|
parentSessionId: sourceSessionId,
|
|
@@ -6208,6 +6483,7 @@ var SessionRequestBroker = class {
|
|
|
6208
6483
|
sourceSessionMetadata,
|
|
6209
6484
|
agentId,
|
|
6210
6485
|
model,
|
|
6486
|
+
runtime,
|
|
6211
6487
|
thinkingLevel,
|
|
6212
6488
|
sessionType,
|
|
6213
6489
|
projectRoot,
|
|
@@ -6490,10 +6766,6 @@ var SessionRequestDeliveryService = class {
|
|
|
6490
6766
|
if (!params.request.sourceToolCallId?.trim()) return;
|
|
6491
6767
|
const backend = this.resolveBackend();
|
|
6492
6768
|
if (!backend) throw new Error("NCP backend is not ready for session request delivery.");
|
|
6493
|
-
if (!await waitForSessionToBecomeIdle({
|
|
6494
|
-
backend,
|
|
6495
|
-
sessionId: params.request.sourceSessionId
|
|
6496
|
-
})) return;
|
|
6497
6769
|
await backend.updateToolCallResult(params.request.sourceSessionId, params.request.sourceToolCallId.trim(), params.result);
|
|
6498
6770
|
};
|
|
6499
6771
|
resumeSourceSession = async (params) => {
|
|
@@ -6502,7 +6774,10 @@ var SessionRequestDeliveryService = class {
|
|
|
6502
6774
|
if (!await waitForSessionToBecomeIdle({
|
|
6503
6775
|
backend,
|
|
6504
6776
|
sessionId: params.request.sourceSessionId
|
|
6505
|
-
}))
|
|
6777
|
+
})) {
|
|
6778
|
+
console.warn(`[session-request] resume skipped for ${params.request.sourceSessionId} because the source session did not become idle in time.`);
|
|
6779
|
+
return;
|
|
6780
|
+
}
|
|
6506
6781
|
scheduleDetached(async () => consumeAgentRun(backend.send({
|
|
6507
6782
|
sessionId: params.request.sourceSessionId,
|
|
6508
6783
|
message: buildSessionRequestCompletionMessage(params)
|
|
@@ -6510,75 +6785,6 @@ var SessionRequestDeliveryService = class {
|
|
|
6510
6785
|
};
|
|
6511
6786
|
};
|
|
6512
6787
|
//#endregion
|
|
6513
|
-
//#region src/cli/commands/ncp/ui-ncp-runtime-registry.ts
|
|
6514
|
-
const DEFAULT_UI_NCP_RUNTIME_KIND = "native";
|
|
6515
|
-
function normalizeRuntimeKind(value) {
|
|
6516
|
-
if (typeof value !== "string") return null;
|
|
6517
|
-
const normalized = value.trim().toLowerCase();
|
|
6518
|
-
return normalized.length > 0 ? normalized : null;
|
|
6519
|
-
}
|
|
6520
|
-
function readRequestedRuntimeKind(sessionMetadata) {
|
|
6521
|
-
return normalizeRuntimeKind(sessionMetadata.session_type) ?? normalizeRuntimeKind(sessionMetadata.sessionType) ?? null;
|
|
6522
|
-
}
|
|
6523
|
-
var UiNcpRuntimeRegistry = class {
|
|
6524
|
-
registrations = /* @__PURE__ */ new Map();
|
|
6525
|
-
constructor(defaultKind = DEFAULT_UI_NCP_RUNTIME_KIND) {
|
|
6526
|
-
this.defaultKind = defaultKind;
|
|
6527
|
-
}
|
|
6528
|
-
register(registration) {
|
|
6529
|
-
const normalizedKind = normalizeRuntimeKind(registration.kind);
|
|
6530
|
-
if (!normalizedKind) throw new Error("ui ncp runtime kind must be a non-empty string");
|
|
6531
|
-
const token = Symbol(normalizedKind);
|
|
6532
|
-
this.registrations.set(normalizedKind, {
|
|
6533
|
-
...registration,
|
|
6534
|
-
kind: normalizedKind,
|
|
6535
|
-
token
|
|
6536
|
-
});
|
|
6537
|
-
return toDisposable(() => {
|
|
6538
|
-
const current = this.registrations.get(normalizedKind);
|
|
6539
|
-
if (!current || current.token !== token) return;
|
|
6540
|
-
this.registrations.delete(normalizedKind);
|
|
6541
|
-
});
|
|
6542
|
-
}
|
|
6543
|
-
createRuntime(params) {
|
|
6544
|
-
const requestedKind = readRequestedRuntimeKind(params.sessionMetadata) ?? this.defaultKind;
|
|
6545
|
-
const registration = this.registrations.get(requestedKind);
|
|
6546
|
-
if (!registration) throw new Error(`ncp runtime unavailable: ${requestedKind}`);
|
|
6547
|
-
const nextSessionMetadata = {
|
|
6548
|
-
...params.sessionMetadata,
|
|
6549
|
-
session_type: registration.kind
|
|
6550
|
-
};
|
|
6551
|
-
params.setSessionMetadata(nextSessionMetadata);
|
|
6552
|
-
return registration.createRuntime({
|
|
6553
|
-
...params,
|
|
6554
|
-
sessionMetadata: nextSessionMetadata
|
|
6555
|
-
});
|
|
6556
|
-
}
|
|
6557
|
-
async listSessionTypes(params) {
|
|
6558
|
-
const options = await Promise.all([...this.registrations.values()].map(async (registration) => {
|
|
6559
|
-
const descriptor = await registration.describeSessionType?.(params);
|
|
6560
|
-
return {
|
|
6561
|
-
value: registration.kind,
|
|
6562
|
-
label: registration.label,
|
|
6563
|
-
ready: descriptor?.ready ?? true,
|
|
6564
|
-
reason: descriptor?.reason ?? null,
|
|
6565
|
-
reasonMessage: descriptor?.reasonMessage ?? null,
|
|
6566
|
-
recommendedModel: descriptor?.recommendedModel ?? null,
|
|
6567
|
-
cta: descriptor?.cta ?? null,
|
|
6568
|
-
...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
|
|
6569
|
-
};
|
|
6570
|
-
}));
|
|
6571
|
-
return {
|
|
6572
|
-
defaultType: this.defaultKind,
|
|
6573
|
-
options: options.sort((left, right) => {
|
|
6574
|
-
if (left.value === this.defaultKind) return -1;
|
|
6575
|
-
if (right.value === this.defaultKind) return 1;
|
|
6576
|
-
return left.value.localeCompare(right.value);
|
|
6577
|
-
})
|
|
6578
|
-
};
|
|
6579
|
-
}
|
|
6580
|
-
};
|
|
6581
|
-
//#endregion
|
|
6582
6788
|
//#region src/cli/commands/ncp/runtime/ui-ncp-agent-handle.ts
|
|
6583
6789
|
function createUiNcpAgentHandle(params) {
|
|
6584
6790
|
return {
|
|
@@ -7688,8 +7894,12 @@ function createGatewayShellContext(params) {
|
|
|
7688
7894
|
const runtimeConfigPath = getConfigPath$2();
|
|
7689
7895
|
const config = resolveConfigSecrets$3(loadConfig$3(), { configPath: runtimeConfigPath });
|
|
7690
7896
|
const workspace = getWorkspacePath$2(config.agents.defaults.workspace);
|
|
7897
|
+
const homeDir = getDataDir$1();
|
|
7691
7898
|
const cronStorePath = join(getDataDir$1(), "cron", "jobs.json");
|
|
7692
|
-
const sessionManager = measureStartupSync("service.gateway_shell_context.session_manager", () => new SessionManager$1(
|
|
7899
|
+
const sessionManager = measureStartupSync("service.gateway_shell_context.session_manager", () => new SessionManager$1({
|
|
7900
|
+
workspace,
|
|
7901
|
+
homeDir
|
|
7902
|
+
}));
|
|
7693
7903
|
const cron = new CronService$1(cronStorePath);
|
|
7694
7904
|
const uiConfig = resolveUiConfig(config, params.uiOverrides);
|
|
7695
7905
|
return {
|
|
@@ -8671,11 +8881,13 @@ function createSkillsLoader(workspace) {
|
|
|
8671
8881
|
var ServiceCommands = class {
|
|
8672
8882
|
applyLiveConfigReload = null;
|
|
8673
8883
|
liveUiNcpAgent = null;
|
|
8884
|
+
fileWatchers = new ServiceFileWatcherRegistry();
|
|
8674
8885
|
constructor(deps) {
|
|
8675
8886
|
this.deps = deps;
|
|
8676
8887
|
}
|
|
8677
8888
|
startGateway = async (options = {}) => {
|
|
8678
8889
|
logStartupTrace("service.start_gateway.begin");
|
|
8890
|
+
await this.fileWatchers.clear();
|
|
8679
8891
|
this.applyLiveConfigReload = null;
|
|
8680
8892
|
this.liveUiNcpAgent = null;
|
|
8681
8893
|
const shellContext = measureStartupSync("service.create_gateway_shell_context", () => createGatewayShellContext({
|
|
@@ -8719,7 +8931,11 @@ var ServiceCommands = class {
|
|
|
8719
8931
|
ncpSessionService: ncpSessionRealtimeBridge.sessionService,
|
|
8720
8932
|
initializeAgentHomeDirectory: this.deps.initializeAgentHomeDirectory
|
|
8721
8933
|
}));
|
|
8722
|
-
|
|
8934
|
+
finalizeLocalUiStartup({
|
|
8935
|
+
uiStartup,
|
|
8936
|
+
setUiEventPublisher: (publish) => ncpSessionRealtimeBridge.setUiEventPublisher(publish),
|
|
8937
|
+
uiConfig: shellContext.uiConfig
|
|
8938
|
+
});
|
|
8723
8939
|
bootstrapStatus.markShellReady();
|
|
8724
8940
|
await setImmediate();
|
|
8725
8941
|
const gateway = measureStartupSync("service.create_gateway_startup_context", () => createGatewayStartupContext({
|
|
@@ -8753,17 +8969,20 @@ var ServiceCommands = class {
|
|
|
8753
8969
|
publishUiEvent: uiStartup?.publish
|
|
8754
8970
|
});
|
|
8755
8971
|
console.log("✓ Capability hydration: scheduled in background");
|
|
8756
|
-
await measureStartupAsync("service.start_gateway_support_services", async () => await
|
|
8972
|
+
await measureStartupAsync("service.start_gateway_support_services", async () => await startGatewayRuntimeSupport({
|
|
8757
8973
|
cronJobs: gateway.cron.status().jobs,
|
|
8758
8974
|
remoteModule: gateway.remoteModule,
|
|
8759
|
-
watchConfigFile: () =>
|
|
8975
|
+
watchConfigFile: () => watchServiceConfigFile({
|
|
8976
|
+
configPath: resolve(getConfigPath$1()),
|
|
8977
|
+
watcherRegistry: this.fileWatchers,
|
|
8978
|
+
scheduleReload: (reason) => gateway.reloader.scheduleReload(reason)
|
|
8979
|
+
}),
|
|
8760
8980
|
startCron: () => gateway.cron.start(),
|
|
8761
|
-
startHeartbeat: () => gateway.heartbeat.start()
|
|
8762
|
-
}));
|
|
8763
|
-
watchCronStoreFile({
|
|
8981
|
+
startHeartbeat: () => gateway.heartbeat.start(),
|
|
8764
8982
|
cronStorePath: resolve(join(NextclawCore.getDataDir(), "cron", "jobs.json")),
|
|
8765
|
-
reloadCronStore: () => gateway.cron.reloadFromStore()
|
|
8766
|
-
|
|
8983
|
+
reloadCronStore: () => gateway.cron.reloadFromStore(),
|
|
8984
|
+
watcherRegistry: this.fileWatchers
|
|
8985
|
+
}));
|
|
8767
8986
|
const deferredGatewayStartupHooks = createDeferredGatewayStartupHooks({
|
|
8768
8987
|
uiStartup,
|
|
8769
8988
|
gateway,
|
|
@@ -8811,6 +9030,8 @@ var ServiceCommands = class {
|
|
|
8811
9030
|
console.error(`Deferred startup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
8812
9031
|
},
|
|
8813
9032
|
cleanup: async () => {
|
|
9033
|
+
clearOwnedServiceState();
|
|
9034
|
+
await this.fileWatchers.clear();
|
|
8814
9035
|
this.applyLiveConfigReload = null;
|
|
8815
9036
|
this.liveUiNcpAgent = null;
|
|
8816
9037
|
ncpSessionRealtimeBridge.clear();
|
|
@@ -8826,27 +9047,6 @@ var ServiceCommands = class {
|
|
|
8826
9047
|
if (typeof value !== "string") return;
|
|
8827
9048
|
return value.trim() || void 0;
|
|
8828
9049
|
};
|
|
8829
|
-
watchConfigFile = (reloader) => {
|
|
8830
|
-
const configPath = resolve(getConfigPath$1());
|
|
8831
|
-
chokidar.watch(configPath, {
|
|
8832
|
-
ignoreInitial: true,
|
|
8833
|
-
awaitWriteFinish: {
|
|
8834
|
-
stabilityThreshold: 200,
|
|
8835
|
-
pollInterval: 50
|
|
8836
|
-
}
|
|
8837
|
-
}).on("all", (event, changedPath) => {
|
|
8838
|
-
if (resolve(changedPath) !== configPath) return;
|
|
8839
|
-
if (event === "add") {
|
|
8840
|
-
reloader.scheduleReload("config add");
|
|
8841
|
-
return;
|
|
8842
|
-
}
|
|
8843
|
-
if (event === "change") {
|
|
8844
|
-
reloader.scheduleReload("config change");
|
|
8845
|
-
return;
|
|
8846
|
-
}
|
|
8847
|
-
if (event === "unlink") reloader.scheduleReload("config unlink");
|
|
8848
|
-
});
|
|
8849
|
-
};
|
|
8850
9050
|
resolveMostRecentRoutableSessionKey = (sessionManager) => {
|
|
8851
9051
|
let best = null;
|
|
8852
9052
|
for (const session of sessionManager.listSessions()) {
|
|
@@ -9559,11 +9759,7 @@ var CliRuntime = class {
|
|
|
9559
9759
|
this.mcpCommands = measureStartupSync("cli.runtime.mcp_commands", () => new McpCommands());
|
|
9560
9760
|
this.secretsCommands = measureStartupSync("cli.runtime.secrets_commands", () => new SecretsCommands({ requestRestart: (params) => this.requestRestart(params) }));
|
|
9561
9761
|
this.pluginCommands = measureStartupSync("cli.runtime.plugin_commands", () => new PluginCommands());
|
|
9562
|
-
this.agentCommands = measureStartupSync("cli.runtime.agent_commands", () => new AgentCommands({
|
|
9563
|
-
requestRestart: (params) => this.requestRestart(params),
|
|
9564
|
-
initializeAgentHomeDirectory: (homeDirectory) => this.workspaceManager.createWorkspaceTemplates(homeDirectory),
|
|
9565
|
-
appName: APP_NAME
|
|
9566
|
-
}));
|
|
9762
|
+
this.agentCommands = measureStartupSync("cli.runtime.agent_commands", () => new AgentCommands({ initializeAgentHomeDirectory: (homeDirectory) => this.workspaceManager.createWorkspaceTemplates(homeDirectory) }));
|
|
9567
9763
|
this.channelCommands = measureStartupSync("cli.runtime.channel_commands", () => new ChannelCommands({
|
|
9568
9764
|
logo: this.logo,
|
|
9569
9765
|
getBridgeDir: () => this.workspaceManager.getBridgeDir(),
|
|
@@ -9966,6 +10162,9 @@ var CliRuntime = class {
|
|
|
9966
10162
|
agentsList = (opts = {}) => {
|
|
9967
10163
|
this.agentCommands.agentsList(opts);
|
|
9968
10164
|
};
|
|
10165
|
+
agentsRuntimes = async (opts = {}) => {
|
|
10166
|
+
await this.agentCommands.agentsRuntimes(opts);
|
|
10167
|
+
};
|
|
9969
10168
|
agentsNew = async (agentId, opts = {}) => {
|
|
9970
10169
|
await this.agentCommands.agentsNew(agentId, opts);
|
|
9971
10170
|
};
|
|
@@ -10127,8 +10326,9 @@ var CliRuntime = class {
|
|
|
10127
10326
|
function registerAgentsCommands(program, runtime) {
|
|
10128
10327
|
const agents = program.command("agents").description("Manage agents");
|
|
10129
10328
|
agents.command("list").description("List available agents").option("--json", "Output JSON", false).action((opts) => runtime.agentsList(opts));
|
|
10130
|
-
agents.command("
|
|
10131
|
-
agents.command("
|
|
10329
|
+
agents.command("runtimes").description("List available agent runtimes").option("--probe", "Actively probe runtime readiness", false).option("--json", "Output JSON", false).action(async (opts) => runtime.agentsRuntimes(opts));
|
|
10330
|
+
agents.command("new <agentId>").description("Create a new agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--home <path>", "Agent home directory").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsNew(agentId, opts));
|
|
10331
|
+
agents.command("update <agentId>").description("Update an existing agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsUpdate(agentId, opts));
|
|
10132
10332
|
agents.command("remove <agentId>").description("Remove an agent").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsRemove(agentId, opts));
|
|
10133
10333
|
}
|
|
10134
10334
|
//#endregion
|