@nextclaw/server 0.5.30 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +65 -1
- package/dist/index.js +345 -8
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ type AgentProfileView = {
|
|
|
55
55
|
default?: boolean;
|
|
56
56
|
workspace?: string;
|
|
57
57
|
model?: string;
|
|
58
|
+
engine?: string;
|
|
59
|
+
engineConfig?: Record<string, unknown>;
|
|
58
60
|
contextTokens?: number;
|
|
59
61
|
maxToolIterations?: number;
|
|
60
62
|
};
|
|
@@ -171,6 +173,8 @@ type RuntimeConfigUpdate = {
|
|
|
171
173
|
agents?: {
|
|
172
174
|
defaults?: {
|
|
173
175
|
contextTokens?: number;
|
|
176
|
+
engine?: string;
|
|
177
|
+
engineConfig?: Record<string, unknown>;
|
|
174
178
|
};
|
|
175
179
|
list?: AgentProfileView[];
|
|
176
180
|
};
|
|
@@ -228,6 +232,7 @@ type ChatTurnRequest = {
|
|
|
228
232
|
chatId?: string;
|
|
229
233
|
model?: string;
|
|
230
234
|
metadata?: Record<string, unknown>;
|
|
235
|
+
runId?: string;
|
|
231
236
|
};
|
|
232
237
|
type ChatTurnResult = {
|
|
233
238
|
reply: string;
|
|
@@ -258,15 +263,67 @@ type ChatTurnView = {
|
|
|
258
263
|
completedAt: string;
|
|
259
264
|
durationMs: number;
|
|
260
265
|
};
|
|
266
|
+
type ChatCapabilitiesView = {
|
|
267
|
+
stopSupported: boolean;
|
|
268
|
+
stopReason?: string;
|
|
269
|
+
};
|
|
270
|
+
type ChatTurnStopRequest = {
|
|
271
|
+
runId: string;
|
|
272
|
+
sessionKey?: string;
|
|
273
|
+
agentId?: string;
|
|
274
|
+
};
|
|
275
|
+
type ChatTurnStopResult = {
|
|
276
|
+
stopped: boolean;
|
|
277
|
+
runId: string;
|
|
278
|
+
sessionKey?: string;
|
|
279
|
+
reason?: string;
|
|
280
|
+
};
|
|
281
|
+
type ChatRunState = "queued" | "running" | "completed" | "failed" | "aborted";
|
|
282
|
+
type ChatRunView = {
|
|
283
|
+
runId: string;
|
|
284
|
+
sessionKey: string;
|
|
285
|
+
agentId?: string;
|
|
286
|
+
model?: string;
|
|
287
|
+
state: ChatRunState;
|
|
288
|
+
requestedAt: string;
|
|
289
|
+
startedAt?: string;
|
|
290
|
+
completedAt?: string;
|
|
291
|
+
stopSupported: boolean;
|
|
292
|
+
stopReason?: string;
|
|
293
|
+
error?: string;
|
|
294
|
+
reply?: string;
|
|
295
|
+
eventCount: number;
|
|
296
|
+
};
|
|
297
|
+
type ChatRunListView = {
|
|
298
|
+
runs: ChatRunView[];
|
|
299
|
+
total: number;
|
|
300
|
+
};
|
|
261
301
|
type UiChatRuntime = {
|
|
262
302
|
processTurn: (params: ChatTurnRequest) => Promise<ChatTurnResult>;
|
|
263
303
|
processTurnStream?: (params: ChatTurnRequest) => AsyncGenerator<ChatTurnStreamEvent>;
|
|
304
|
+
startTurnRun?: (params: ChatTurnRequest) => Promise<ChatRunView> | ChatRunView;
|
|
305
|
+
streamRun?: (params: {
|
|
306
|
+
runId: string;
|
|
307
|
+
fromEventIndex?: number;
|
|
308
|
+
}) => AsyncGenerator<ChatTurnStreamEvent>;
|
|
309
|
+
getRun?: (params: {
|
|
310
|
+
runId: string;
|
|
311
|
+
}) => Promise<ChatRunView | null> | ChatRunView | null;
|
|
312
|
+
listRuns?: (params: {
|
|
313
|
+
sessionKey?: string;
|
|
314
|
+
states?: ChatRunState[];
|
|
315
|
+
limit?: number;
|
|
316
|
+
}) => Promise<ChatRunListView> | ChatRunListView;
|
|
317
|
+
getCapabilities?: (params: Pick<ChatTurnRequest, "sessionKey" | "agentId">) => Promise<ChatCapabilitiesView> | ChatCapabilitiesView;
|
|
318
|
+
stopTurn?: (params: ChatTurnStopRequest) => Promise<ChatTurnStopResult> | ChatTurnStopResult;
|
|
264
319
|
};
|
|
265
320
|
type ConfigView = {
|
|
266
321
|
agents: {
|
|
267
322
|
defaults: {
|
|
268
323
|
model: string;
|
|
269
324
|
workspace?: string;
|
|
325
|
+
engine?: string;
|
|
326
|
+
engineConfig?: Record<string, unknown>;
|
|
270
327
|
contextTokens?: number;
|
|
271
328
|
maxToolIterations?: number;
|
|
272
329
|
};
|
|
@@ -461,6 +518,8 @@ type MarketplaceInstalledRecord = {
|
|
|
461
518
|
id?: string;
|
|
462
519
|
spec: string;
|
|
463
520
|
label?: string;
|
|
521
|
+
description?: string;
|
|
522
|
+
descriptionZh?: string;
|
|
464
523
|
source?: string;
|
|
465
524
|
installedAt?: string;
|
|
466
525
|
enabled?: boolean;
|
|
@@ -572,6 +631,11 @@ type UiServerEvent = {
|
|
|
572
631
|
payload: {
|
|
573
632
|
path: string;
|
|
574
633
|
};
|
|
634
|
+
} | {
|
|
635
|
+
type: "run.updated";
|
|
636
|
+
payload: {
|
|
637
|
+
run: ChatRunView;
|
|
638
|
+
};
|
|
575
639
|
} | {
|
|
576
640
|
type: "config.reload.started";
|
|
577
641
|
payload?: Record<string, unknown>;
|
|
@@ -649,4 +713,4 @@ declare function deleteSession(configPath: string, key: string): boolean;
|
|
|
649
713
|
declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
|
|
650
714
|
declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
|
|
651
715
|
|
|
652
|
-
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStreamEvent, type ChatTurnView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceLocalizedTextMap, type MarketplacePluginContentView, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderCreateRequest, type ProviderCreateResult, type ProviderDeleteResult, type ProviderSpecView, type RuntimeConfigUpdate, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSecrets };
|
|
716
|
+
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ChatCapabilitiesView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStopRequest, type ChatTurnStopResult, type ChatTurnStreamEvent, type ChatTurnView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceLocalizedTextMap, type MarketplacePluginContentView, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderCreateRequest, type ProviderCreateResult, type ProviderDeleteResult, type ProviderSpecView, type RuntimeConfigUpdate, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSecrets };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
var MASK_MIN_LENGTH = 8;
|
|
35
35
|
var EXTRA_SENSITIVE_PATH_PATTERNS = [/authorization/i, /cookie/i, /session/i, /bearer/i];
|
|
36
36
|
var PROVIDER_TEST_MODEL_FALLBACKS = {
|
|
37
|
+
nextclaw: "dashscope/qwen3.5-flash",
|
|
37
38
|
openai: "gpt-5-mini",
|
|
38
39
|
deepseek: "deepseek-chat",
|
|
39
40
|
gemini: "gemini-3-flash-preview",
|
|
@@ -47,6 +48,7 @@ var PROVIDER_TEST_MODEL_FALLBACKS = {
|
|
|
47
48
|
anthropic: "claude-opus-4-6"
|
|
48
49
|
};
|
|
49
50
|
var PREFERRED_PROVIDER_ORDER = [
|
|
51
|
+
"nextclaw",
|
|
50
52
|
"openai",
|
|
51
53
|
"anthropic",
|
|
52
54
|
"gemini",
|
|
@@ -914,17 +916,33 @@ function deleteSession(configPath, key) {
|
|
|
914
916
|
}
|
|
915
917
|
function updateRuntime(configPath, patch) {
|
|
916
918
|
const config = loadConfigOrDefault(configPath);
|
|
917
|
-
|
|
918
|
-
|
|
919
|
+
const defaultsPatch = patch.agents?.defaults;
|
|
920
|
+
if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "contextTokens")) {
|
|
921
|
+
const nextContextTokens = defaultsPatch.contextTokens;
|
|
919
922
|
if (typeof nextContextTokens === "number" && Number.isFinite(nextContextTokens)) {
|
|
920
923
|
config.agents.defaults.contextTokens = Math.max(1e3, Math.trunc(nextContextTokens));
|
|
921
924
|
}
|
|
922
925
|
}
|
|
926
|
+
if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "engine")) {
|
|
927
|
+
config.agents.defaults.engine = normalizeOptionalString(defaultsPatch.engine) ?? "native";
|
|
928
|
+
}
|
|
929
|
+
if (defaultsPatch && Object.prototype.hasOwnProperty.call(defaultsPatch, "engineConfig")) {
|
|
930
|
+
const nextEngineConfig = defaultsPatch.engineConfig;
|
|
931
|
+
if (nextEngineConfig && typeof nextEngineConfig === "object" && !Array.isArray(nextEngineConfig)) {
|
|
932
|
+
config.agents.defaults.engineConfig = { ...nextEngineConfig };
|
|
933
|
+
}
|
|
934
|
+
}
|
|
923
935
|
if (patch.agents && Object.prototype.hasOwnProperty.call(patch.agents, "list")) {
|
|
924
|
-
config.agents.list = (patch.agents.list ?? []).map((entry) =>
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
936
|
+
config.agents.list = (patch.agents.list ?? []).map((entry) => {
|
|
937
|
+
const normalizedEngine = normalizeOptionalString(entry.engine);
|
|
938
|
+
const hasEngineConfig = entry.engineConfig && typeof entry.engineConfig === "object" && !Array.isArray(entry.engineConfig);
|
|
939
|
+
return {
|
|
940
|
+
...entry,
|
|
941
|
+
default: Boolean(entry.default),
|
|
942
|
+
...normalizedEngine ? { engine: normalizedEngine } : {},
|
|
943
|
+
...hasEngineConfig ? { engineConfig: { ...entry.engineConfig } } : {}
|
|
944
|
+
};
|
|
945
|
+
});
|
|
928
946
|
}
|
|
929
947
|
if (Object.prototype.hasOwnProperty.call(patch, "bindings")) {
|
|
930
948
|
config.bindings = patch.bindings ?? [];
|
|
@@ -1172,6 +1190,24 @@ function resolveAgentIdFromSessionKey(sessionKey) {
|
|
|
1172
1190
|
const agentId = readNonEmptyString(parsed?.agentId);
|
|
1173
1191
|
return agentId;
|
|
1174
1192
|
}
|
|
1193
|
+
function createChatRunId() {
|
|
1194
|
+
const now = Date.now().toString(36);
|
|
1195
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
1196
|
+
return `run-${now}-${rand}`;
|
|
1197
|
+
}
|
|
1198
|
+
function isChatRunState(value) {
|
|
1199
|
+
return value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "aborted";
|
|
1200
|
+
}
|
|
1201
|
+
function readChatRunStates(value) {
|
|
1202
|
+
if (typeof value !== "string") {
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
1205
|
+
const values = value.split(",").map((item) => item.trim().toLowerCase()).filter((item) => Boolean(item) && isChatRunState(item));
|
|
1206
|
+
if (values.length === 0) {
|
|
1207
|
+
return void 0;
|
|
1208
|
+
}
|
|
1209
|
+
return Array.from(new Set(values));
|
|
1210
|
+
}
|
|
1175
1211
|
function buildChatTurnView(params) {
|
|
1176
1212
|
const completedAt = /* @__PURE__ */ new Date();
|
|
1177
1213
|
return {
|
|
@@ -1184,6 +1220,21 @@ function buildChatTurnView(params) {
|
|
|
1184
1220
|
durationMs: Math.max(0, completedAt.getTime() - params.startedAtMs)
|
|
1185
1221
|
};
|
|
1186
1222
|
}
|
|
1223
|
+
function buildChatTurnViewFromRun(params) {
|
|
1224
|
+
const requestedAt = readNonEmptyString(params.run.requestedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1225
|
+
const completedAt = readNonEmptyString(params.run.completedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1226
|
+
const requestedAtMs = Date.parse(requestedAt);
|
|
1227
|
+
const completedAtMs = Date.parse(completedAt);
|
|
1228
|
+
return {
|
|
1229
|
+
reply: readNonEmptyString(params.run.reply) ?? params.fallbackReply ?? "",
|
|
1230
|
+
sessionKey: readNonEmptyString(params.run.sessionKey) ?? params.fallbackSessionKey,
|
|
1231
|
+
...readNonEmptyString(params.run.agentId) || params.fallbackAgentId ? { agentId: readNonEmptyString(params.run.agentId) ?? params.fallbackAgentId } : {},
|
|
1232
|
+
...readNonEmptyString(params.run.model) || params.fallbackModel ? { model: readNonEmptyString(params.run.model) ?? params.fallbackModel } : {},
|
|
1233
|
+
requestedAt,
|
|
1234
|
+
completedAt,
|
|
1235
|
+
durationMs: Number.isFinite(requestedAtMs) && Number.isFinite(completedAtMs) ? Math.max(0, completedAtMs - requestedAtMs) : 0
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1187
1238
|
function toSseFrame(event, data) {
|
|
1188
1239
|
return `event: ${event}
|
|
1189
1240
|
data: ${JSON.stringify(data)}
|
|
@@ -1361,11 +1412,16 @@ function collectInstalledSkillRecords(options) {
|
|
|
1361
1412
|
const listedSkills = skillsLoader?.listSkills(false) ?? [];
|
|
1362
1413
|
const records = listedSkills.map((skill) => {
|
|
1363
1414
|
const enabled = availableSkillSet.has(skill.name);
|
|
1415
|
+
const metadata = skillsLoader?.getSkillMetadata?.(skill.name);
|
|
1416
|
+
const description = readNonEmptyString(metadata?.description);
|
|
1417
|
+
const descriptionZh = readNonEmptyString(metadata?.description_zh) ?? readNonEmptyString(metadata?.descriptionZh) ?? readNonEmptyString(MARKETPLACE_ZH_COPY_BY_SLUG[skill.name]?.description);
|
|
1364
1418
|
return {
|
|
1365
1419
|
type: "skill",
|
|
1366
1420
|
id: skill.name,
|
|
1367
1421
|
spec: skill.name,
|
|
1368
1422
|
label: skill.name,
|
|
1423
|
+
...description ? { description } : {},
|
|
1424
|
+
...descriptionZh ? { descriptionZh } : {},
|
|
1369
1425
|
source: skill.source,
|
|
1370
1426
|
enabled,
|
|
1371
1427
|
runtimeStatus: enabled ? "enabled" : "disabled"
|
|
@@ -2336,6 +2392,23 @@ function createUiRouter(options) {
|
|
|
2336
2392
|
options.publish({ type: "config.updated", payload: { path: "secrets" } });
|
|
2337
2393
|
return c.json(ok(result));
|
|
2338
2394
|
});
|
|
2395
|
+
app.get("/api/chat/capabilities", async (c) => {
|
|
2396
|
+
const chatRuntime = options.chatRuntime;
|
|
2397
|
+
if (!chatRuntime) {
|
|
2398
|
+
return c.json(err("NOT_AVAILABLE", "chat runtime unavailable"), 503);
|
|
2399
|
+
}
|
|
2400
|
+
const query = c.req.query();
|
|
2401
|
+
const params = {
|
|
2402
|
+
sessionKey: readNonEmptyString(query.sessionKey),
|
|
2403
|
+
agentId: readNonEmptyString(query.agentId)
|
|
2404
|
+
};
|
|
2405
|
+
try {
|
|
2406
|
+
const capabilities = chatRuntime.getCapabilities ? await chatRuntime.getCapabilities(params) : { stopSupported: Boolean(chatRuntime.stopTurn) };
|
|
2407
|
+
return c.json(ok(capabilities));
|
|
2408
|
+
} catch (error) {
|
|
2409
|
+
return c.json(err("CHAT_RUNTIME_FAILED", String(error)), 500);
|
|
2410
|
+
}
|
|
2411
|
+
});
|
|
2339
2412
|
app.post("/api/chat/turn", async (c) => {
|
|
2340
2413
|
if (!options.chatRuntime) {
|
|
2341
2414
|
return c.json(err("NOT_AVAILABLE", "chat runtime unavailable"), 503);
|
|
@@ -2379,6 +2452,31 @@ function createUiRouter(options) {
|
|
|
2379
2452
|
return c.json(err("CHAT_TURN_FAILED", String(error)), 500);
|
|
2380
2453
|
}
|
|
2381
2454
|
});
|
|
2455
|
+
app.post("/api/chat/turn/stop", async (c) => {
|
|
2456
|
+
const chatRuntime = options.chatRuntime;
|
|
2457
|
+
if (!chatRuntime?.stopTurn) {
|
|
2458
|
+
return c.json(err("NOT_AVAILABLE", "chat turn stop is not supported by runtime"), 503);
|
|
2459
|
+
}
|
|
2460
|
+
const body = await readJson(c.req.raw);
|
|
2461
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
2462
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
2463
|
+
}
|
|
2464
|
+
const runId = readNonEmptyString(body.data.runId);
|
|
2465
|
+
if (!runId) {
|
|
2466
|
+
return c.json(err("INVALID_BODY", "runId is required"), 400);
|
|
2467
|
+
}
|
|
2468
|
+
const request = {
|
|
2469
|
+
runId,
|
|
2470
|
+
...readNonEmptyString(body.data.sessionKey) ? { sessionKey: readNonEmptyString(body.data.sessionKey) } : {},
|
|
2471
|
+
...readNonEmptyString(body.data.agentId) ? { agentId: readNonEmptyString(body.data.agentId) } : {}
|
|
2472
|
+
};
|
|
2473
|
+
try {
|
|
2474
|
+
const result = await chatRuntime.stopTurn(request);
|
|
2475
|
+
return c.json(ok(result));
|
|
2476
|
+
} catch (error) {
|
|
2477
|
+
return c.json(err("CHAT_TURN_STOP_FAILED", String(error)), 500);
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2382
2480
|
app.post("/api/chat/turn/stream", async (c) => {
|
|
2383
2481
|
const chatRuntime = options.chatRuntime;
|
|
2384
2482
|
if (!chatRuntime) {
|
|
@@ -2398,15 +2496,47 @@ function createUiRouter(options) {
|
|
|
2398
2496
|
const metadata = isRecord(body.data.metadata) ? body.data.metadata : void 0;
|
|
2399
2497
|
const requestedAgentId = readNonEmptyString(body.data.agentId) ?? resolveAgentIdFromSessionKey(sessionKey);
|
|
2400
2498
|
const requestedModel = readNonEmptyString(body.data.model);
|
|
2499
|
+
let runId = createChatRunId();
|
|
2500
|
+
const supportsManagedRuns = Boolean(chatRuntime.startTurnRun && chatRuntime.streamRun);
|
|
2501
|
+
let stopCapabilities = { stopSupported: Boolean(chatRuntime.stopTurn) };
|
|
2502
|
+
if (chatRuntime.getCapabilities) {
|
|
2503
|
+
try {
|
|
2504
|
+
stopCapabilities = await chatRuntime.getCapabilities({
|
|
2505
|
+
sessionKey,
|
|
2506
|
+
...requestedAgentId ? { agentId: requestedAgentId } : {}
|
|
2507
|
+
});
|
|
2508
|
+
} catch {
|
|
2509
|
+
stopCapabilities = {
|
|
2510
|
+
stopSupported: false,
|
|
2511
|
+
stopReason: "failed to resolve runtime stop capability"
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2401
2515
|
const request = {
|
|
2402
2516
|
message,
|
|
2403
2517
|
sessionKey,
|
|
2404
2518
|
channel: readNonEmptyString(body.data.channel) ?? "ui",
|
|
2405
2519
|
chatId: readNonEmptyString(body.data.chatId) ?? "web-ui",
|
|
2520
|
+
runId,
|
|
2406
2521
|
...requestedAgentId ? { agentId: requestedAgentId } : {},
|
|
2407
2522
|
...requestedModel ? { model: requestedModel } : {},
|
|
2408
2523
|
...metadata ? { metadata } : {}
|
|
2409
2524
|
};
|
|
2525
|
+
let managedRun = null;
|
|
2526
|
+
if (supportsManagedRuns && chatRuntime.startTurnRun) {
|
|
2527
|
+
try {
|
|
2528
|
+
managedRun = await chatRuntime.startTurnRun(request);
|
|
2529
|
+
} catch (error) {
|
|
2530
|
+
return c.json(err("CHAT_TURN_FAILED", String(error)), 500);
|
|
2531
|
+
}
|
|
2532
|
+
if (readNonEmptyString(managedRun.runId)) {
|
|
2533
|
+
runId = readNonEmptyString(managedRun.runId);
|
|
2534
|
+
}
|
|
2535
|
+
stopCapabilities = {
|
|
2536
|
+
stopSupported: managedRun.stopSupported,
|
|
2537
|
+
...readNonEmptyString(managedRun.stopReason) ? { stopReason: readNonEmptyString(managedRun.stopReason) } : {}
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2410
2540
|
const encoder = new TextEncoder();
|
|
2411
2541
|
const stream = new ReadableStream({
|
|
2412
2542
|
start: async (controller) => {
|
|
@@ -2415,9 +2545,65 @@ function createUiRouter(options) {
|
|
|
2415
2545
|
};
|
|
2416
2546
|
try {
|
|
2417
2547
|
push("ready", {
|
|
2418
|
-
sessionKey,
|
|
2419
|
-
requestedAt: requestedAt.toISOString()
|
|
2548
|
+
sessionKey: managedRun?.sessionKey ?? sessionKey,
|
|
2549
|
+
requestedAt: managedRun?.requestedAt ?? requestedAt.toISOString(),
|
|
2550
|
+
runId,
|
|
2551
|
+
stopSupported: stopCapabilities.stopSupported,
|
|
2552
|
+
...readNonEmptyString(stopCapabilities.stopReason) ? { stopReason: readNonEmptyString(stopCapabilities.stopReason) } : {}
|
|
2420
2553
|
});
|
|
2554
|
+
if (supportsManagedRuns && chatRuntime.streamRun) {
|
|
2555
|
+
let hasFinal2 = false;
|
|
2556
|
+
for await (const event of chatRuntime.streamRun({ runId })) {
|
|
2557
|
+
const typed = event;
|
|
2558
|
+
if (typed.type === "delta") {
|
|
2559
|
+
if (typed.delta) {
|
|
2560
|
+
push("delta", { delta: typed.delta });
|
|
2561
|
+
}
|
|
2562
|
+
continue;
|
|
2563
|
+
}
|
|
2564
|
+
if (typed.type === "session_event") {
|
|
2565
|
+
push("session_event", typed.event);
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
if (typed.type === "final") {
|
|
2569
|
+
const latestRun = chatRuntime.getRun ? await chatRuntime.getRun({ runId }) : null;
|
|
2570
|
+
const response = latestRun ? buildChatTurnViewFromRun({
|
|
2571
|
+
run: latestRun,
|
|
2572
|
+
fallbackSessionKey: sessionKey,
|
|
2573
|
+
fallbackAgentId: requestedAgentId,
|
|
2574
|
+
fallbackModel: requestedModel,
|
|
2575
|
+
fallbackReply: typed.result.reply
|
|
2576
|
+
}) : buildChatTurnView({
|
|
2577
|
+
result: typed.result,
|
|
2578
|
+
fallbackSessionKey: sessionKey,
|
|
2579
|
+
requestedAgentId,
|
|
2580
|
+
requestedModel,
|
|
2581
|
+
requestedAt,
|
|
2582
|
+
startedAtMs
|
|
2583
|
+
});
|
|
2584
|
+
hasFinal2 = true;
|
|
2585
|
+
push("final", response);
|
|
2586
|
+
options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
2587
|
+
continue;
|
|
2588
|
+
}
|
|
2589
|
+
if (typed.type === "error") {
|
|
2590
|
+
push("error", {
|
|
2591
|
+
code: "CHAT_TURN_FAILED",
|
|
2592
|
+
message: typed.error
|
|
2593
|
+
});
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
if (!hasFinal2) {
|
|
2598
|
+
push("error", {
|
|
2599
|
+
code: "CHAT_TURN_FAILED",
|
|
2600
|
+
message: "stream ended without a final result"
|
|
2601
|
+
});
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
push("done", { ok: true });
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2421
2607
|
const streamTurn = chatRuntime.processTurnStream;
|
|
2422
2608
|
if (!streamTurn) {
|
|
2423
2609
|
const result = await chatRuntime.processTurn(request);
|
|
@@ -2497,6 +2683,151 @@ function createUiRouter(options) {
|
|
|
2497
2683
|
}
|
|
2498
2684
|
});
|
|
2499
2685
|
});
|
|
2686
|
+
app.get("/api/chat/runs", async (c) => {
|
|
2687
|
+
const chatRuntime = options.chatRuntime;
|
|
2688
|
+
if (!chatRuntime?.listRuns) {
|
|
2689
|
+
return c.json(err("NOT_AVAILABLE", "chat run management unavailable"), 503);
|
|
2690
|
+
}
|
|
2691
|
+
const query = c.req.query();
|
|
2692
|
+
const sessionKey = readNonEmptyString(query.sessionKey);
|
|
2693
|
+
const states = readChatRunStates(query.states);
|
|
2694
|
+
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
2695
|
+
try {
|
|
2696
|
+
const data = await chatRuntime.listRuns({
|
|
2697
|
+
...sessionKey ? { sessionKey } : {},
|
|
2698
|
+
...states ? { states } : {},
|
|
2699
|
+
...Number.isFinite(limit) ? { limit } : {}
|
|
2700
|
+
});
|
|
2701
|
+
return c.json(ok(data));
|
|
2702
|
+
} catch (error) {
|
|
2703
|
+
return c.json(err("CHAT_RUN_QUERY_FAILED", String(error)), 500);
|
|
2704
|
+
}
|
|
2705
|
+
});
|
|
2706
|
+
app.get("/api/chat/runs/:runId", async (c) => {
|
|
2707
|
+
const chatRuntime = options.chatRuntime;
|
|
2708
|
+
if (!chatRuntime?.getRun) {
|
|
2709
|
+
return c.json(err("NOT_AVAILABLE", "chat run management unavailable"), 503);
|
|
2710
|
+
}
|
|
2711
|
+
const runId = readNonEmptyString(c.req.param("runId"));
|
|
2712
|
+
if (!runId) {
|
|
2713
|
+
return c.json(err("INVALID_PATH", "runId is required"), 400);
|
|
2714
|
+
}
|
|
2715
|
+
try {
|
|
2716
|
+
const run = await chatRuntime.getRun({ runId });
|
|
2717
|
+
if (!run) {
|
|
2718
|
+
return c.json(err("NOT_FOUND", `chat run not found: ${runId}`), 404);
|
|
2719
|
+
}
|
|
2720
|
+
return c.json(ok(run));
|
|
2721
|
+
} catch (error) {
|
|
2722
|
+
return c.json(err("CHAT_RUN_QUERY_FAILED", String(error)), 500);
|
|
2723
|
+
}
|
|
2724
|
+
});
|
|
2725
|
+
app.get("/api/chat/runs/:runId/stream", async (c) => {
|
|
2726
|
+
const chatRuntime = options.chatRuntime;
|
|
2727
|
+
const streamRun = chatRuntime?.streamRun;
|
|
2728
|
+
const getRun = chatRuntime?.getRun;
|
|
2729
|
+
if (!streamRun || !getRun) {
|
|
2730
|
+
return c.json(err("NOT_AVAILABLE", "chat run stream unavailable"), 503);
|
|
2731
|
+
}
|
|
2732
|
+
const runId = readNonEmptyString(c.req.param("runId"));
|
|
2733
|
+
if (!runId) {
|
|
2734
|
+
return c.json(err("INVALID_PATH", "runId is required"), 400);
|
|
2735
|
+
}
|
|
2736
|
+
const query = c.req.query();
|
|
2737
|
+
const fromEventIndex = typeof query.fromEventIndex === "string" ? Number.parseInt(query.fromEventIndex, 10) : void 0;
|
|
2738
|
+
const run = await getRun({ runId });
|
|
2739
|
+
if (!run) {
|
|
2740
|
+
return c.json(err("NOT_FOUND", `chat run not found: ${runId}`), 404);
|
|
2741
|
+
}
|
|
2742
|
+
const encoder = new TextEncoder();
|
|
2743
|
+
const stream = new ReadableStream({
|
|
2744
|
+
start: async (controller) => {
|
|
2745
|
+
const push = (event, data) => {
|
|
2746
|
+
controller.enqueue(encoder.encode(toSseFrame(event, data)));
|
|
2747
|
+
};
|
|
2748
|
+
try {
|
|
2749
|
+
push("ready", {
|
|
2750
|
+
sessionKey: run.sessionKey,
|
|
2751
|
+
requestedAt: run.requestedAt,
|
|
2752
|
+
runId: run.runId,
|
|
2753
|
+
stopSupported: run.stopSupported,
|
|
2754
|
+
...readNonEmptyString(run.stopReason) ? { stopReason: readNonEmptyString(run.stopReason) } : {}
|
|
2755
|
+
});
|
|
2756
|
+
let hasFinal = false;
|
|
2757
|
+
for await (const event of streamRun({
|
|
2758
|
+
runId: run.runId,
|
|
2759
|
+
...Number.isFinite(fromEventIndex) ? { fromEventIndex } : {}
|
|
2760
|
+
})) {
|
|
2761
|
+
const typed = event;
|
|
2762
|
+
if (typed.type === "delta") {
|
|
2763
|
+
if (typed.delta) {
|
|
2764
|
+
push("delta", { delta: typed.delta });
|
|
2765
|
+
}
|
|
2766
|
+
continue;
|
|
2767
|
+
}
|
|
2768
|
+
if (typed.type === "session_event") {
|
|
2769
|
+
push("session_event", typed.event);
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
if (typed.type === "final") {
|
|
2773
|
+
const latestRun = await getRun({ runId: run.runId });
|
|
2774
|
+
const response = latestRun ? buildChatTurnViewFromRun({
|
|
2775
|
+
run: latestRun,
|
|
2776
|
+
fallbackSessionKey: run.sessionKey,
|
|
2777
|
+
fallbackAgentId: run.agentId,
|
|
2778
|
+
fallbackModel: run.model,
|
|
2779
|
+
fallbackReply: typed.result.reply
|
|
2780
|
+
}) : buildChatTurnView({
|
|
2781
|
+
result: typed.result,
|
|
2782
|
+
fallbackSessionKey: run.sessionKey,
|
|
2783
|
+
requestedAgentId: run.agentId,
|
|
2784
|
+
requestedModel: run.model,
|
|
2785
|
+
requestedAt: new Date(run.requestedAt),
|
|
2786
|
+
startedAtMs: Date.parse(run.requestedAt)
|
|
2787
|
+
});
|
|
2788
|
+
hasFinal = true;
|
|
2789
|
+
push("final", response);
|
|
2790
|
+
continue;
|
|
2791
|
+
}
|
|
2792
|
+
if (typed.type === "error") {
|
|
2793
|
+
push("error", {
|
|
2794
|
+
code: "CHAT_TURN_FAILED",
|
|
2795
|
+
message: typed.error
|
|
2796
|
+
});
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if (!hasFinal) {
|
|
2801
|
+
const latestRun = await getRun({ runId: run.runId });
|
|
2802
|
+
if (latestRun?.state === "failed") {
|
|
2803
|
+
push("error", {
|
|
2804
|
+
code: "CHAT_TURN_FAILED",
|
|
2805
|
+
message: latestRun.error ?? "chat run failed"
|
|
2806
|
+
});
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
push("done", { ok: true });
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
push("error", {
|
|
2813
|
+
code: "CHAT_TURN_FAILED",
|
|
2814
|
+
message: String(error)
|
|
2815
|
+
});
|
|
2816
|
+
} finally {
|
|
2817
|
+
controller.close();
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
return new Response(stream, {
|
|
2822
|
+
status: 200,
|
|
2823
|
+
headers: {
|
|
2824
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2825
|
+
"Cache-Control": "no-cache, no-transform",
|
|
2826
|
+
"Connection": "keep-alive",
|
|
2827
|
+
"X-Accel-Buffering": "no"
|
|
2828
|
+
}
|
|
2829
|
+
});
|
|
2830
|
+
});
|
|
2500
2831
|
app.get("/api/sessions", (c) => {
|
|
2501
2832
|
const query = c.req.query();
|
|
2502
2833
|
const q = typeof query.q === "string" ? query.q : void 0;
|
|
@@ -2610,6 +2941,12 @@ function createUiRouter(options) {
|
|
|
2610
2941
|
if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "contextTokens")) {
|
|
2611
2942
|
options.publish({ type: "config.updated", payload: { path: "agents.defaults.contextTokens" } });
|
|
2612
2943
|
}
|
|
2944
|
+
if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engine")) {
|
|
2945
|
+
options.publish({ type: "config.updated", payload: { path: "agents.defaults.engine" } });
|
|
2946
|
+
}
|
|
2947
|
+
if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "engineConfig")) {
|
|
2948
|
+
options.publish({ type: "config.updated", payload: { path: "agents.defaults.engineConfig" } });
|
|
2949
|
+
}
|
|
2613
2950
|
options.publish({ type: "config.updated", payload: { path: "agents.list" } });
|
|
2614
2951
|
options.publish({ type: "config.updated", payload: { path: "bindings" } });
|
|
2615
2952
|
options.publish({ type: "config.updated", payload: { path: "session" } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Nextclaw UI/API server.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@hono/node-server": "^1.13.3",
|
|
18
|
-
"@nextclaw/openclaw-compat": "^0.
|
|
18
|
+
"@nextclaw/openclaw-compat": "^0.2.0",
|
|
19
19
|
"hono": "^4.6.2",
|
|
20
20
|
"ws": "^8.18.0",
|
|
21
|
-
"@nextclaw/core": "^0.
|
|
21
|
+
"@nextclaw/core": "^0.7.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^20.17.6",
|