@nextclaw/server 0.6.0 → 0.6.2

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 CHANGED
@@ -278,9 +278,42 @@ type ChatTurnStopResult = {
278
278
  sessionKey?: string;
279
279
  reason?: string;
280
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
+ };
281
301
  type UiChatRuntime = {
282
302
  processTurn: (params: ChatTurnRequest) => Promise<ChatTurnResult>;
283
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;
284
317
  getCapabilities?: (params: Pick<ChatTurnRequest, "sessionKey" | "agentId">) => Promise<ChatCapabilitiesView> | ChatCapabilitiesView;
285
318
  stopTurn?: (params: ChatTurnStopRequest) => Promise<ChatTurnStopResult> | ChatTurnStopResult;
286
319
  };
@@ -598,6 +631,11 @@ type UiServerEvent = {
598
631
  payload: {
599
632
  path: string;
600
633
  };
634
+ } | {
635
+ type: "run.updated";
636
+ payload: {
637
+ run: ChatRunView;
638
+ };
601
639
  } | {
602
640
  type: "config.reload.started";
603
641
  payload?: Record<string, unknown>;
@@ -675,4 +713,4 @@ declare function deleteSession(configPath: string, key: string): boolean;
675
713
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
676
714
  declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
677
715
 
678
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ChatCapabilitiesView, 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 };
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",
@@ -1193,6 +1195,19 @@ function createChatRunId() {
1193
1195
  const rand = Math.random().toString(36).slice(2, 10);
1194
1196
  return `run-${now}-${rand}`;
1195
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
+ }
1196
1211
  function buildChatTurnView(params) {
1197
1212
  const completedAt = /* @__PURE__ */ new Date();
1198
1213
  return {
@@ -1205,6 +1220,21 @@ function buildChatTurnView(params) {
1205
1220
  durationMs: Math.max(0, completedAt.getTime() - params.startedAtMs)
1206
1221
  };
1207
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
+ }
1208
1238
  function toSseFrame(event, data) {
1209
1239
  return `event: ${event}
1210
1240
  data: ${JSON.stringify(data)}
@@ -2466,7 +2496,8 @@ function createUiRouter(options) {
2466
2496
  const metadata = isRecord(body.data.metadata) ? body.data.metadata : void 0;
2467
2497
  const requestedAgentId = readNonEmptyString(body.data.agentId) ?? resolveAgentIdFromSessionKey(sessionKey);
2468
2498
  const requestedModel = readNonEmptyString(body.data.model);
2469
- const runId = createChatRunId();
2499
+ let runId = createChatRunId();
2500
+ const supportsManagedRuns = Boolean(chatRuntime.startTurnRun && chatRuntime.streamRun);
2470
2501
  let stopCapabilities = { stopSupported: Boolean(chatRuntime.stopTurn) };
2471
2502
  if (chatRuntime.getCapabilities) {
2472
2503
  try {
@@ -2491,6 +2522,21 @@ function createUiRouter(options) {
2491
2522
  ...requestedModel ? { model: requestedModel } : {},
2492
2523
  ...metadata ? { metadata } : {}
2493
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
+ }
2494
2540
  const encoder = new TextEncoder();
2495
2541
  const stream = new ReadableStream({
2496
2542
  start: async (controller) => {
@@ -2499,12 +2545,65 @@ function createUiRouter(options) {
2499
2545
  };
2500
2546
  try {
2501
2547
  push("ready", {
2502
- sessionKey,
2503
- requestedAt: requestedAt.toISOString(),
2548
+ sessionKey: managedRun?.sessionKey ?? sessionKey,
2549
+ requestedAt: managedRun?.requestedAt ?? requestedAt.toISOString(),
2504
2550
  runId,
2505
2551
  stopSupported: stopCapabilities.stopSupported,
2506
2552
  ...readNonEmptyString(stopCapabilities.stopReason) ? { stopReason: readNonEmptyString(stopCapabilities.stopReason) } : {}
2507
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
+ }
2508
2607
  const streamTurn = chatRuntime.processTurnStream;
2509
2608
  if (!streamTurn) {
2510
2609
  const result = await chatRuntime.processTurn(request);
@@ -2584,6 +2683,151 @@ function createUiRouter(options) {
2584
2683
  }
2585
2684
  });
2586
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
+ });
2587
2831
  app.get("/api/sessions", (c) => {
2588
2832
  const query = c.req.query();
2589
2833
  const q = typeof query.q === "string" ? query.q : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",