@nextclaw/server 0.6.7 → 0.6.8

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
@@ -117,6 +117,8 @@ type SessionEntryView = {
117
117
  updatedAt: string;
118
118
  label?: string;
119
119
  preferredModel?: string;
120
+ sessionType: string;
121
+ sessionTypeMutable: boolean;
120
122
  messageCount: number;
121
123
  lastRole?: string;
122
124
  lastTimestamp?: string;
@@ -144,6 +146,8 @@ type SessionHistoryView = {
144
146
  key: string;
145
147
  totalMessages: number;
146
148
  totalEvents: number;
149
+ sessionType: string;
150
+ sessionTypeMutable: boolean;
147
151
  metadata: Record<string, unknown>;
148
152
  messages: SessionMessageView[];
149
153
  events: SessionEventView[];
@@ -151,6 +155,7 @@ type SessionHistoryView = {
151
155
  type SessionPatchUpdate = {
152
156
  label?: string | null;
153
157
  preferredModel?: string | null;
158
+ sessionType?: string | null;
154
159
  clearHistory?: boolean;
155
160
  };
156
161
  type CronScheduleView = {
@@ -300,6 +305,14 @@ type ChatCapabilitiesView = {
300
305
  stopSupported: boolean;
301
306
  stopReason?: string;
302
307
  };
308
+ type ChatSessionTypeOptionView = {
309
+ value: string;
310
+ label: string;
311
+ };
312
+ type ChatSessionTypesView = {
313
+ defaultType: string;
314
+ options: ChatSessionTypeOptionView[];
315
+ };
303
316
  type ChatCommandOptionView = {
304
317
  name: string;
305
318
  description: string;
@@ -363,6 +376,7 @@ type UiChatRuntime = {
363
376
  limit?: number;
364
377
  }) => Promise<ChatRunListView> | ChatRunListView;
365
378
  getCapabilities?: (params: Pick<ChatTurnRequest, "sessionKey" | "agentId">) => Promise<ChatCapabilitiesView> | ChatCapabilitiesView;
379
+ listSessionTypes?: () => Promise<ChatSessionTypesView> | ChatSessionTypesView;
366
380
  stopTurn?: (params: ChatTurnStopRequest) => Promise<ChatTurnStopResult> | ChatTurnStopResult;
367
381
  };
368
382
  type ConfigView = {
@@ -781,15 +795,22 @@ declare function createCustomProvider(configPath: string, patch?: ProviderConfig
781
795
  declare function deleteCustomProvider(configPath: string, providerName: string): boolean | null;
782
796
  declare function testProviderConnection(configPath: string, providerName: string, patch: ProviderConnectionTestRequest): Promise<ProviderConnectionTestResult | null>;
783
797
  declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>): Record<string, unknown> | null;
798
+ declare const DEFAULT_SESSION_TYPE = "native";
799
+ declare class SessionPatchValidationError extends Error {
800
+ readonly code: "SESSION_TYPE_INVALID" | "SESSION_TYPE_IMMUTABLE" | "SESSION_TYPE_UNAVAILABLE";
801
+ constructor(code: "SESSION_TYPE_INVALID" | "SESSION_TYPE_IMMUTABLE" | "SESSION_TYPE_UNAVAILABLE", message: string);
802
+ }
784
803
  declare function listSessions(configPath: string, query?: {
785
804
  q?: string;
786
805
  limit?: number;
787
806
  activeMinutes?: number;
788
807
  }): SessionsListView;
789
808
  declare function getSessionHistory(configPath: string, key: string, limit?: number): SessionHistoryView | null;
790
- declare function patchSession(configPath: string, key: string, patch: SessionPatchUpdate): SessionHistoryView | null;
809
+ declare function patchSession(configPath: string, key: string, patch: SessionPatchUpdate, options?: {
810
+ availableSessionTypes?: string[];
811
+ }): SessionHistoryView | null;
791
812
  declare function deleteSession(configPath: string, key: string): boolean;
792
813
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
793
814
  declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
794
815
 
795
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type BindingPeerView, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, 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 MarketplacePluginInstallKind, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallKind, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderAuthImportResult, type ProviderAuthPollRequest, type ProviderAuthPollResult, type ProviderAuthStartRequest, type ProviderAuthStartResult, 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 };
816
+ export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type BindingPeerView, type ChannelSpecView, type ChatCapabilitiesView, type ChatCommandOptionView, type ChatCommandView, type ChatCommandsView, type ChatRunListView, type ChatRunState, type ChatRunView, type ChatSessionTypeOptionView, type ChatSessionTypesView, 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, DEFAULT_SESSION_TYPE, 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 MarketplacePluginInstallKind, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallKind, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderAuthImportResult, type ProviderAuthPollRequest, type ProviderAuthPollResult, type ProviderAuthStartRequest, type ProviderAuthStartResult, 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, SessionPatchValidationError, 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
@@ -875,6 +875,36 @@ function normalizeSessionKey(value) {
875
875
  function createSessionManager(config) {
876
876
  return new SessionManager(getWorkspacePathFromConfig(config));
877
877
  }
878
+ var DEFAULT_SESSION_TYPE = "native";
879
+ var SESSION_TYPE_METADATA_KEY = "session_type";
880
+ function normalizeSessionType(value) {
881
+ if (typeof value !== "string") {
882
+ return null;
883
+ }
884
+ const normalized = value.trim().toLowerCase();
885
+ return normalized.length > 0 ? normalized : null;
886
+ }
887
+ function readSessionType(session) {
888
+ const normalized = normalizeSessionType(session.metadata[SESSION_TYPE_METADATA_KEY]);
889
+ return normalized ?? DEFAULT_SESSION_TYPE;
890
+ }
891
+ function countUserMessages(session) {
892
+ return session.messages.reduce((total, message) => {
893
+ const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
894
+ return role === "user" ? total + 1 : total;
895
+ }, 0);
896
+ }
897
+ function isSessionTypeMutable(session) {
898
+ const activeUiRunId = typeof session.metadata.ui_active_run_id === "string" ? session.metadata.ui_active_run_id.trim() : "";
899
+ return countUserMessages(session) === 0 && activeUiRunId.length === 0;
900
+ }
901
+ var SessionPatchValidationError = class extends Error {
902
+ constructor(code, message) {
903
+ super(message);
904
+ this.code = code;
905
+ this.name = "SessionPatchValidationError";
906
+ }
907
+ };
878
908
  function listSessions(configPath, query) {
879
909
  const config = loadConfigOrDefault(configPath);
880
910
  const sessionManager = createSessionManager(config);
@@ -895,12 +925,22 @@ function listSessions(configPath, query) {
895
925
  const preferredModel = typeof metadata.preferred_model === "string" ? metadata.preferred_model.trim() : "";
896
926
  const createdAt = typeof item.created_at === "string" ? item.created_at : (/* @__PURE__ */ new Date(0)).toISOString();
897
927
  const updatedAt = typeof item.updated_at === "string" ? item.updated_at : createdAt;
928
+ const sessionType = readSessionType({
929
+ metadata,
930
+ messages
931
+ });
932
+ const sessionTypeMutable = isSessionTypeMutable({
933
+ metadata,
934
+ messages
935
+ });
898
936
  return {
899
937
  key,
900
938
  createdAt,
901
939
  updatedAt,
902
940
  label: label || void 0,
903
941
  preferredModel: preferredModel || void 0,
942
+ sessionType,
943
+ sessionTypeMutable,
904
944
  messageCount: messages.length,
905
945
  lastRole: typeof lastMessage?.role === "string" ? lastMessage.role : void 0,
906
946
  lastTimestamp: typeof lastMessage?.timestamp === "string" ? lastMessage.timestamp : void 0
@@ -940,10 +980,14 @@ function getSessionHistory(configPath, key, limit) {
940
980
  const safeEventLimit = Math.min(2e3, Math.max(50, safeLimit * 4));
941
981
  const allEvents = session.events ?? [];
942
982
  const events = allEvents.length > safeEventLimit ? allEvents.slice(-safeEventLimit) : allEvents;
983
+ const sessionType = readSessionType(session);
984
+ const sessionTypeMutable = isSessionTypeMutable(session);
943
985
  return {
944
986
  key: normalizedKey,
945
987
  totalMessages: allMessages.length,
946
988
  totalEvents: allEvents.length,
989
+ sessionType,
990
+ sessionTypeMutable,
947
991
  metadata: session.metadata,
948
992
  messages: messages.map((message) => {
949
993
  const entry = {
@@ -990,7 +1034,7 @@ function getSessionHistory(configPath, key, limit) {
990
1034
  })
991
1035
  };
992
1036
  }
993
- function patchSession(configPath, key, patch) {
1037
+ function patchSession(configPath, key, patch, options) {
994
1038
  const normalizedKey = normalizeSessionKey(key);
995
1039
  if (!normalizedKey) {
996
1040
  return null;
@@ -1020,6 +1064,32 @@ function patchSession(configPath, key, patch) {
1020
1064
  delete session.metadata.preferred_model;
1021
1065
  }
1022
1066
  }
1067
+ if (Object.prototype.hasOwnProperty.call(patch, "sessionType")) {
1068
+ const normalizedSessionType = normalizeSessionType(patch.sessionType);
1069
+ if (!normalizedSessionType) {
1070
+ throw new SessionPatchValidationError(
1071
+ "SESSION_TYPE_INVALID",
1072
+ "sessionType must be a non-empty string"
1073
+ );
1074
+ }
1075
+ if (!isSessionTypeMutable(session)) {
1076
+ throw new SessionPatchValidationError(
1077
+ "SESSION_TYPE_IMMUTABLE",
1078
+ "sessionType cannot be changed after the first user message"
1079
+ );
1080
+ }
1081
+ const availableSessionTypes = new Set(
1082
+ (options?.availableSessionTypes ?? [DEFAULT_SESSION_TYPE]).map((item) => normalizeSessionType(item)).filter((item) => Boolean(item))
1083
+ );
1084
+ availableSessionTypes.add(DEFAULT_SESSION_TYPE);
1085
+ if (!availableSessionTypes.has(normalizedSessionType)) {
1086
+ throw new SessionPatchValidationError(
1087
+ "SESSION_TYPE_UNAVAILABLE",
1088
+ `sessionType is unavailable: ${normalizedSessionType}`
1089
+ );
1090
+ }
1091
+ session.metadata[SESSION_TYPE_METADATA_KEY] = normalizedSessionType;
1092
+ }
1023
1093
  session.updatedAt = /* @__PURE__ */ new Date();
1024
1094
  sessionManager.save(session);
1025
1095
  return getSessionHistory(configPath, normalizedKey, 200);
@@ -1851,6 +1921,67 @@ function readNonEmptyString(value) {
1851
1921
  const trimmed = value.trim();
1852
1922
  return trimmed || void 0;
1853
1923
  }
1924
+ function normalizeSessionType2(value) {
1925
+ return readNonEmptyString(value)?.toLowerCase();
1926
+ }
1927
+ function resolveSessionTypeLabel(sessionType) {
1928
+ if (sessionType === "native") {
1929
+ return "Native";
1930
+ }
1931
+ if (sessionType === "codex-sdk") {
1932
+ return "Codex";
1933
+ }
1934
+ if (sessionType === "claude-agent-sdk") {
1935
+ return "Claude Code";
1936
+ }
1937
+ return sessionType;
1938
+ }
1939
+ async function buildChatSessionTypesView(chatRuntime) {
1940
+ if (!chatRuntime?.listSessionTypes) {
1941
+ return {
1942
+ defaultType: DEFAULT_SESSION_TYPE,
1943
+ options: [{ value: DEFAULT_SESSION_TYPE, label: resolveSessionTypeLabel(DEFAULT_SESSION_TYPE) }]
1944
+ };
1945
+ }
1946
+ const payload = await chatRuntime.listSessionTypes();
1947
+ const deduped = /* @__PURE__ */ new Map();
1948
+ for (const rawOption of payload.options ?? []) {
1949
+ const normalized = normalizeSessionType2(rawOption.value);
1950
+ if (!normalized) {
1951
+ continue;
1952
+ }
1953
+ deduped.set(normalized, {
1954
+ value: normalized,
1955
+ label: readNonEmptyString(rawOption.label) ?? resolveSessionTypeLabel(normalized)
1956
+ });
1957
+ }
1958
+ if (!deduped.has(DEFAULT_SESSION_TYPE)) {
1959
+ deduped.set(DEFAULT_SESSION_TYPE, {
1960
+ value: DEFAULT_SESSION_TYPE,
1961
+ label: resolveSessionTypeLabel(DEFAULT_SESSION_TYPE)
1962
+ });
1963
+ }
1964
+ const defaultType = normalizeSessionType2(payload.defaultType) ?? DEFAULT_SESSION_TYPE;
1965
+ if (!deduped.has(defaultType)) {
1966
+ deduped.set(defaultType, {
1967
+ value: defaultType,
1968
+ label: resolveSessionTypeLabel(defaultType)
1969
+ });
1970
+ }
1971
+ const options = Array.from(deduped.values()).sort((left, right) => {
1972
+ if (left.value === DEFAULT_SESSION_TYPE) {
1973
+ return -1;
1974
+ }
1975
+ if (right.value === DEFAULT_SESSION_TYPE) {
1976
+ return 1;
1977
+ }
1978
+ return left.value.localeCompare(right.value);
1979
+ });
1980
+ return {
1981
+ defaultType,
1982
+ options
1983
+ };
1984
+ }
1854
1985
  function resolveAgentIdFromSessionKey(sessionKey) {
1855
1986
  const parsed = NextclawCore.parseAgentScopedSessionKey(sessionKey);
1856
1987
  const agentId = readNonEmptyString(parsed?.agentId);
@@ -3052,6 +3183,14 @@ function createUiRouter(options) {
3052
3183
  return c.json(err("CHAT_RUNTIME_FAILED", String(error)), 500);
3053
3184
  }
3054
3185
  });
3186
+ app.get("/api/chat/session-types", async (c) => {
3187
+ try {
3188
+ const payload = await buildChatSessionTypesView(options.chatRuntime);
3189
+ return c.json(ok(payload));
3190
+ } catch (error) {
3191
+ return c.json(err("CHAT_SESSION_TYPES_FAILED", String(error)), 500);
3192
+ }
3193
+ });
3055
3194
  app.get("/api/chat/commands", async (c) => {
3056
3195
  try {
3057
3196
  const config = loadConfigOrDefault(options.configPath);
@@ -3524,7 +3663,25 @@ function createUiRouter(options) {
3524
3663
  if (!body.ok || !body.data || typeof body.data !== "object") {
3525
3664
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
3526
3665
  }
3527
- const data = patchSession(options.configPath, key, body.data);
3666
+ let availableSessionTypes;
3667
+ if (Object.prototype.hasOwnProperty.call(body.data, "sessionType")) {
3668
+ const sessionTypes = await buildChatSessionTypesView(options.chatRuntime);
3669
+ availableSessionTypes = sessionTypes.options.map((item) => item.value);
3670
+ }
3671
+ let data;
3672
+ try {
3673
+ data = patchSession(options.configPath, key, body.data, {
3674
+ ...availableSessionTypes ? { availableSessionTypes } : {}
3675
+ });
3676
+ } catch (error) {
3677
+ if (error instanceof SessionPatchValidationError) {
3678
+ if (error.code === "SESSION_TYPE_IMMUTABLE") {
3679
+ return c.json(err(error.code, error.message), 409);
3680
+ }
3681
+ return c.json(err(error.code, error.message), 400);
3682
+ }
3683
+ throw error;
3684
+ }
3528
3685
  if (!data) {
3529
3686
  return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
3530
3687
  }
@@ -3729,6 +3886,8 @@ function startUiServer(options) {
3729
3886
  };
3730
3887
  }
3731
3888
  export {
3889
+ DEFAULT_SESSION_TYPE,
3890
+ SessionPatchValidationError,
3732
3891
  buildConfigMeta,
3733
3892
  buildConfigSchemaView,
3734
3893
  buildConfigView,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
@@ -17,9 +17,9 @@
17
17
  "@hono/node-server": "^1.13.3",
18
18
  "hono": "^4.6.2",
19
19
  "ws": "^8.18.0",
20
- "@nextclaw/openclaw-compat": "0.2.2",
21
- "@nextclaw/runtime": "0.1.2",
22
- "@nextclaw/core": "0.7.3"
20
+ "@nextclaw/openclaw-compat": "0.2.3",
21
+ "@nextclaw/core": "0.7.4",
22
+ "@nextclaw/runtime": "0.1.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^20.17.6",