@nextclaw/server 0.4.10 → 0.4.12

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
@@ -32,6 +32,7 @@ type AgentProfileView = {
32
32
  workspace?: string;
33
33
  model?: string;
34
34
  maxTokens?: number;
35
+ contextTokens?: number;
35
36
  maxToolIterations?: number;
36
37
  };
37
38
  type BindingPeerView = {
@@ -52,8 +53,43 @@ type SessionConfigView = {
52
53
  maxPingPongTurns?: number;
53
54
  };
54
55
  };
56
+ type SessionEntryView = {
57
+ key: string;
58
+ createdAt: string;
59
+ updatedAt: string;
60
+ label?: string;
61
+ preferredModel?: string;
62
+ messageCount: number;
63
+ lastRole?: string;
64
+ lastTimestamp?: string;
65
+ };
66
+ type SessionsListView = {
67
+ sessions: SessionEntryView[];
68
+ total: number;
69
+ };
70
+ type SessionMessageView = {
71
+ role: string;
72
+ content: string;
73
+ timestamp: string;
74
+ name?: string;
75
+ tool_call_id?: string;
76
+ };
77
+ type SessionHistoryView = {
78
+ key: string;
79
+ totalMessages: number;
80
+ metadata: Record<string, unknown>;
81
+ messages: SessionMessageView[];
82
+ };
83
+ type SessionPatchUpdate = {
84
+ label?: string | null;
85
+ preferredModel?: string | null;
86
+ clearHistory?: boolean;
87
+ };
55
88
  type RuntimeConfigUpdate = {
56
89
  agents?: {
90
+ defaults?: {
91
+ contextTokens?: number;
92
+ };
57
93
  list?: AgentProfileView[];
58
94
  };
59
95
  bindings?: AgentBindingView[];
@@ -65,6 +101,7 @@ type ConfigView = {
65
101
  model: string;
66
102
  workspace?: string;
67
103
  maxTokens?: number;
104
+ contextTokens?: number;
68
105
  maxToolIterations?: number;
69
106
  };
70
107
  list?: AgentProfileView[];
@@ -234,6 +271,14 @@ declare function loadConfigOrDefault(configPath: string): Config;
234
271
  declare function updateModel(configPath: string, model: string): ConfigView;
235
272
  declare function updateProvider(configPath: string, providerName: string, patch: ProviderConfigUpdate): ProviderConfigView | null;
236
273
  declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>): Record<string, unknown> | null;
274
+ declare function listSessions(configPath: string, query?: {
275
+ q?: string;
276
+ limit?: number;
277
+ activeMinutes?: number;
278
+ }): SessionsListView;
279
+ declare function getSessionHistory(configPath: string, key: string, limit?: number): SessionHistoryView | null;
280
+ declare function patchSession(configPath: string, key: string, patch: SessionPatchUpdate): SessionHistoryView | null;
281
+ declare function deleteSession(configPath: string, key: string): boolean;
237
282
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
238
283
 
239
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type ProviderConfigUpdate, type ProviderConfigView, type ProviderSpecView, type RuntimeConfigUpdate, type SessionConfigView, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, executeConfigAction, loadConfigOrDefault, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime };
284
+ export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type ProviderConfigUpdate, type ProviderConfigView, type ProviderSpecView, type RuntimeConfigUpdate, type SessionConfigView, type SessionEntryView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime };
package/dist/index.js CHANGED
@@ -20,7 +20,9 @@ import {
20
20
  buildConfigSchema,
21
21
  findProviderByName,
22
22
  getPackageVersion,
23
- isSensitiveConfigPath
23
+ isSensitiveConfigPath,
24
+ SessionManager,
25
+ getWorkspacePathFromConfig
24
26
  } from "@nextclaw/core";
25
27
  var MASK_MIN_LENGTH = 8;
26
28
  var EXTRA_SENSITIVE_PATH_PATTERNS = [/authorization/i, /cookie/i, /session/i, /bearer/i];
@@ -391,8 +393,145 @@ function updateChannel(configPath, channelName, patch) {
391
393
  uiHints
392
394
  );
393
395
  }
