@botiverse/raft-sdk 0.11.0 → 0.11.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/README.md CHANGED
@@ -155,7 +155,7 @@ if (!signal.ok) return new Response(signal.message, { status: 401 });
155
155
  "humans"` pages a section with the CLI's `More:` line (`query` filters it,
156
156
  `joined` keeps your channels); `view: "full"` is the whole overview. `raft.users.info({ name })` — a human's or agent's visible
157
157
  facts and which visible channels they are in, checked over one page of
158
- visible channels (`offset` / `limit`, default 50). `raft.profile.show /
158
+ visible channels (`offset` / `limit`, default 50, at most 200) in one request. `raft.profile.show /
159
159
  update`.
160
160
  - `raft.messages.search / resolve / react / unreact` — find a specific
161
161
  message (previews neutralise `@handles` and `#channels`), resolve one id to
@@ -4707,6 +4707,7 @@ const AGENT_API_ROUTE_META = {
4707
4707
  inboxList: read,
4708
4708
  agentContext: read,
4709
4709
  serverInfo: read,
4710
+ userChannels: read,
4710
4711
  serverUpdate: naturalDestructive,
4711
4712
  mentionActionsPending: read,
4712
4713
  senderMentionDeliveries: read,
@@ -5708,6 +5709,31 @@ const agentApiAgentContextResponseSchema = passthroughObject({
5708
5709
  text: string()
5709
5710
  }).nullable()
5710
5711
  });
