commonswarm 0.1.70 → 0.1.71

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.
Files changed (2) hide show
  1. package/cswarm.cjs +68 -16
  2. package/package.json +1 -1
package/cswarm.cjs CHANGED
@@ -4088,7 +4088,10 @@ async function readAgentProfile(path) {
4088
4088
  } catch {
4089
4089
  throw new AgentSetupError("profile_invalid", "The agent profile is damaged. Run setup again.");
4090
4090
  }
4091
- if (!p || p.version !== 1 || Object.keys(p).sort().join() !== ["version", "url", "anon_key", "workspace_id", "principal_id", "credential_file"].sort().join() || typeof p.url !== "string" || typeof p.anon_key !== "string" || typeof p.workspace_id !== "string" || !ONBOARDING_UUID.test(p.workspace_id) || typeof p.principal_id !== "string" || !ONBOARDING_UUID.test(p.principal_id) || p.credential_file !== (0, import_node_path4.join)((0, import_node_path4.dirname)(path), "credential.json")) {
4091
+ const required2 = ["version", "url", "anon_key", "workspace_id", "principal_id", "credential_file"];
4092
+ const keys = Object.keys(p ?? {}).sort().join();
4093
+ const keysAccepted = keys === [...required2].sort().join() || keys === [...required2, "workspace_name"].sort().join();
4094
+ if (!p || p.version !== 1 || !keysAccepted || p.workspace_name !== void 0 && (typeof p.workspace_name !== "string" || p.workspace_name.length > 200) || typeof p.url !== "string" || typeof p.anon_key !== "string" || typeof p.workspace_id !== "string" || !ONBOARDING_UUID.test(p.workspace_id) || typeof p.principal_id !== "string" || !ONBOARDING_UUID.test(p.principal_id) || p.credential_file !== (0, import_node_path4.join)((0, import_node_path4.dirname)(path), "credential.json")) {
4092
4095
  throw new AgentSetupError("profile_invalid", "The agent profile is damaged. Run setup again.");
4093
4096
  }
4094
4097
  checkedTarget2(p.url, p.anon_key);
@@ -4107,7 +4110,7 @@ async function openProfileCredential(profile, fetcher = fetch) {
4107
4110
  const store2 = await agentCredentialStore({ target: target2, lineageKey: credentialLineageKey(agent.token) });
4108
4111
  return AgentCredentialSession.open({ target: target2, workspaceId: profile.workspace_id, presented: agent, store: store2, fetcher });
4109
4112
  }
4110
- async function saveAgentProfile(path, connection2) {
4113
+ async function saveAgentProfile(path, connection2, workspaceName) {
4111
4114
  path = await assertPrivateLocation(path);
4112
4115
  const profile = {
4113
4116
  version: 1,
@@ -4115,7 +4118,11 @@ async function saveAgentProfile(path, connection2) {
4115
4118
  anon_key: connection2.anon_key,
4116
4119
  workspace_id: connection2.workspace_id,
4117
4120
  principal_id: connection2.principal_id,
4118
- credential_file: (0, import_node_path4.join)((0, import_node_path4.dirname)(path), "credential.json")
4121
+ credential_file: (0, import_node_path4.join)((0, import_node_path4.dirname)(path), "credential.json"),
4122
+ /* Only when the server actually gave one. The key is omitted rather than written null, so
4123
+ * a profile from a deployment that does not send the name keeps exactly the six keys every
4124
+ * released client already accepts. */
4125
+ ...workspaceName === void 0 ? {} : { workspace_name: workspaceName }
4119
4126
  };
4120
4127
  await withFileLock((0, import_node_path4.dirname)(path), "setup", async () => {
4121
4128
  const existingRaw = await readSecureJsonFileIfPresent(path, ONBOARDING_MAX_FILE_BYTES);
@@ -5760,11 +5767,16 @@ function parseAgentIdentity(value) {
5760
5767
  if (row.credential_valid !== true) {
5761
5768
  throw new Error("member read returned a malformed credential validity");
5762
5769
  }
5770
+ const name = row.workspace_name;
5771
+ if (name !== void 0 && name !== null && typeof name !== "string") {
5772
+ throw new Error("member read returned a malformed workspace name");
5773
+ }
5763
5774
  return {
5764
5775
  credential_valid: true,
5765
5776
  owner_user_id: checkedUuid2(row.owner_user_id, "identity owner_user_id"),
5766
5777
  principal_id: checkedUuid2(row.principal_id, "identity principal_id"),
5767
- workspace_id: checkedUuid2(row.workspace_id, "identity workspace_id")
5778
+ workspace_id: checkedUuid2(row.workspace_id, "identity workspace_id"),
5779
+ workspace_name: typeof name === "string" ? name : null
5768
5780
  };
5769
5781
  }
5770
5782
  async function readAgentSignalDirectory(target2, token, workspaceId2, fetcherOrOptions = fetch) {
@@ -6044,16 +6056,17 @@ function askReplyReadFailureMessage(workspaceId2, error2) {
6044
6056
  return `Your message was posted, but its reply could not be fetched (${failure.detail}). Do not resend this ask. Check with: cswarm inbox --workspace-id ${workspaceId2}`;
6045
6057
  }
6046
6058
  function renderSignals(signals, options) {
6059
+ const heading = (base) => options.workspace === void 0 ? `${base}:` : `${base} \u2014 ${options.workspace.name === null ? options.workspace.id : `${options.workspace.name} (${options.workspace.id})`}:`;
6047
6060
  const feedScopeGuidance = "This feed shows broadcast signals only. It omits directed messages, including messages you sent. Read messages directed to you with: cswarm inbox";
6048
6061
  if (signals.length === 0) {
6049
6062
  return [
6050
- options.inbox ? "Inbox:" : "Recent broadcast signals:",
6063
+ heading(options.inbox ? "Inbox" : "Recent broadcast signals"),
6051
6064
  options.inbox ? "Nothing is waiting for you." : options.includeStale ? "No broadcast signals have been shared in this workspace yet." : "No live broadcast signals in this workspace yet.",
6052
6065
  ...options.inbox ? [] : [feedScopeGuidance]
6053
6066
  ].join("\n");
6054
6067
  }
6055
6068
  const now = options.now ?? Date.now();
6056
- const lines = [options.inbox ? "Inbox:" : "Recent broadcast signals:"];
6069
+ const lines = [heading(options.inbox ? "Inbox" : "Recent broadcast signals")];
6057
6070
  for (const signal of signals) {
6058
6071
  const authorKind = signal.from_kind === "agent" ? "agent" : "member";
6059
6072
  const authorName = signal.from_kind === "agent" ? options.authors?.agents.get(signal.from) : options.authors?.users.get(signal.from);
@@ -6623,11 +6636,14 @@ async function checkAgentMessages(options) {
6623
6636
  consumed += 1;
6624
6637
  }
6625
6638
  const hasMore = consumed < page.signals.length || page.rawCount >= AGENT_CHECK_PAGE_SIZE;
6639
+ const rawName = directory.identity?.workspace_name;
6626
6640
  const result = {
6627
6641
  checked: true,
6628
6642
  cached: false,
6629
6643
  messages: messages2,
6630
6644
  has_more: hasMore,
6645
+ workspace_id: profile.workspace_id,
6646
+ workspace_name: rawName == null || rawName.trim() === "" ? null : rawName,
6631
6647
  next_action: hasMore ? `More messages may remain. Run cswarm check --profile ${shellQuote(profilePath)}${options.hostSessionId ? ` --host-session-id ${shellQuote(options.hostSessionId)}` : ""} again.` : null
6632
6648
  };
6633
6649
  if (signal.aborted) throw new AgentSetupError("check_timeout", "The message check timed out. Try again.");
@@ -50732,6 +50748,7 @@ __export(cli_exports, {
50732
50748
  readBoundedUtf8Stream: () => readBoundedUtf8Stream,
50733
50749
  renderListenerStatus: () => renderListenerStatus,
50734
50750
  renderRoster: () => renderRoster,
50751
+ renderWorkspace: () => renderWorkspace,
50735
50752
  replyAllowedFlags: () => replyAllowedFlags,
50736
50753
  replyRefusalHint: () => replyRefusalHint,
50737
50754
  resolveDetachedClaudeExecutable: () => resolveDetachedClaudeExecutable,
@@ -50740,7 +50757,8 @@ __export(cli_exports, {
50740
50757
  resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer,
50741
50758
  stripSingleTrailingNewline: () => stripSingleTrailingNewline,
50742
50759
  threadReplyMessage: () => threadReplyMessage,
50743
- usage: () => usage
50760
+ usage: () => usage,
50761
+ workspaceLabel: () => workspaceLabel
50744
50762
  });
50745
50763
  module.exports = __toCommonJS(cli_exports);
50746
50764
  var import_node_crypto23 = require("node:crypto");
@@ -50838,11 +50856,15 @@ async function setupAgent(options) {
50838
50856
  if (!page.capabilities.cursorAfter) throw new AgentSetupError("check_paging_unsupported", "Update this deployment to support inbox paging before using quick setup.");
50839
50857
  return {
50840
50858
  name: directory.agents.find((a) => a.principal_id === connection2.principal_id)?.name,
50859
+ /* The workspace's human name, from the directory read this already does. Item D: the
50860
+ * agent and the person must call one workspace the same thing, and setup is where the
50861
+ * agent first learns which workspace it is in. */
50862
+ workspace_name: directory.identity?.workspace_name ?? null,
50841
50863
  inbox_pending: page.signals.length > 0,
50842
50864
  expires_at: session.expiry === null ? null : new Date(session.expiry).toISOString()
50843
50865
  };
50844
50866
  }, options.fetcher);
50845
- await saveAgentProfile(profilePath, connection2);
50867
+ await saveAgentProfile(profilePath, connection2, identity.workspace_name ?? void 0);
50846
50868
  const receive = await readReceiveBinding(profilePath, options.hostSessionId);
50847
50869
  const wakeProviders = RECEIVE_WAKE_PROVIDERS.map((provider) => ({ provider, preview: provider === RECEIVE_WAKE_PROVIDER, requires_idle_test: true }));
50848
50870
  const primaryWakeProvider = wakeProviders.find((provider) => provider.provider === RECEIVE_WAKE_PROVIDER);
@@ -63697,8 +63719,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
63697
63719
  ]);
63698
63720
  var UUID_RE25 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
63699
63721
  function packageVersion() {
63700
- if ("0.1.70".length > 0) {
63701
- return "0.1.70";
63722
+ if ("0.1.71".length > 0) {
63723
+ return "0.1.71";
63702
63724
  }
63703
63725
  try {
63704
63726
  const value = JSON.parse(
@@ -65895,7 +65917,10 @@ async function signalAuthorLabels(cloud, selectedWorkspace, credential) {
65895
65917
  agent.principal_id,
65896
65918
  sanitizeDisplayLabel(agent.name, "Unnamed agent")
65897
65919
  ])
65898
- )
65920
+ ),
65921
+ /* Free: this directory read already happened for the author names, so naming the
65922
+ * workspace in the inbox and feed headers costs no extra round trip. */
65923
+ workspaceName: workspaceLabel(directory)
65899
65924
  };
65900
65925
  }
65901
65926
  const human = credential.human;
@@ -66361,8 +66386,20 @@ function describeAudience(signal, authors) {
66361
66386
  const name = signal.to_agent !== null ? authors.agents.get(signal.to_agent) : authors.users.get(recipientId);
66362
66387
  return `visible only to ${name === void 0 ? recipientId : `${name} (${recipientId})`}`;
66363
66388
  }
66364
- function renderRoster(directory, memberNames) {
66389
+ function workspaceLabel(directory) {
66390
+ const name = directory.identity?.workspace_name;
66391
+ if (name == null || name.trim() === "") return null;
66392
+ return sanitizeDisplayLabel(name, "Unnamed workspace");
66393
+ }
66394
+ function renderWorkspace(id, name) {
66395
+ return name === null ? id : `${name} (${id})`;
66396
+ }
66397
+ function renderRoster(directory, memberNames, workspace) {
66365
66398
  const lines = [];
66399
+ if (workspace !== void 0) {
66400
+ lines.push(`Workspace: ${renderWorkspace(workspace.workspaceId, workspace.workspaceName)}`);
66401
+ lines.push("");
66402
+ }
66366
66403
  if (directory.members.length === 0 && directory.agents.length === 0) {
66367
66404
  return "This credential is not scoped to that workspace.\n\nThat is all this command can tell you: the answer is the same whether the workspace does\nnot exist or exists without you. Asking about a workspace must not reveal whether it is\nreal, so the two are deliberately indistinguishable.\n";
66368
66405
  }
@@ -66417,6 +66454,9 @@ async function runMembers(args) {
66417
66454
  `${JSON.stringify(
66418
66455
  {
66419
66456
  workspace_id: selected.selectedWorkspace,
66457
+ /* The workspace's human name beside its id, so this roster and the app name one
66458
+ * workspace the same way. null on an older deployment or an archived row. */
66459
+ workspace_name: workspaceLabel(directory),
66420
66460
  members: directory.members.map((member) => ({
66421
66461
  user_id: member.user_id,
66422
66462
  name: memberNames.get(member.user_id) ?? null
@@ -66437,7 +66477,10 @@ async function runMembers(args) {
66437
66477
  );
66438
66478
  return;
66439
66479
  }
66440
- process.stdout.write(renderRoster(directory, memberNames));
66480
+ process.stdout.write(renderRoster(directory, memberNames, {
66481
+ workspaceId: selected.selectedWorkspace,
66482
+ workspaceName: workspaceLabel(directory)
66483
+ }));
66441
66484
  }
66442
66485
  async function runWhoami(args) {
66443
66486
  args.assertShape([
@@ -66498,11 +66541,13 @@ async function runWhoami(args) {
66498
66541
  `
66499
66542
  );
66500
66543
  }
66544
+ const workspaceName = workspaceLabel(directory);
66501
66545
  const output2 = {
66502
66546
  credential_valid: identity.credential_valid,
66503
66547
  principal_id: identity.principal_id,
66504
66548
  display_name: displayName2,
66505
66549
  workspace_id: identity.workspace_id,
66550
+ workspace_name: workspaceName,
66506
66551
  owner_user_id: identity.owner_user_id,
66507
66552
  owner_display_name: ownerName,
66508
66553
  credential_metadata_match: artifactMatches,
@@ -66517,7 +66562,7 @@ async function runWhoami(args) {
66517
66562
  process.stdout.write(
66518
66563
  `You are ${displayName2} (${identity.principal_id}).
66519
66564
  Credential valid now: yes.
66520
- Workspace: ${identity.workspace_id}.
66565
+ Workspace: ${renderWorkspace(identity.workspace_id, workspaceName)}.
66521
66566
  Owner: ${ownerName} (${identity.owner_user_id}).
66522
66567
  ` + (output2.renewal_grant === null ? "Grant: no current renewal grant is visible. Next step: ask a workspace owner to mint a new credential.\n" : `${describeRenewalGrant(output2.renewal_grant).join("\n")}
66523
66568
  `)
@@ -66753,7 +66798,12 @@ async function runSignalRead(args, inbox) {
66753
66798
  process.stdout.write(`${renderSignals(rows3, {
66754
66799
  inbox,
66755
66800
  includeStale: args.has("include-stale"),
66756
- authors
66801
+ authors,
66802
+ /* Name the workspace in the header so a reader can tell this is the inbox they meant.
66803
+ * Omitted on the human path, whose labels carry no name; the header then reads as before. */
66804
+ ...authors.workspaceName === void 0 ? {} : {
66805
+ workspace: { id: selected.selectedWorkspace, name: authors.workspaceName }
66806
+ }
66757
66807
  })}
66758
66808
  `);
66759
66809
  if (selected.kind === "agent") {
@@ -70332,6 +70382,7 @@ ${usage()}
70332
70382
  readBoundedUtf8Stream,
70333
70383
  renderListenerStatus,
70334
70384
  renderRoster,
70385
+ renderWorkspace,
70335
70386
  replyAllowedFlags,
70336
70387
  replyRefusalHint,
70337
70388
  resolveDetachedClaudeExecutable,
@@ -70340,5 +70391,6 @@ ${usage()}
70340
70391
  resolveTurnBudgetOrDefer,
70341
70392
  stripSingleTrailingNewline,
70342
70393
  threadReplyMessage,
70343
- usage
70394
+ usage,
70395
+ workspaceLabel
70344
70396
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.70",
3
+ "version": "0.1.71",
4
4
  "description": "CommonSwarm CLI — coordination for teams where people and AI agents work side by side.",
5
5
  "bin": {
6
6
  "cswarm": "cswarm.cjs"