396
+ function normalizeSessionKey(value) {
397
+ return value.trim();
398
+ }
399
+ function createSessionManager(config) {
400
+ return new SessionManager(getWorkspacePathFromConfig(config));
401
+ }
402
+ function listSessions(configPath, query) {
403
+ const config = loadConfigOrDefault(configPath);
404
+ const sessionManager = createSessionManager(config);
405
+ const now = Date.now();
406
+ const activeMinutes = typeof query?.activeMinutes === "number" ? Math.max(0, Math.trunc(query.activeMinutes)) : 0;
407
+ const q = (query?.q ?? "").trim().toLowerCase();
408
+ const limit = typeof query?.limit === "number" ? Math.max(0, Math.trunc(query.limit)) : 0;
409
+ const entries = sessionManager.listSessions().map((item) => {
410
+ const key = typeof item.key === "string" ? normalizeSessionKey(item.key) : "";
411
+ if (!key) {
412
+ return null;
413
+ }
414
+ const session = sessionManager.getIfExists(key);
415
+ const messages = session?.messages ?? [];
416
+ const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
417
+ const metadata = item.metadata && typeof item.metadata === "object" ? item.metadata : {};
418
+ const label = typeof metadata.label === "string" ? metadata.label.trim() : "";
419
+ const preferredModel = typeof metadata.preferred_model === "string" ? metadata.preferred_model.trim() : "";
420
+ const createdAt = typeof item.created_at === "string" ? item.created_at : (/* @__PURE__ */ new Date(0)).toISOString();
421
+ const updatedAt = typeof item.updated_at === "string" ? item.updated_at : createdAt;
422
+ return {
423
+ key,
424
+ createdAt,
425
+ updatedAt,
426
+ label: label || void 0,
427
+ preferredModel: preferredModel || void 0,
428
+ messageCount: messages.length,
429
+ lastRole: typeof lastMessage?.role === "string" ? lastMessage.role : void 0,
430
+ lastTimestamp: typeof lastMessage?.timestamp === "string" ? lastMessage.timestamp : void 0
431
+ };
432
+ }).filter((item) => Boolean(item));
433
+ const filtered = entries.filter((entry) => {
434
+ if (activeMinutes > 0) {
435
+ const ageMs = now - new Date(entry.updatedAt).getTime();
436
+ if (!Number.isFinite(ageMs) || ageMs > activeMinutes * 6e4) {
437
+ return false;
438
+ }
439
+ }
440
+ if (!q) {
441
+ return true;
442
+ }
443
+ return entry.key.toLowerCase().includes(q) || (entry.label ?? "").toLowerCase().includes(q);
444
+ });
445
+ filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
446
+ const total = filtered.length;
447
+ const sessions = limit > 0 ? filtered.slice(0, limit) : filtered;
448
+ return { sessions, total };
449
+ }
450
+ function getSessionHistory(configPath, key, limit) {
451
+ const normalizedKey = normalizeSessionKey(key);
452
+ if (!normalizedKey) {
453
+ return null;
454
+ }
455
+ const config = loadConfigOrDefault(configPath);
456
+ const sessionManager = createSessionManager(config);
457
+ const session = sessionManager.getIfExists(normalizedKey);
458
+ if (!session) {
459
+ return null;
460
+ }
461
+ const safeLimit = typeof limit === "number" ? Math.min(500, Math.max(1, Math.trunc(limit))) : 200;
462
+ const allMessages = session.messages;
463
+ const messages = allMessages.length > safeLimit ? allMessages.slice(-safeLimit) : allMessages;
464
+ return {
465
+ key: normalizedKey,
466
+ totalMessages: allMessages.length,
467
+ metadata: session.metadata,
468
+ messages: messages.map((message) => {
469
+ const entry = {
470
+ role: message.role,
471
+ content: message.content,
472
+ timestamp: message.timestamp
473
+ };
474
+ if (typeof message.name === "string") {
475
+ entry.name = message.name;
476
+ }
477
+ if (typeof message.tool_call_id === "string") {
478
+ entry.tool_call_id = message.tool_call_id;
479
+ }
480
+ return entry;
481
+ })
482
+ };
483
+ }
484
+ function patchSession(configPath, key, patch) {
485
+ const normalizedKey = normalizeSessionKey(key);
486
+ if (!normalizedKey) {
487
+ return null;
488
+ }
489
+ const config = loadConfigOrDefault(configPath);
490
+ const sessionManager = createSessionManager(config);
491
+ const session = sessionManager.getIfExists(normalizedKey);
492
+ if (!session) {
493
+ return null;
494
+ }
495
+ if (patch.clearHistory) {
496
+ sessionManager.clear(session);
497
+ }
498
+ if (Object.prototype.hasOwnProperty.call(patch, "label")) {
499
+ const label = typeof patch.label === "string" ? patch.label.trim() : "";
500
+ if (label) {
501
+ session.metadata.label = label;
502
+ } else {
503
+ delete session.metadata.label;
504
+ }
505
+ }
506
+ if (Object.prototype.hasOwnProperty.call(patch, "preferredModel")) {
507
+ const preferredModel = typeof patch.preferredModel === "string" ? patch.preferredModel.trim() : "";
508
+ if (preferredModel) {
509
+ session.metadata.preferred_model = preferredModel;
510
+ } else {
511
+ delete session.metadata.preferred_model;
512
+ }
513
+ }
514
+ session.updatedAt = /* @__PURE__ */ new Date();
515
+ sessionManager.save(session);
516
+ return getSessionHistory(configPath, normalizedKey, 200);
517
+ }
518
+ function deleteSession(configPath, key) {
519
+ const normalizedKey = normalizeSessionKey(key);
520
+ if (!normalizedKey) {
521
+ return false;
522
+ }
523
+ const config = loadConfigOrDefault(configPath);
524
+ const sessionManager = createSessionManager(config);
525
+ return sessionManager.delete(normalizedKey);
526
+ }
394
527
  function updateRuntime(configPath, patch) {
395
528
  const config = loadConfigOrDefault(configPath);
529
+ if (patch.agents?.defaults && Object.prototype.hasOwnProperty.call(patch.agents.defaults, "contextTokens")) {
530
+ const nextContextTokens = patch.agents.defaults.contextTokens;
531
+ if (typeof nextContextTokens === "number" && Number.isFinite(nextContextTokens)) {
532
+ config.agents.defaults.contextTokens = Math.max(1e3, Math.trunc(nextContextTokens));
533
+ }
534
+ }
396
535
  if (patch.agents && Object.prototype.hasOwnProperty.call(patch.agents, "list")) {
397
536
  config.agents.list = (patch.agents.list ?? []).map((entry) => ({
398
537
  ...entry,
@@ -489,12 +628,59 @@ function createUiRouter(options) {
489
628
  options.publish({ type: "config.updated", payload: { path: `channels.${channel}` } });
490
629
  return c.json(ok(result));
491
630
  });
631
+ app.get("/api/sessions", (c) => {
632
+ const query = c.req.query();
633
+ const q = typeof query.q === "string" ? query.q : void 0;
634
+ const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
635
+ const activeMinutes = typeof query.activeMinutes === "string" ? Number.parseInt(query.activeMinutes, 10) : void 0;
636
+ const data = listSessions(options.configPath, {
637
+ q,
638
+ limit: Number.isFinite(limit) ? limit : void 0,
639
+ activeMinutes: Number.isFinite(activeMinutes) ? activeMinutes : void 0
640
+ });
641
+ return c.json(ok(data));
642
+ });
643
+ app.get("/api/sessions/:key/history", (c) => {
644
+ const key = decodeURIComponent(c.req.param("key"));
645
+ const query = c.req.query();
646
+ const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
647
+ const data = getSessionHistory(options.configPath, key, Number.isFinite(limit) ? limit : void 0);
648
+ if (!data) {
649
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
650
+ }
651
+ return c.json(ok(data));
652
+ });
653
+ app.put("/api/sessions/:key", async (c) => {
654
+ const key = decodeURIComponent(c.req.param("key"));
655
+ const body = await readJson(c.req.raw);
656
+ if (!body.ok || !body.data || typeof body.data !== "object") {
657
+ return c.json(err("INVALID_BODY", "invalid json body"), 400);
658
+ }
659
+ const data = patchSession(options.configPath, key, body.data);
660
+ if (!data) {
661
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
662
+ }
663
+ options.publish({ type: "config.updated", payload: { path: "session" } });
664
+ return c.json(ok(data));
665
+ });
666
+ app.delete("/api/sessions/:key", (c) => {
667
+ const key = decodeURIComponent(c.req.param("key"));
668
+ const deleted = deleteSession(options.configPath, key);
669
+ if (!deleted) {
670
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
671
+ }
672
+ options.publish({ type: "config.updated", payload: { path: "session" } });
673
+ return c.json(ok({ deleted: true }));
674
+ });
492
675
  app.put("/api/config/runtime", async (c) => {
493
676
  const body = await readJson(c.req.raw);
494
677
  if (!body.ok || !body.data || typeof body.data !== "object") {
495
678
  return c.json(err("INVALID_BODY", "invalid json body"), 400);
496
679
  }
497
680
  const result = updateRuntime(options.configPath, body.data);
681
+ if (body.data.agents?.defaults && Object.prototype.hasOwnProperty.call(body.data.agents.defaults, "contextTokens")) {
682
+ options.publish({ type: "config.updated", payload: { path: "agents.defaults.contextTokens" } });
683
+ }
498
684
  options.publish({ type: "config.updated", payload: { path: "agents.list" } });
499
685
  options.publish({ type: "config.updated", payload: { path: "bindings" } });
500
686
  options.publish({ type: "config.updated", payload: { path: "session" } });
@@ -607,8 +793,12 @@ export {
607
793
  buildConfigSchemaView,
608
794
  buildConfigView,
609
795
  createUiRouter,
796
+ deleteSession,
610
797
  executeConfigAction,
798
+ getSessionHistory,
799
+ listSessions,
611
800
  loadConfigOrDefault,
801
+ patchSession,
612
802
  startUiServer,
613
803
  updateChannel,
614
804
  updateModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",