@super-one/cli 0.53.1-alpha → 0.53.2-alpha

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/MANIFEST.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@super-one/cli",
3
- "version": "0.53.1-alpha",
3
+ "version": "0.53.2-alpha",
4
4
  "kind": "npm",
5
5
  "minNodeMajor": 20,
6
- "builtAt": "2026-08-12T19:18:38.159Z"
6
+ "builtAt": "2026-08-12T21:47:29.550Z"
7
7
  }
package/lib/cli.mjs CHANGED
@@ -659,7 +659,7 @@ var init_host_action_superone_descriptors = __esm({
659
659
  },
660
660
  {
661
661
  "name": "session_rename",
662
- "description": 'Rename the current chat session to a concise topic label shown in the sidebar.\n\nOnly the top-level agent talking directly to the user may call this. If you were launched as a Task/subagent worker, do NOT call it \u2014 you do not own the user-facing session title.\n\nIf the tool returns an error containing "user_locked", the user has manually named this session \u2014 do not call session_rename again for this session.',
662
+ "description": 'Rename the current chat session to a concise topic label shown in the sidebar. Optional tags (set) label it for session_list/session_search \u2014 discover names with session_tag_list.\n\nOnly the top-level agent talking directly to the user may call this. If you were launched as a Task/subagent worker, do NOT call it \u2014 you do not own the user-facing session title.\n\nIf the tool returns an error containing "user_locked", the user has manually named this session \u2014 do not call session_rename again. Tags in the same call were still applied; use session_tag for later tag edits.',
663
663
  "inputSchema": {
664
664
  "type": "object",
665
665
  "properties": {
@@ -668,6 +668,12 @@ var init_host_action_superone_descriptors = __esm({
668
668
  "description": "A concise 4-8 word title describing the current conversation topic.",
669
669
  "minLength": 1,
670
670
  "maxLength": 80
671
+ },
672
+ "tags": {
673
+ "type": "array",
674
+ "items": { "type": "string" },
675
+ "maxItems": 8,
676
+ "description": "Replace this session's tags (set). Discover names with session_tag_list. Empty array clears. Applied even when the title is user_locked."
671
677
  }
672
678
  },
673
679
  "required": [
@@ -676,6 +682,81 @@ var init_host_action_superone_descriptors = __esm({
676
682
  "additionalProperties": false
677
683
  }
678
684
  },
685
+ {
686
+ "name": "session_tag",
687
+ "description": "Tag SuperOne sessions so session_list/session_search can filter by tag. Default: current session. Pass sessionId for one other session, or sessionIds with add to tag many. Use add, remove, or set (exactly one). set: [] clears. Discover existing labels with session_tag_list first \u2014 do not invent names. Only the top-level agent may call this; subagents must not. Not session_rename (titles) and not live collab.",
688
+ "inputSchema": {
689
+ "type": "object",
690
+ "properties": {
691
+ "sessionId": {
692
+ "type": "string",
693
+ "description": "One session to tag. Default: current. Mutually exclusive with sessionIds."
694
+ },
695
+ "sessionIds": {
696
+ "type": "array",
697
+ "items": { "type": "string" },
698
+ "maxItems": 50,
699
+ "description": "Bulk target ids (max 50). add required; set/remove not allowed. Mutually exclusive with sessionId."
700
+ },
701
+ "add": {
702
+ "type": "array",
703
+ "items": { "type": "string" },
704
+ "maxItems": 8,
705
+ "description": "Tags to add (normalized, de-duped). Mutually exclusive with remove/set."
706
+ },
707
+ "remove": {
708
+ "type": "array",
709
+ "items": { "type": "string" },
710
+ "maxItems": 8,
711
+ "description": "Tags to remove. Mutually exclusive with add/set."
712
+ },
713
+ "set": {
714
+ "type": "array",
715
+ "items": { "type": "string" },
716
+ "maxItems": 8,
717
+ "description": "Replace all tags. Empty array clears. Mutually exclusive with add/remove."
718
+ }
719
+ },
720
+ "additionalProperties": false
721
+ }
722
+ },
723
+ {
724
+ "name": "session_tag_list",
725
+ "description": "List tags used on SuperOne sessions (tag + session count). Default: current project; projectId or allProjects for other scope. Filter with query (tag substring). Hidden sessions omitted unless includeHidden. Call this before session_list/session_search with tags. Then filter with tags + tagMatch any (at least one) or all (every tag). Not live collab.",
726
+ "inputSchema": {
727
+ "type": "object",
728
+ "properties": {
729
+ "query": {
730
+ "type": "string",
731
+ "description": "Case-insensitive substring filter on tag name."
732
+ },
733
+ "includeHidden": {
734
+ "type": "boolean",
735
+ "description": "Count hidden sessions. Default false."
736
+ },
737
+ "projectId": {
738
+ "type": "string",
739
+ "description": "List tags in this SuperOne project id only (from project_list). Mutually exclusive with allProjects. Default: current project."
740
+ },
741
+ "allProjects": {
742
+ "type": "boolean",
743
+ "description": "List tags across every SuperOne project. Mutually exclusive with projectId. Default false."
744
+ },
745
+ "limit": {
746
+ "type": "integer",
747
+ "minimum": 1,
748
+ "maximum": 100,
749
+ "description": "Max rows. Default 50, max 100."
750
+ },
751
+ "offset": {
752
+ "type": "integer",
753
+ "minimum": 0,
754
+ "description": "Pagination offset. Default 0."
755
+ }
756
+ },
757
+ "additionalProperties": false
758
+ }
759
+ },
679
760
  {
680
761
  "name": "project_list",
681
762
  "description": "List SuperOne projects (id, name, path, lastActiveAt). Call this to discover projectId before session_list/session_search with projectId. Default order is last-active desc. Filter with query (name/path substring). isCurrent marks the project of the calling session.",
@@ -703,7 +784,7 @@ var init_host_action_superone_descriptors = __esm({
703
784
  },
704
785
  {
705
786
  "name": "session_list",
706
- "description": "List SuperOne sessions (metadata only). Default: current project. Pass projectId (from project_list) for another project, or allProjects=true for every project. Rows include projectId only \u2014 use project_list for path/name. Use before session_read/session_search to find ids. Filter by title query, harness, pin/hidden, dates. Sort with order (default last_active_desc; last_active_asc oldest-first; also created_*, message_count_*, size_*). When order is size_*, rows include sizeBytes (approx character length of stored message JSON for ranking \u2014 not disk page-file bytes). Paginate with limit/offset. Not live collab or harness resume.",
787
+ "description": "List SuperOne sessions (metadata only). Default: current project. Pass projectId (from project_list) or allProjects=true. Rows include projectId only \u2014 use project_list for path/name. Use before session_read/session_search. Filter by title query, harness, pin/hidden, dates, or tags + tagMatch (any=at least one, all=every tag; default any). Discover tags with session_tag_list. Sort with order (default last_active_desc; also created_*, message_count_*, size_*). When order is size_*, rows include sizeBytes (character length of message JSON, not disk bytes). Paginate with limit/offset. Not live collab or harness resume.",
707
788
  "inputSchema": {
708
789
  "type": "object",
709
790
  "properties": {
@@ -741,6 +822,17 @@ var init_host_action_superone_descriptors = __esm({
741
822
  "type": "string",
742
823
  "description": "ISO timestamp \u2014 only sessions last active after this."
743
824
  },
825
+ "tags": {
826
+ "type": "array",
827
+ "items": { "type": "string" },
828
+ "maxItems": 8,
829
+ "description": "Tags from session_tag_list. Filter sessions that have these labels."
830
+ },
831
+ "tagMatch": {
832
+ "type": "string",
833
+ "enum": ["any", "all"],
834
+ "description": "any = at least one listed tag (default). all = every listed tag. Ignored when tags is omitted."
835
+ },
744
836
  "projectId": {
745
837
  "type": "string",
746
838
  "description": "List sessions in this SuperOne project id only (from project_list). Mutually exclusive with allProjects. Default: current project."
@@ -780,7 +872,7 @@ var init_host_action_superone_descriptors = __esm({
780
872
  },
781
873
  {
782
874
  "name": "session_search",
783
- "description": "Search SuperOne chat transcripts by text. Default: current project; projectId (from project_list) or allProjects for cross-project. Returns matching message hits with short snippets and projectId. Then call session_read with sessionId/messageId for full content. Snippets are pointers only \u2014 not full message bodies.",
875
+ "description": "Search SuperOne chat transcripts by text (title + message body). Default: current project; projectId or allProjects for cross-project. Optional tags + tagMatch (any/all, default any) narrows sessions in SQL before scanning messages. Discover tags with session_tag_list. Returns matching message hits with short snippets and projectId. Then call session_read with sessionId/messageId. Snippets are pointers only \u2014 not full bodies.",
784
876
  "inputSchema": {
785
877
  "type": "object",
786
878
  "properties": {
@@ -815,6 +907,17 @@ var init_host_action_superone_descriptors = __esm({
815
907
  ],
816
908
  "description": "Message role filter. Default any."
817
909
  },
910
+ "tags": {
911
+ "type": "array",
912
+ "items": { "type": "string" },
913
+ "maxItems": 8,
914
+ "description": "Tags from session_tag_list. Filter sessions that have these labels."
915
+ },
916
+ "tagMatch": {
917
+ "type": "string",
918
+ "enum": ["any", "all"],
919
+ "description": "any = at least one listed tag (default). all = every listed tag. Ignored when tags is omitted."
920
+ },
818
921
  "projectId": {
819
922
  "type": "string",
820
923
  "description": "Search this SuperOne project id only (from project_list). Mutually exclusive with allProjects. Default: current project."
@@ -838,14 +941,14 @@ var init_host_action_superone_descriptors = __esm({
838
941
  },
839
942
  {
840
943
  "name": "session_read",
841
- "description": "Read any SuperOne session's saved transcript by id (any project; harness-agnostic content; does not resume provider threads). Views: meta | user | assistant | text | tools | tool_detail. user/assistant/text are pure conversation (no tool lines; assistant/text include toolCount). tools = index; tool_detail needs toolUseId. Paginate with limit/cursor; anchor with messageId/around. Prefer user then on-demand assistant/tools. meta includes projectId (use project_list for path/name).",
944
+ "description": "Read another SuperOne session's saved transcript by id (any project; harness-agnostic; does not resume provider threads). Do not read the current session \u2014 it is already in your context. Views: meta | user | assistant | text | tools | tool_detail. user/assistant/text are pure conversation (no tool lines; assistant/text include toolCount). tools = index; tool_detail needs toolUseId. Paginate with limit/cursor; anchor with messageId/around. Prefer user then on-demand assistant/tools. meta includes projectId and tags.",
842
945
  "inputSchema": {
843
946
  "type": "object",
844
947
  "properties": {
845
948
  "sessionId": {
846
949
  "type": "string",
847
950
  "minLength": 1,
848
- "description": "Target SuperOne session id from session_list or session_search (any project)."
951
+ "description": "Target SuperOne session id from session_list or session_search (any project). Do not pass the current session."
849
952
  },
850
953
  "view": {
851
954
  "type": "string",
@@ -9691,6 +9794,8 @@ var init_superone_host_owned_tools = __esm({
9691
9794
  "miniapp_dev_pack",
9692
9795
  "miniapp_dev_update_types",
9693
9796
  "session_rename",
9797
+ "session_tag",
9798
+ "session_tag_list",
9694
9799
  "project_list",
9695
9800
  "session_list",
9696
9801
  "session_search",
@@ -13190,6 +13295,10 @@ var init_session_runtime = __esm({
13190
13295
  session.isUserRenamed = false;
13191
13296
  changed = true;
13192
13297
  }
13298
+ if (!Array.isArray(session.tags)) {
13299
+ session.tags = [];
13300
+ changed = true;
13301
+ }
13193
13302
  if (session.isAutomation !== true && session.isAutomation !== false) {
13194
13303
  session.isAutomation = false;
13195
13304
  changed = true;
@@ -13264,6 +13373,7 @@ var init_session_runtime = __esm({
13264
13373
  isPinned: false,
13265
13374
  isHidden: false,
13266
13375
  isUserRenamed: false,
13376
+ tags: [],
13267
13377
  controllerClientSessionId: controller,
13268
13378
  hostActionCapabilityVersion: controller ? input.hostActionCapabilityVersion ?? HOST_ACTION_CAPABILITY_VERSION : 0,
13269
13379
  hostActionToolGroups: controller ? input.hostActionToolGroups ?? [...DEFAULT_HOST_ACTION_TOOL_GROUPS] : [],
@@ -13455,6 +13565,7 @@ var init_session_runtime = __esm({
13455
13565
  isHidden: false,
13456
13566
  // Fork title is derived; start unlocked so agent can rename the fork.
13457
13567
  isUserRenamed: false,
13568
+ tags: [],
13458
13569
  // Fork inherits controller binding (same paired desktop).
13459
13570
  controllerClientSessionId: source.controllerClientSessionId,
13460
13571
  hostActionCapabilityVersion: source.hostActionCapabilityVersion,
@@ -13927,6 +14038,17 @@ var init_session_runtime = __esm({
13927
14038
  });
13928
14039
  return this.clone(session);
13929
14040
  }
14041
+ setTags(sessionId, tags) {
14042
+ const session = this.live.get(sessionId);
14043
+ if (!session) throw Object.assign(new Error("session not found"), { code: "not_found" });
14044
+ if (session.closed) {
14045
+ throw Object.assign(new Error("session is closed"), { code: "failed_precondition" });
14046
+ }
14047
+ session.tags = [...tags];
14048
+ session.updatedAt = Date.now();
14049
+ this.persist(session);
14050
+ return this.clone(session);
14051
+ }
13930
14052
  setUiFlags(sessionId, flags) {
13931
14053
  const session = this.live.get(sessionId);
13932
14054
  if (!session) throw Object.assign(new Error("session not found"), { code: "not_found" });
@@ -14789,6 +14911,7 @@ var init_session_runtime = __esm({
14789
14911
  transcript: s2.transcript.map((t) => ({ ...t })),
14790
14912
  pendingInteraction: s2.pendingInteraction ? { ...s2.pendingInteraction } : null,
14791
14913
  isUserRenamed: s2.isUserRenamed === true,
14914
+ tags: [...s2.tags ?? []],
14792
14915
  controllerClientSessionId: s2.controllerClientSessionId ?? null,
14793
14916
  hostActionCapabilityVersion: s2.hostActionCapabilityVersion ?? 0,
14794
14917
  hostActionToolGroups: [...s2.hostActionToolGroups ?? []],
@@ -14989,7 +15112,8 @@ function createSqliteSessionStore(db) {
14989
15112
  COALESCE(always_allowed_tools_json, '[]') AS always_allowed_tools_json,
14990
15113
  settings_json,
14991
15114
  COALESCE(is_automation, 0) AS is_automation,
14992
- automation_id
15115
+ automation_id,
15116
+ COALESCE(tags_json, '[]') AS tags_json
14993
15117
  FROM sessions`
14994
15118
  ).all();
14995
15119
  const parseStringArray = (raw) => {
@@ -15028,7 +15152,8 @@ function createSqliteSessionStore(db) {
15028
15152
  hostActionToolGroups: parseStringArray(r.host_action_tool_groups_json),
15029
15153
  alwaysAllowedTools: parseStringArray(r.always_allowed_tools_json),
15030
15154
  isAutomation: r.is_automation === 1,
15031
- automationId: r.automation_id ?? null
15155
+ automationId: r.automation_id ?? null,
15156
+ tags: parseStringArray(r.tags_json)
15032
15157
  };
15033
15158
  });
15034
15159
  },
@@ -15039,8 +15164,8 @@ function createSqliteSessionStore(db) {
15039
15164
  pending_interaction_json, provider_resume, cwd, created_at, updated_at, is_pinned, is_hidden,
15040
15165
  is_user_renamed,
15041
15166
  controller_client_session_id, host_action_capability_version, host_action_tool_groups_json,
15042
- always_allowed_tools_json, settings_json, is_automation, automation_id)
15043
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
15167
+ always_allowed_tools_json, settings_json, is_automation, automation_id, tags_json)
15168
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
15044
15169
  ON CONFLICT(session_id) DO UPDATE SET
15045
15170
  title = excluded.title,
15046
15171
  status = excluded.status,
@@ -15058,7 +15183,8 @@ function createSqliteSessionStore(db) {
15058
15183
  always_allowed_tools_json = excluded.always_allowed_tools_json,
15059
15184
  settings_json = excluded.settings_json,
15060
15185
  is_automation = excluded.is_automation,
15061
- automation_id = excluded.automation_id`
15186
+ automation_id = excluded.automation_id,
15187
+ tags_json = excluded.tags_json`
15062
15188
  ).run(
15063
15189
  session.sessionId,
15064
15190
  session.projectId,
@@ -15081,7 +15207,8 @@ function createSqliteSessionStore(db) {
15081
15207
  JSON.stringify(session.alwaysAllowedTools ?? []),
15082
15208
  serializeSettingsJson(session),
15083
15209
  session.isAutomation ? 1 : 0,
15084
- session.automationId ?? null
15210
+ session.automationId ?? null,
15211
+ JSON.stringify(session.tags ?? [])
15085
15212
  );
15086
15213
  },
15087
15214
  delete(sessionId) {
@@ -42431,13 +42558,13 @@ var init_tool_normalization = __esm({
42431
42558
  // ../../packages/shared/src/session-archive-result-shape.ts
42432
42559
  function isSessionArchiveToolName(toolName) {
42433
42560
  if (!toolName) return false;
42434
- return /(?:^|__)(?:project_list|session_(?:list|search|read|cleanup))$/.test(toolName);
42561
+ return /(?:^|__)(?:project_list|session_(?:list|search|read|cleanup|tag_list))$/.test(toolName);
42435
42562
  }
42436
42563
  function looksLikeSessionArchiveToon(summary) {
42437
- return /(?:^|\n)(?:projects|sessions|hits)\[\d+\]/.test(summary.trim());
42564
+ return /(?:^|\n)(?:projects|sessions|hits|tags)\[\d+\]/.test(summary.trim());
42438
42565
  }
42439
42566
  function looksLikeSessionArchiveJson(obj) {
42440
- return Array.isArray(obj.sessions) || Array.isArray(obj.hits) || Array.isArray(obj.projects) || typeof obj.action === "string" && (Array.isArray(obj.deleted) || Array.isArray(obj.affected) || Array.isArray(obj.candidates) || Array.isArray(obj.failed));
42567
+ return Array.isArray(obj.sessions) || Array.isArray(obj.hits) || Array.isArray(obj.projects) || Array.isArray(obj.tags) || typeof obj.action === "string" && (Array.isArray(obj.deleted) || Array.isArray(obj.affected) || Array.isArray(obj.candidates) || Array.isArray(obj.failed));
42441
42568
  }
42442
42569
  var init_session_archive_result_shape = __esm({
42443
42570
  "../../packages/shared/src/session-archive-result-shape.ts"() {
@@ -60528,8 +60655,8 @@ import { fileURLToPath } from "node:url";
60528
60655
  function resolveCliReleaseVersion() {
60529
60656
  const fromEnv = process.env.SUPERONE_CLI_VERSION?.trim();
60530
60657
  if (fromEnv) return fromEnv;
60531
- if ("0.53.1-alpha".trim()) {
60532
- return "0.53.1-alpha".trim();
60658
+ if ("0.53.2-alpha".trim()) {
60659
+ return "0.53.2-alpha".trim();
60533
60660
  }
60534
60661
  const fromDist = readDistManifestVersion();
60535
60662
  if (fromDist) return fromDist;
@@ -61249,7 +61376,8 @@ CREATE TABLE IF NOT EXISTS sessions (
61249
61376
  settings_json TEXT,
61250
61377
  -- Automation ownership markers (filterable in session.list metadata)
61251
61378
  is_automation INTEGER NOT NULL DEFAULT 0,
61252
- automation_id TEXT
61379
+ automation_id TEXT,
61380
+ tags_json TEXT NOT NULL DEFAULT '[]'
61253
61381
  );
61254
61382
 
61255
61383
  -- Host Action channel (durable poll/claim/respond). Also ensured at open time.
@@ -61412,6 +61540,7 @@ function openNodeDatabase(dbPath) {
61412
61540
  ensureSessionSettingsColumn(db);
61413
61541
  ensureCollaborationTables(db);
61414
61542
  ensureAutomationsSupport(db);
61543
+ ensureSessionTagsColumn(db);
61415
61544
  ensureSessionProvidersSupport(db);
61416
61545
  const row = db.prepare("SELECT value FROM meta WHERE key = ?").get("schema_generation");
61417
61546
  if (!row) {
@@ -61608,6 +61737,12 @@ CREATE TABLE IF NOT EXISTS session_collaboration_cursors (
61608
61737
  db.exec(`ALTER TABLE session_collaboration_grants ADD COLUMN kind TEXT NOT NULL DEFAULT 'spawn'`);
61609
61738
  }
61610
61739
  }
61740
+ function ensureSessionTagsColumn(db) {
61741
+ const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
61742
+ if (!cols.some((c) => c.name === "tags_json")) {
61743
+ db.exec(`ALTER TABLE sessions ADD COLUMN tags_json TEXT NOT NULL DEFAULT '[]'`);
61744
+ }
61745
+ }
61611
61746
  function ensureAutomationsSupport(db) {
61612
61747
  const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
61613
61748
  const names = new Set(cols.map((c) => c.name));
@@ -61682,6 +61817,86 @@ init_environment();
61682
61817
  // src/rpc/handlers.ts
61683
61818
  init_environment();
61684
61819
 
61820
+ // ../../packages/shared/src/session-tags.ts
61821
+ var SESSION_TAG_MAX_LENGTH = 32;
61822
+ var SESSION_TAG_MAX_PER_SESSION = 8;
61823
+ var TAG_CHAR = /^[\p{L}\p{N}]+(?:-[\p{L}\p{N}]+)*$/u;
61824
+ function normalizeSessionTag(raw) {
61825
+ const collapsed = raw.trim().toLowerCase().replace(/\s+/g, "-");
61826
+ if (!collapsed || collapsed.length > SESSION_TAG_MAX_LENGTH) return null;
61827
+ if (!TAG_CHAR.test(collapsed)) return null;
61828
+ return collapsed;
61829
+ }
61830
+ function normalizeSessionTagList(raw) {
61831
+ if (!Array.isArray(raw)) {
61832
+ return { error: "tags must be an array of strings" };
61833
+ }
61834
+ const out = [];
61835
+ const seen = /* @__PURE__ */ new Set();
61836
+ for (const item of raw) {
61837
+ if (typeof item !== "string") {
61838
+ return { error: "each tag must be a string" };
61839
+ }
61840
+ const tag = normalizeSessionTag(item);
61841
+ if (!tag) {
61842
+ return {
61843
+ error: `Invalid tag "${item}". Use 1\u2013${SESSION_TAG_MAX_LENGTH} letters, numbers, or hyphens (CJK allowed).`
61844
+ };
61845
+ }
61846
+ if (seen.has(tag)) continue;
61847
+ seen.add(tag);
61848
+ out.push(tag);
61849
+ if (out.length > SESSION_TAG_MAX_PER_SESSION) {
61850
+ return { error: `A session can have at most ${SESSION_TAG_MAX_PER_SESSION} tags.` };
61851
+ }
61852
+ }
61853
+ return { tags: out };
61854
+ }
61855
+ function parseSessionTagOp(args) {
61856
+ const present = [args.add !== void 0, args.remove !== void 0, args.set !== void 0].filter(Boolean).length;
61857
+ if (present === 0) {
61858
+ return { error: "Pass exactly one of add, remove, or set." };
61859
+ }
61860
+ if (present > 1) {
61861
+ return { error: "Pass exactly one of add, remove, or set \u2014 not more than one." };
61862
+ }
61863
+ if (args.set !== void 0) {
61864
+ const parsed2 = normalizeSessionTagList(args.set);
61865
+ if ("error" in parsed2) return parsed2;
61866
+ return { kind: "set", tags: parsed2.tags };
61867
+ }
61868
+ if (args.add !== void 0) {
61869
+ const parsed2 = normalizeSessionTagList(args.add);
61870
+ if ("error" in parsed2) return parsed2;
61871
+ if (parsed2.tags.length === 0) return { error: "add requires at least one tag." };
61872
+ return { kind: "add", tags: parsed2.tags };
61873
+ }
61874
+ const parsed = normalizeSessionTagList(args.remove);
61875
+ if ("error" in parsed) return parsed;
61876
+ if (parsed.tags.length === 0) return { error: "remove requires at least one tag." };
61877
+ return { kind: "remove", tags: parsed.tags };
61878
+ }
61879
+ function applySessionTagOp(current, op) {
61880
+ if (op.kind === "set") return op.tags;
61881
+ if (op.kind === "add") {
61882
+ const seen = new Set(current);
61883
+ const next = [...current];
61884
+ for (const tag of op.tags) {
61885
+ if (seen.has(tag)) continue;
61886
+ if (next.length >= SESSION_TAG_MAX_PER_SESSION) {
61887
+ return {
61888
+ error: `A session can have at most ${SESSION_TAG_MAX_PER_SESSION} tags. Remove some first.`
61889
+ };
61890
+ }
61891
+ seen.add(tag);
61892
+ next.push(tag);
61893
+ }
61894
+ return next;
61895
+ }
61896
+ const drop = new Set(op.tags);
61897
+ return current.filter((tag) => !drop.has(tag));
61898
+ }
61899
+
61685
61900
  // ../../packages/runtime/src/settings/node-agent-settings.ts
61686
61901
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
61687
61902
  import { dirname as dirname4 } from "node:path";
@@ -66189,6 +66404,7 @@ var MUTATING_METHODS = /* @__PURE__ */ new Set([
66189
66404
  "session.close",
66190
66405
  "session.remove",
66191
66406
  "session.rename",
66407
+ "session.setTags",
66192
66408
  "session.setUiFlags",
66193
66409
  // pin/hide
66194
66410
  "terminal.acquireControl",
@@ -66460,6 +66676,8 @@ async function dispatchRpcInner(method, payload, ctx) {
66460
66676
  return handleSessionRemove(payload, ctx);
66461
66677
  case "session.rename":
66462
66678
  return handleSessionRename(payload, ctx);
66679
+ case "session.setTags":
66680
+ return handleSessionSetTags(payload, ctx);
66463
66681
  case "session.setUiFlags":
66464
66682
  return handleSessionSetUiFlags(payload, ctx);
66465
66683
  case "collaboration.listProfiles":
@@ -67953,6 +68171,32 @@ function handleSessionRename(payload, ctx) {
67953
68171
  return mapThrown7(err);
67954
68172
  }
67955
68173
  }
68174
+ function handleSessionSetTags(payload, ctx) {
68175
+ const denied = requireScopes7(ctx.client, OPERATION_SCOPES.operateSession);
68176
+ if (denied) return denied;
68177
+ const p2 = asRecord17(payload);
68178
+ const sessionId = String(p2.sessionId ?? "");
68179
+ if (!sessionId) {
68180
+ return { error: { code: "invalid_argument", message: "sessionId required" } };
68181
+ }
68182
+ const op = parseSessionTagOp({ add: p2.add, remove: p2.remove, set: p2.set });
68183
+ if ("error" in op) {
68184
+ return { error: { code: "invalid_argument", message: op.error } };
68185
+ }
68186
+ try {
68187
+ const session = ctx.sessions.get(sessionId);
68188
+ if (!session) {
68189
+ return { error: { code: "not_found", message: "session not found" } };
68190
+ }
68191
+ const next = applySessionTagOp(session.tags ?? [], op);
68192
+ if ("error" in next) {
68193
+ return { error: { code: "failed_precondition", message: next.error } };
68194
+ }
68195
+ return { result: ctx.sessions.setTags(sessionId, next) };
68196
+ } catch (err) {
68197
+ return mapThrown7(err);
68198
+ }
68199
+ }
67956
68200
  function handleSessionSetUiFlags(payload, ctx) {
67957
68201
  const denied = requireScopes7(ctx.client, OPERATION_SCOPES.operateSession);
67958
68202
  if (denied) return denied;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@super-one/cli",
3
- "version": "0.53.1-alpha",
3
+ "version": "0.53.2-alpha",
4
4
  "description": "SuperOne headless node CLI — remote execution environment (RPC, workspaces, sessions).",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",