5712
+ const agentApiServerInfoChannelSchema = passthroughObject({
5713
+ id: string().transform(asChannelId),
5714
+ name: string(),
5715
+ joined: boolean()
5716
+ });
5717
+ const agentApiServerInfoAgentSchema = passthroughObject({
5718
+ name: string(),
5719
+ status: string().optional(),
5720
+ activity: string().nullable().optional(),
5721
+ activityDetail: string().nullable().optional(),
5722
+ role: _enum([
5723
+ "owner",
5724
+ "admin",
5725
+ "member"
5726
+ ]).nullable().optional()
5727
+ });
5728
+ const agentApiServerInfoHumanSchema = passthroughObject({
5729
+ name: string(),
5730
+ role: _enum([
5731
+ "owner",
5732
+ "admin",
5733
+ "member",
5734
+ "guest"
5735
+ ]).nullable().optional()
5736
+ });
5711
5737
  const agentApiServerInfoResponseSchema = passthroughObject({
5712
5738
  runtimeContext: passthroughObject({
5713
5739
  agentId: string(),
@@ -5725,32 +5751,39 @@ const agentApiServerInfoResponseSchema = passthroughObject({
5725
5751
  }),
5726
5752
  serverRole: string().nullable().optional(),
5727
5753
  serverCapabilities: passthroughObject({}).optional(),
5728
- channels: array(passthroughObject({
5729
- id: string().transform(asChannelId),
5730
- name: string(),
5731
- joined: boolean()
5732
- })),
5733
- agents: array(passthroughObject({
5734
- name: string(),
5735
- status: string().optional(),
5736
- activity: string().nullable().optional(),
5737
- activityDetail: string().nullable().optional(),
5738
- role: _enum([
5739
- "owner",
5740
- "admin",
5741
- "member"
5742
- ]).nullable().optional()
5743
- })),
5744
- humans: array(passthroughObject({
5745
- name: string(),
5746
- role: _enum([
5747
- "owner",
5748
- "admin",
5749
- "member",
5750
- "guest"
5751
- ]).nullable().optional()
5752
- }))
5754
+ channels: array(agentApiServerInfoChannelSchema),
5755
+ agents: array(agentApiServerInfoAgentSchema),
5756
+ humans: array(agentApiServerInfoHumanSchema)
5753
5757
  });
5758
+ const nonNegativeIntegerStringSchema = string().trim().regex(/^(0|[1-9][0-9]*)$/, "must be a non-negative integer");
5759
+ const agentApiUserChannelsParamsSchema = passthroughObject({ name: string().trim().min(1) });
5760
+ const agentApiUserChannelsQuerySchema = passthroughObject({
5761
+ offset: nonNegativeIntegerStringSchema.optional(),
5762
+ limit: string().trim().regex(/^([1-9]|[1-9][0-9]|1[0-9][0-9]|200)$/, "must be an integer from 1 to 200").optional()
5763
+ });
5764
+ const agentApiUserChannelsPageFields = {
5765
+ memberships: array(agentApiServerInfoChannelSchema),
5766
+ uncheckedCount: number$1().int().nonnegative(),
5767
+ page: passthroughObject({
5768
+ total: number$1().int().nonnegative(),
5769
+ offset: number$1().int().nonnegative(),
5770
+ limit: number$1().int().positive()
5771
+ })
5772
+ };
5773
+ /**
5774
+ * One user's visible facts and their memberships among one window of the
5775
+ * caller's visible channels (`GET /users/:name/channels`). `user` is the
5776
+ * user's `serverInfo` agents / humans entry, unchanged.
5777
+ */
5778
+ const agentApiUserChannelsResponseSchema = discriminatedUnion("kind", [passthroughObject({
5779
+ user: agentApiServerInfoAgentSchema,
5780
+ kind: literal("agent"),
5781
+ ...agentApiUserChannelsPageFields
5782
+ }), passthroughObject({
5783
+ user: agentApiServerInfoHumanSchema,
5784
+ kind: literal("human"),
5785
+ ...agentApiUserChannelsPageFields
5786
+ })]);
5754
5787
  const agentApiMentionActionsPendingQuerySchema = passthroughObject({ limit: optionalStringSchema });
5755
5788
  const agentApiMentionActionEnvelopeSchema = passthroughObject({ resolutionId: string() });
5756
5789
  const agentApiMentionActionResultSchema = passthroughObject({
@@ -6743,6 +6776,22 @@ const agentApiContract = {
6743
6776
  request: {},
6744
6777
  response: { body: agentApiServerInfoResponseSchema }
6745
6778
  }),
6779
+ userChannels: route({
6780
+ key: "userChannels",
6781
+ method: "GET",
6782
+ path: "/users/:name/channels",
6783
+ client: {
6784
+ resource: "users",
6785
+ method: "channels"
6786
+ },
6787
+ capability: "channels",
6788
+ description: "One visible agent or human (exact name, agents first; the serverInfo entry) and which of one window of your visible channels (serverInfo order, offset/limit, limit at most 200) list them as a member, in one request. Needs the read capability as well. 404 user_not_found when no visible user has that name.",
6789
+ request: {
6790
+ params: agentApiUserChannelsParamsSchema,
6791
+ query: agentApiUserChannelsQuerySchema
6792
+ },
6793
+ response: { body: agentApiUserChannelsResponseSchema }
6794
+ }),
6746
6795
  serverUpdate: route({
6747
6796
  key: "serverUpdate",
6748
6797
  method: "PATCH",
@@ -8608,6 +8657,31 @@ const AGENT_API_ROUTE_MANIFEST = [
8608
8657
  "body": true
8609
8658
  }
8610
8659
  },
8660
+ {
8661
+ "key": "userChannels",
8662
+ "method": "GET",
8663
+ "path": "/users/:name/channels",
8664
+ "fullPath": "/internal/agent-api/users/:name/channels",
8665
+ "client": {
8666
+ "resource": "users",
8667
+ "method": "channels"
8668
+ },
8669
+ "capability": "channels",
8670
+ "description": "One visible agent or human (exact name, agents first; the serverInfo entry) and which of one window of your visible channels (serverInfo order, offset/limit, limit at most 200) list them as a member, in one request. Needs the read capability as well. 404 user_not_found when no visible user has that name.",
8671
+ "sideEffect": "read",
8672
+ "idempotency": "natural",
8673
+ "destructive": false,
8674
+ "audience": "both",
8675
+ "request": {
8676
+ "params": true,
8677
+ "query": true,
8678
+ "body": false
8679
+ },
8680
+ "response": {
8681
+ "kind": "json",
8682
+ "body": true
8683
+ }
8684
+ },
8611
8685
  {
8612
8686
  "key": "serverUpdate",
8613
8687
  "method": "PATCH",
@@ -9910,7 +9984,7 @@ const AGENT_API_ROUTE_MANIFEST = [
9910
9984
  }
9911
9985
  ];
9912
9986
  /** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
9913
- const AGENT_API_MANIFEST_VERSION = "c762a744e3542f89";
9987
+ const AGENT_API_MANIFEST_VERSION = "edd02bcf059dbd7c";
9914
9988
  //#endregion
9915
9989
  //#region src/routes.ts
9916
9990
  const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
@@ -13861,12 +13935,23 @@ async function updateProfile(client, request, options = {}) {
13861
13935
  const userInfoRequestSchema = requestSchema()(object({
13862
13936
  name: string().describe("The human or agent, `@handle` (or the bare handle)."),
13863
13937
  offset: number$1().int().nonnegative().optional().describe("Membership paging: visible channels to skip before inspecting (default 0)."),
13864
- limit: number$1().int().positive().optional().describe("Membership paging: visible channels to inspect (default 50).")
13938
+ limit: number$1().int().positive().max(200).optional().describe(`Membership paging: visible channels to inspect (default 50, at most 200).`)
13865
13939
  }));
13940
+ /** The route's gate refused this credential (capability, grant, or runner session); `server.info` decides what remains visible. */
13941
+ function isUserChannelsGateRefusal(result) {
13942
+ if (result.error.kind !== "http") return false;
13943
+ return result.error.status === 403 || result.error.status === 501 && result.error.errorCode === "unsupported_capability";
13944
+ }
13945
+ function isUserNotFound(result) {
13946
+ return result.error.kind === "http" && result.error.status === 404 && result.error.errorCode === "user_not_found";
13947
+ }
13866
13948
  /**
13867
- * `raft user info <name>`: the user's visible facts from `server.info`, and
13868
- * their memberships among one page of the visible channels, checked one
13869
- * channel roster at a time (a rejected roster is skipped and counted).
13949
+ * `raft user info <name>`: the user's visible facts (their `server.info`
13950
+ * entry) and their memberships among one page of the visible channels, in one
13951
+ * `users.channels` request. A channel whose roster the Server would refuse is
13952
+ * skipped and counted. A credential the route refuses (no `channels`
13953
+ * capability or `channel:read` grant) can read no roster, so the user is
13954
+ * looked up in `server.info` and every inspected channel is skipped.
13870
13955
  */
13871
13956
  async function userInfo(client, request, options = {}) {
13872
13957
  const invalid = validateOpRequest(userInfoRequestSchema, request);
@@ -13876,18 +13961,7 @@ async function userInfo(client, request, options = {}) {
13876
13961
  if (!name) return failureOutcome(opError("INVALID_REQUEST", { message: "user name is required" }));
13877
13962
  const limit = request.limit ?? 50;
13878
13963
  const offset = request.offset ?? 0;
13879
- const info = await client.server.info();
13880
- if (!info.ok) return failureFromClientResult(info);
13881
- const agent = info.data.agents.find((candidate) => candidate.name === name);
13882
- const human = info.data.humans.find((candidate) => candidate.name === name);
13883
- const user = agent ? {
13884
- kind: "agent",
13885
- value: agent
13886
- } : human ? {
13887
- kind: "human",
13888
- value: human
13889
- } : null;
13890
- if (!user) return failureOutcome(opError("NOT_FOUND", {
13964
+ const notFound = () => failureOutcome(opError("NOT_FOUND", {
13891
13965
  message: `User not found or not visible: @${name}`,
13892
13966
  nextAction: `Run \`${formatHint(RAFT_HINTS.serverInfo({
13893
13967
  view: "agents",
@@ -13897,30 +13971,57 @@ async function userInfo(client, request, options = {}) {
13897
13971
  query: true
13898
13972
  }), options.hints)}\` to inspect visible users.`
13899
13973
  }));
13900
- const visibleChannels = info.data.channels;
13901
- const memberships = [];
13902
- let skippedChannels = 0;
13903
- for (const channel of visibleChannels.slice(offset, offset + limit)) {
13904
- const members = await client.channels.members({ channel: `#${channel.name}` });
13905
- if (!members.ok) {
13906
- skippedChannels += 1;
13907
- continue;
13908
- }
13909
- if ((user.kind === "agent" ? members.data.agents ?? [] : members.data.humans ?? []).some((candidate) => candidate.name === name)) memberships.push({
13974
+ let user;
13975
+ let memberships;
13976
+ let skippedChannels;
13977
+ let total;
13978
+ const result = await client.users.channels({ name }, {
13979
+ offset: String(offset),
13980
+ limit: String(limit)
13981
+ });
13982
+ if (result.ok) {
13983
+ user = result.data.kind === "agent" ? {
13984
+ kind: "agent",
13985
+ value: result.data.user
13986
+ } : {
13987
+ kind: "human",
13988
+ value: result.data.user
13989
+ };
13990
+ memberships = result.data.memberships.map((channel) => ({
13910
13991
  ...channel,
13911
13992
  joined: true,
13912
13993
  muted: void 0,
13913
13994
  activityMuted: void 0
13914
- });
13915
- }
13995
+ }));
13996
+ skippedChannels = result.data.uncheckedCount;
13997
+ total = result.data.page.total;
13998
+ } else if (isUserNotFound(result)) return notFound();
13999
+ else if (isUserChannelsGateRefusal(result)) {
14000
+ const info = await client.server.info();
14001
+ if (!info.ok) return failureFromClientResult(info);
14002
+ const agent = info.data.agents.find((candidate) => candidate.name === name);
14003
+ const human = info.data.humans.find((candidate) => candidate.name === name);
14004
+ if (agent) user = {
14005
+ kind: "agent",
14006
+ value: agent
14007
+ };
14008
+ else if (human) user = {
14009
+ kind: "human",
14010
+ value: human
14011
+ };
14012
+ else return notFound();
14013
+ memberships = [];
14014
+ skippedChannels = info.data.channels.slice(offset, offset + limit).length;
14015
+ total = info.data.channels.length;
14016
+ } else return failureFromClientResult(result);
13916
14017
  const nextOffset = offset + limit;
13917
- const nextHint = nextOffset < visibleChannels.length ? RAFT_HINTS.userInfo({
14018
+ const nextHint = nextOffset < total ? RAFT_HINTS.userInfo({
13918
14019
  name,
13919
14020
  offset: nextOffset,
13920
14021
  limit
13921
14022
  }) : null;
13922
14023
  const page = {
13923
- total: visibleChannels.length,
14024
+ total,
13924
14025
  offset,
13925
14026
  limit,
13926
14027
  nextCommand: nextHint ? formatHint(nextHint, options.hints) : void 0
@@ -15841,7 +15942,7 @@ const OPERATION_DEFS = [
15841
15942
  name: "users.info",
15842
15943
  description: "A human's or agent's visible facts (kind, status, role, description) and which visible channels they are in. Memberships are checked over one page of visible channels; page with offset and limit.",
15843
15944
  schema: userInfoRequestSchema,
15844
- routes: ["serverInfo", "channelMembers"],
15945
+ routes: ["userChannels", "serverInfo"],
15845
15946
  consumes: nothing,
15846
15947
  output: {
15847
15948
  mayBeLarge: true,
package/dist/esm/index.js CHANGED
@@ -4706,6 +4706,7 @@ const AGENT_API_ROUTE_META = {
4706
4706
  inboxList: read,
4707
4707
  agentContext: read,
4708
4708
  serverInfo: read,
4709
+ userChannels: read,
4709
4710
  serverUpdate: naturalDestructive,
4710
4711
  mentionActionsPending: read,
4711
4712
  senderMentionDeliveries: read,
@@ -5707,6 +5708,31 @@ const agentApiAgentContextResponseSchema = passthroughObject({
5707
5708
  text: string()
5708
5709
  }).nullable()
5709
5710
  });
5711
+ const agentApiServerInfoChannelSchema = passthroughObject({
5712
+ id: string().transform(asChannelId),
5713
+ name: string(),
5714
+ joined: boolean()
5715
+ });
5716
+ const agentApiServerInfoAgentSchema = passthroughObject({
5717
+ name: string(),
5718
+ status: string().optional(),
5719
+ activity: string().nullable().optional(),
5720
+ activityDetail: string().nullable().optional(),
5721
+ role: _enum([
5722
+ "owner",
5723
+ "admin",
5724
+ "member"
5725
+ ]).nullable().optional()
5726
+ });
5727
+ const agentApiServerInfoHumanSchema = passthroughObject({
5728
+ name: string(),
5729
+ role: _enum([
5730
+ "owner",
5731
+ "admin",
5732
+ "member",
5733
+ "guest"
5734
+ ]).nullable().optional()
5735
+ });
5710
5736
  const agentApiServerInfoResponseSchema = passthroughObject({
5711
5737
  runtimeContext: passthroughObject({
5712
5738
  agentId: string(),
@@ -5724,32 +5750,39 @@ const agentApiServerInfoResponseSchema = passthroughObject({
5724
5750
  }),
5725
5751
  serverRole: string().nullable().optional(),
5726
5752
  serverCapabilities: passthroughObject({}).optional(),
5727
- channels: array(passthroughObject({
5728
- id: string().transform(asChannelId),
5729
- name: string(),
5730
- joined: boolean()
5731
- })),
5732
- agents: array(passthroughObject({
5733
- name: string(),
5734
- status: string().optional(),
5735
- activity: string().nullable().optional(),
5736
- activityDetail: string().nullable().optional(),
5737
- role: _enum([
5738
- "owner",
5739
- "admin",
5740
- "member"
5741
- ]).nullable().optional()
5742
- })),
5743
- humans: array(passthroughObject({
5744
- name: string(),
5745
- role: _enum([
5746
- "owner",
5747
- "admin",
5748
- "member",
5749
- "guest"
5750
- ]).nullable().optional()
5751
- }))
5752
- });
5753
+ channels: array(agentApiServerInfoChannelSchema),
5754
+ agents: array(agentApiServerInfoAgentSchema),
5755
+ humans: array(agentApiServerInfoHumanSchema)
5756
+ });
5757
+ const nonNegativeIntegerStringSchema = string().trim().regex(/^(0|[1-9][0-9]*)$/, "must be a non-negative integer");
5758
+ const agentApiUserChannelsParamsSchema = passthroughObject({ name: string().trim().min(1) });
5759
+ const agentApiUserChannelsQuerySchema = passthroughObject({
5760
+ offset: nonNegativeIntegerStringSchema.optional(),
5761
+ limit: string().trim().regex(/^([1-9]|[1-9][0-9]|1[0-9][0-9]|200)$/, "must be an integer from 1 to 200").optional()
5762
+ });
5763
+ const agentApiUserChannelsPageFields = {
5764
+ memberships: array(agentApiServerInfoChannelSchema),
5765
+ uncheckedCount: number$1().int().nonnegative(),
5766
+ page: passthroughObject({
5767
+ total: number$1().int().nonnegative(),
5768
+ offset: number$1().int().nonnegative(),
5769
+ limit: number$1().int().positive()
5770
+ })
5771
+ };
5772
+ /**
5773
+ * One user's visible facts and their memberships among one window of the
5774
+ * caller's visible channels (`GET /users/:name/channels`). `user` is the
5775
+ * user's `serverInfo` agents / humans entry, unchanged.
5776
+ */
5777
+ const agentApiUserChannelsResponseSchema = discriminatedUnion("kind", [passthroughObject({
5778
+ user: agentApiServerInfoAgentSchema,
5779
+ kind: literal("agent"),
5780
+ ...agentApiUserChannelsPageFields
5781
+ }), passthroughObject({
5782
+ user: agentApiServerInfoHumanSchema,
5783
+ kind: literal("human"),
5784
+ ...agentApiUserChannelsPageFields
5785
+ })]);
5753
5786
  const agentApiMentionActionsPendingQuerySchema = passthroughObject({ limit: optionalStringSchema });
5754
5787
  const agentApiMentionActionEnvelopeSchema = passthroughObject({ resolutionId: string() });
5755
5788
  const agentApiMentionActionResultSchema = passthroughObject({
@@ -6742,6 +6775,22 @@ const agentApiContract = {
6742
6775
  request: {},
6743
6776
  response: { body: agentApiServerInfoResponseSchema }
6744
6777
  }),
6778
+ userChannels: route({
6779
+ key: "userChannels",
6780
+ method: "GET",
6781
+ path: "/users/:name/channels",
6782
+ client: {
6783
+ resource: "users",
6784
+ method: "channels"
6785
+ },
6786
+ capability: "channels",
6787
+ description: "One visible agent or human (exact name, agents first; the serverInfo entry) and which of one window of your visible channels (serverInfo order, offset/limit, limit at most 200) list them as a member, in one request. Needs the read capability as well. 404 user_not_found when no visible user has that name.",
6788
+ request: {
6789
+ params: agentApiUserChannelsParamsSchema,
6790
+ query: agentApiUserChannelsQuerySchema
6791
+ },
6792
+ response: { body: agentApiUserChannelsResponseSchema }
6793
+ }),
6745
6794
  serverUpdate: route({
6746
6795
  key: "serverUpdate",
6747
6796
  method: "PATCH",
@@ -8607,6 +8656,31 @@ const AGENT_API_ROUTE_MANIFEST = [
8607
8656
  "body": true
8608
8657
  }
8609
8658
  },
8659
+ {
8660
+ "key": "userChannels",
8661
+ "method": "GET",
8662
+ "path": "/users/:name/channels",
8663
+ "fullPath": "/internal/agent-api/users/:name/channels",
8664
+ "client": {
8665
+ "resource": "users",
8666
+ "method": "channels"
8667
+ },
8668
+ "capability": "channels",
8669
+ "description": "One visible agent or human (exact name, agents first; the serverInfo entry) and which of one window of your visible channels (serverInfo order, offset/limit, limit at most 200) list them as a member, in one request. Needs the read capability as well. 404 user_not_found when no visible user has that name.",
8670
+ "sideEffect": "read",
8671
+ "idempotency": "natural",
8672
+ "destructive": false,
8673
+ "audience": "both",
8674
+ "request": {
8675
+ "params": true,
8676
+ "query": true,
8677
+ "body": false
8678
+ },
8679
+ "response": {
8680
+ "kind": "json",
8681
+ "body": true
8682
+ }
8683
+ },
8610
8684
  {
8611
8685
  "key": "serverUpdate",
8612
8686
  "method": "PATCH",
@@ -9909,7 +9983,7 @@ const AGENT_API_ROUTE_MANIFEST = [
9909
9983
  }
9910
9984
  ];
9911
9985
  /** Content hash of AGENT_API_ROUTE_MANIFEST; see computeAgentApiManifestVersion. */
9912
- const AGENT_API_MANIFEST_VERSION = "c762a744e3542f89";
9986
+ const AGENT_API_MANIFEST_VERSION = "edd02bcf059dbd7c";
9913
9987
  //#endregion
9914
9988
  //#region src/routes.ts
9915
9989
  const ROUTE_INFO = Object.fromEntries(AGENT_API_ROUTE_MANIFEST.map((entry) => {
@@ -13860,12 +13934,23 @@ async function updateProfile(client, request, options = {}) {
13860
13934
  const userInfoRequestSchema = requestSchema()(object({
13861
13935
  name: string().describe("The human or agent, `@handle` (or the bare handle)."),
13862
13936
  offset: number$1().int().nonnegative().optional().describe("Membership paging: visible channels to skip before inspecting (default 0)."),
13863
- limit: number$1().int().positive().optional().describe("Membership paging: visible channels to inspect (default 50).")
13937
+ limit: number$1().int().positive().max(200).optional().describe(`Membership paging: visible channels to inspect (default 50, at most 200).`)
13864
13938
  }));
13939
+ /** The route's gate refused this credential (capability, grant, or runner session); `server.info` decides what remains visible. */
13940
+ function isUserChannelsGateRefusal(result) {
13941
+ if (result.error.kind !== "http") return false;
13942
+ return result.error.status === 403 || result.error.status === 501 && result.error.errorCode === "unsupported_capability";
13943
+ }
13944
+ function isUserNotFound(result) {
13945
+ return result.error.kind === "http" && result.error.status === 404 && result.error.errorCode === "user_not_found";
13946
+ }
13865
13947
  /**
13866
- * `raft user info <name>`: the user's visible facts from `server.info`, and
13867
- * their memberships among one page of the visible channels, checked one
13868
- * channel roster at a time (a rejected roster is skipped and counted).
13948
+ * `raft user info <name>`: the user's visible facts (their `server.info`
13949
+ * entry) and their memberships among one page of the visible channels, in one
13950
+ * `users.channels` request. A channel whose roster the Server would refuse is
13951
+ * skipped and counted. A credential the route refuses (no `channels`
13952
+ * capability or `channel:read` grant) can read no roster, so the user is
13953
+ * looked up in `server.info` and every inspected channel is skipped.
13869
13954
  */
13870
13955
  async function userInfo(client, request, options = {}) {
13871
13956
  const invalid = validateOpRequest(userInfoRequestSchema, request);
@@ -13875,18 +13960,7 @@ async function userInfo(client, request, options = {}) {
13875
13960
  if (!name) return failureOutcome(opError("INVALID_REQUEST", { message: "user name is required" }));
13876
13961
  const limit = request.limit ?? 50;
13877
13962
  const offset = request.offset ?? 0;
13878
- const info = await client.server.info();
13879
- if (!info.ok) return failureFromClientResult(info);
13880
- const agent = info.data.agents.find((candidate) => candidate.name === name);
13881
- const human = info.data.humans.find((candidate) => candidate.name === name);
13882
- const user = agent ? {
13883
- kind: "agent",
13884
- value: agent
13885
- } : human ? {
13886
- kind: "human",
13887
- value: human
13888
- } : null;
13889
- if (!user) return failureOutcome(opError("NOT_FOUND", {
13963
+ const notFound = () => failureOutcome(opError("NOT_FOUND", {
13890
13964
  message: `User not found or not visible: @${name}`,
13891
13965
  nextAction: `Run \`${formatHint(RAFT_HINTS.serverInfo({
13892
13966
  view: "agents",
@@ -13896,30 +13970,57 @@ async function userInfo(client, request, options = {}) {
13896
13970
  query: true
13897
13971
  }), options.hints)}\` to inspect visible users.`
13898
13972
  }));
13899
- const visibleChannels = info.data.channels;
13900
- const memberships = [];
13901
- let skippedChannels = 0;
13902
- for (const channel of visibleChannels.slice(offset, offset + limit)) {
13903
- const members = await client.channels.members({ channel: `#${channel.name}` });
13904
- if (!members.ok) {
13905
- skippedChannels += 1;
13906
- continue;
13907
- }
13908
- if ((user.kind === "agent" ? members.data.agents ?? [] : members.data.humans ?? []).some((candidate) => candidate.name === name)) memberships.push({
13973
+ let user;
13974
+ let memberships;
13975
+ let skippedChannels;
13976
+ let total;
13977
+ const result = await client.users.channels({ name }, {
13978
+ offset: String(offset),
13979
+ limit: String(limit)
13980
+ });
13981
+ if (result.ok) {
13982
+ user = result.data.kind === "agent" ? {
13983
+ kind: "agent",
13984
+ value: result.data.user
13985
+ } : {
13986
+ kind: "human",
13987
+ value: result.data.user
13988
+ };
13989
+ memberships = result.data.memberships.map((channel) => ({
13909
13990
  ...channel,
13910
13991
  joined: true,
13911
13992
  muted: void 0,
13912
13993
  activityMuted: void 0
13913
- });
13914
- }
13994
+ }));
13995
+ skippedChannels = result.data.uncheckedCount;
13996
+ total = result.data.page.total;
13997
+ } else if (isUserNotFound(result)) return notFound();
13998
+ else if (isUserChannelsGateRefusal(result)) {
13999
+ const info = await client.server.info();
14000
+ if (!info.ok) return failureFromClientResult(info);
14001
+ const agent = info.data.agents.find((candidate) => candidate.name === name);
14002
+ const human = info.data.humans.find((candidate) => candidate.name === name);
14003
+ if (agent) user = {
14004
+ kind: "agent",
14005
+ value: agent
14006
+ };
14007
+ else if (human) user = {
14008
+ kind: "human",
14009
+ value: human
14010
+ };
14011
+ else return notFound();
14012
+ memberships = [];
14013
+ skippedChannels = info.data.channels.slice(offset, offset + limit).length;
14014
+ total = info.data.channels.length;
14015
+ } else return failureFromClientResult(result);
13915
14016
  const nextOffset = offset + limit;
13916
- const nextHint = nextOffset < visibleChannels.length ? RAFT_HINTS.userInfo({
14017
+ const nextHint = nextOffset < total ? RAFT_HINTS.userInfo({
13917
14018
  name,
13918
14019
  offset: nextOffset,
13919
14020
  limit
13920
14021
  }) : null;
13921
14022
  const page = {
13922
- total: visibleChannels.length,
14023
+ total,
13923
14024
  offset,
13924
14025
  limit,
13925
14026
  nextCommand: nextHint ? formatHint(nextHint, options.hints) : void 0
@@ -15840,7 +15941,7 @@ const OPERATION_DEFS = [
15840
15941
  name: "users.info",
15841
15942
  description: "A human's or agent's visible facts (kind, status, role, description) and which visible channels they are in. Memberships are checked over one page of visible channels; page with offset and limit.",
15842
15943
  schema: userInfoRequestSchema,
15843
- routes: ["serverInfo", "channelMembers"],
15944
+ routes: ["userChannels", "serverInfo"],
15844
15945
  consumes: nothing,
15845
15946
  output: {
15846
15947
  mayBeLarge: true,
package/dist/index.d.ts CHANGED
@@ -1164,7 +1164,7 @@ interface UserInfoRequest {
1164
1164
  name: string;
1165
1165
  /** Visible channels to skip before inspecting memberships (default 0). */
1166
1166
  offset?: number;
1167
- /** Visible channels to inspect for memberships (default 50). */
1167
+ /** Visible channels to inspect for memberships (default 50, at most 200). */
1168
1168
  limit?: number;
1169
1169
  }
1170
1170
  export declare const userInfoRequestSchema: z.ZodObject<{
@@ -2561,6 +2561,65 @@ declare const agentApiServerInfoResponseSchema: z.ZodObject<{
2561
2561
  }>>>;
2562
2562
  }, z.core.$loose>>;
2563
2563
  }, z.core.$loose>;
2564
+ declare const agentApiUserChannelsParamsSchema: z.ZodObject<{
2565
+ name: z.ZodString;
2566
+ }, z.core.$loose>;
2567
+ declare const agentApiUserChannelsQuerySchema: z.ZodObject<{
2568
+ offset: z.ZodOptional<z.ZodString>;
2569
+ limit: z.ZodOptional<z.ZodString>;
2570
+ }, z.core.$loose>;
2571
+ /**
2572
+ * One user's visible facts and their memberships among one window of the
2573
+ * caller's visible channels (`GET /users/:name/channels`). `user` is the
2574
+ * user's `serverInfo` agents / humans entry, unchanged.
2575
+ */
2576
+ declare const agentApiUserChannelsResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
2577
+ memberships: z.ZodArray<z.ZodObject<{
2578
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<ChannelId, string>>;
2579
+ name: z.ZodString;
2580
+ joined: z.ZodBoolean;
2581
+ }, z.core.$loose>>;
2582
+ uncheckedCount: z.ZodNumber;
2583
+ page: z.ZodObject<{
2584
+ total: z.ZodNumber;
2585
+ offset: z.ZodNumber;
2586
+ limit: z.ZodNumber;
2587
+ }, z.core.$loose>;
2588
+ user: z.ZodObject<{
2589
+ name: z.ZodString;
2590
+ status: z.ZodOptional<z.ZodString>;
2591
+ activity: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2592
+ activityDetail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2593
+ role: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
2594
+ owner: "owner";
2595
+ admin: "admin";
2596
+ member: "member";
2597
+ }>>>;
2598
+ }, z.core.$loose>;
2599
+ kind: z.ZodLiteral<"agent">;
2600
+ }, z.core.$loose>, z.ZodObject<{
2601
+ memberships: z.ZodArray<z.ZodObject<{
2602
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<ChannelId, string>>;
2603
+ name: z.ZodString;
2604
+ joined: z.ZodBoolean;
2605
+ }, z.core.$loose>>;
2606
+ uncheckedCount: z.ZodNumber;
2607
+ page: z.ZodObject<{
2608
+ total: z.ZodNumber;
2609
+ offset: z.ZodNumber;
2610
+ limit: z.ZodNumber;
2611
+ }, z.core.$loose>;
2612
+ user: z.ZodObject<{
2613
+ name: z.ZodString;
2614
+ role: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
2615
+ guest: "guest";
2616
+ owner: "owner";
2617
+ admin: "admin";
2618
+ member: "member";
2619
+ }>>>;
2620
+ }, z.core.$loose>;
2621
+ kind: z.ZodLiteral<"human">;
2622
+ }, z.core.$loose>], "kind">;
2564
2623
  declare const agentApiMentionActionsPendingQuerySchema: z.ZodObject<{
2565
2624
  limit: z.ZodOptional<z.ZodString>;
2566
2625
  }, z.core.$loose>;
@@ -5731,6 +5790,77 @@ declare const agentApiContract: {
5731
5790
  } & {
5732
5791
  fullPath: string;
5733
5792
  };
5793
+ readonly userChannels: {
5794
+ readonly key: "userChannels";
5795
+ readonly method: "GET";
5796
+ readonly path: "/users/:name/channels";
5797
+ readonly client: {
5798
+ readonly resource: "users";
5799
+ readonly method: "channels";
5800
+ };
5801
+ readonly capability: "channels";
5802
+ readonly description: "One visible agent or human (exact name, agents first; the serverInfo entry) and which of one window of your visible channels (serverInfo order, offset/limit, limit at most 200) list them as a member, in one request. Needs the read capability as well. 404 user_not_found when no visible user has that name.";
5803
+ readonly request: {
5804
+ readonly params: z.ZodObject<{
5805
+ name: z.ZodString;
5806
+ }, z.core.$loose>;
5807
+ readonly query: z.ZodObject<{
5808
+ offset: z.ZodOptional<z.ZodString>;
5809
+ limit: z.ZodOptional<z.ZodString>;
5810
+ }, z.core.$loose>;
5811
+ };
5812
+ readonly response: {
5813
+ readonly body: z.ZodDiscriminatedUnion<[z.ZodObject<{
5814
+ memberships: z.ZodArray<z.ZodObject<{
5815
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<ChannelId, string>>;
5816
+ name: z.ZodString;
5817
+ joined: z.ZodBoolean;
5818
+ }, z.core.$loose>>;
5819
+ uncheckedCount: z.ZodNumber;
5820
+ page: z.ZodObject<{
5821
+ total: z.ZodNumber;
5822
+ offset: z.ZodNumber;
5823
+ limit: z.ZodNumber;
5824
+ }, z.core.$loose>;
5825
+ user: z.ZodObject<{
5826
+ name: z.ZodString;
5827
+ status: z.ZodOptional<z.ZodString>;
5828
+ activity: z.ZodOptional<z.ZodNullable<z.ZodString>>;
5829
+ activityDetail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
5830
+ role: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
5831
+ owner: "owner";
5832
+ admin: "admin";
5833
+ member: "member";
5834
+ }>>>;
5835
+ }, z.core.$loose>;
5836
+ kind: z.ZodLiteral<"agent">;
5837
+ }, z.core.$loose>, z.ZodObject<{
5838
+ memberships: z.ZodArray<z.ZodObject<{
5839
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<ChannelId, string>>;
5840
+ name: z.ZodString;
5841
+ joined: z.ZodBoolean;
5842
+ }, z.core.$loose>>;
5843
+ uncheckedCount: z.ZodNumber;
5844
+ page: z.ZodObject<{
5845
+ total: z.ZodNumber;
5846
+ offset: z.ZodNumber;
5847
+ limit: z.ZodNumber;
5848
+ }, z.core.$loose>;
5849
+ user: z.ZodObject<{
5850
+ name: z.ZodString;
5851
+ role: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
5852
+ guest: "guest";
5853
+ owner: "owner";
5854
+ admin: "admin";
5855
+ member: "member";
5856
+ }>>>;
5857
+ }, z.core.$loose>;
5858
+ kind: z.ZodLiteral<"human">;
5859
+ }, z.core.$loose>], "kind">;
5860
+ };
5861
+ } & {
5862
+ fullPath: string;
5863
+ };
5734
5864
  readonly serverUpdate: {
5735
5865
  readonly key: "serverUpdate";
5736
5866
  readonly method: "PATCH";
@@ -8740,6 +8870,9 @@ type AgentApiOkResponse = z.infer<typeof agentApiOkResponseSchema>;
8740
8870
  type AgentApiActionPrepareResponse = z.infer<typeof agentApiActionPrepareResponseSchema>;
8741
8871
  type AgentApiAgentContextResponse = z.infer<typeof agentApiAgentContextResponseSchema>;
8742
8872
  type AgentApiServerInfoResponse = z.infer<typeof agentApiServerInfoResponseSchema>;
8873
+ type AgentApiUserChannelsParams = z.infer<typeof agentApiUserChannelsParamsSchema>;
8874
+ type AgentApiUserChannelsQuery = z.infer<typeof agentApiUserChannelsQuerySchema>;
8875
+ type AgentApiUserChannelsResponse = z.infer<typeof agentApiUserChannelsResponseSchema>;
8743
8876
  type AgentApiServerUpdateBody = z.infer<typeof agentApiServerUpdateBodySchema>;
8744
8877
  type AgentApiServerUpdateResponse = z.infer<typeof agentApiServerUpdateResponseSchema>;
8745
8878
  type AgentApiMentionActionsPendingQuery = z.infer<typeof agentApiMentionActionsPendingQuerySchema>;
@@ -8806,6 +8939,7 @@ type AgentApiRequestParamsByRoute = {
8806
8939
  inboxList: never;
8807
8940
  agentContext: never;
8808
8941
  serverInfo: never;
8942
+ userChannels: AgentApiUserChannelsParams;
8809
8943
  serverUpdate: never;
8810
8944
  mentionActionsPending: never;
8811
8945
  senderMentionDeliveries: AgentApiSenderMentionDeliveriesParams;
@@ -8887,6 +9021,7 @@ type AgentApiRequestQueryByRoute = {
8887
9021
  inboxList: AgentApiInboxListQuery;
8888
9022
  agentContext: never;
8889
9023
  serverInfo: never;
9024
+ userChannels: AgentApiUserChannelsQuery;
8890
9025
  serverUpdate: never;
8891
9026
  mentionActionsPending: AgentApiMentionActionsPendingQuery;
8892
9027
  senderMentionDeliveries: never;
@@ -8968,6 +9103,7 @@ type AgentApiRequestBodyByRoute = {
8968
9103
  inboxList: never;
8969
9104
  agentContext: never;
8970
9105
  serverInfo: never;
9106
+ userChannels: never;
8971
9107
  serverUpdate: AgentApiServerUpdateBody;
8972
9108
  mentionActionsPending: never;
8973
9109
  senderMentionDeliveries: never;
@@ -9049,6 +9185,7 @@ type AgentApiResponseByRoute = {
9049
9185
  inboxList: AgentApiInboxListResponse;
9050
9186
  agentContext: AgentApiAgentContextResponse;
9051
9187
  serverInfo: AgentApiServerInfoResponse;
9188
+ userChannels: AgentApiUserChannelsResponse;
9052
9189
  serverUpdate: AgentApiServerUpdateResponse;
9053
9190
  mentionActionsPending: AgentApiMentionActionsPendingResponse;
9054
9191
  senderMentionDeliveries: AgentApiSenderMentionDeliveriesResponse;
@@ -10514,7 +10651,7 @@ declare const OPERATION_DEFS: readonly [{
10514
10651
  offset: z.ZodOptional<z.ZodNumber>;
10515
10652
  limit: z.ZodOptional<z.ZodNumber>;
10516
10653
  }, z.core.$strip>;
10517
- readonly routes: readonly ["serverInfo", "channelMembers"];
10654
+ readonly routes: readonly ["userChannels", "serverInfo"];
10518
10655
  readonly consumes: {
10519
10656
  model: never[];
10520
10657
  code: never[];
package/operations.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "raft-sdk-operations.v1",
3
- "version": "16c7523f47f8b204",
3
+ "version": "2a304e867c59570a",
4
4
  "operations": [
5
5
  {
6
6
  "name": "identity.whoami",
@@ -2141,8 +2141,9 @@
2141
2141
  },
2142
2142
  "limit": {
2143
2143
  "type": "integer",
2144
- "description": "Membership paging: visible channels to inspect (default 50).",
2145
- "minimum": 1
2144
+ "description": "Membership paging: visible channels to inspect (default 50, at most 200).",
2145
+ "minimum": 1,
2146
+ "maximum": 200
2146
2147
  }
2147
2148
  },
2148
2149
  "required": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "license": "FSL-1.1-ALv2",
5
5
  "description": "Typed Raft Agent API client for external agents and bots.",
6
6
  "type": "module",