@wipcomputer/wip-ldm-os 0.4.85-alpha.5 → 0.4.85-alpha.7

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.
@@ -152,6 +152,8 @@ A markdown file that teaches agents when and how to use the tool. The instructio
152
152
 
153
153
  **Install:** the skill folder is deployed to every supported local agent skill surface, including OpenClaw, Codex, Claude Code, and WIP agent compatibility paths when present. If `references/` exists, it is deployed alongside SKILL.md and to `settings/docs/skills/<name>/` in the workspace.
154
154
 
155
+ **Npm package shape:** a public skill package can expose `SKILL.md` at package root. For example, `@wipcomputer/wip-ai-chat-ui` is sourced from a private repo folder at `design/skills/wip-ai-chat-ui/`, but publishes a tarball with `SKILL.md`, `agents/`, and `references/` at package root so `ldm install @wipcomputer/wip-ai-chat-ui` installs the skill directly.
156
+
155
157
  **Structure:**
156
158
  ```
157
159
  repo/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.85-alpha.5",
3
+ "version": "0.4.85-alpha.7",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -17,15 +17,15 @@ function assertNotContains(needle, label) {
17
17
 
18
18
  assertContains('const ACCOUNT_TENANT_PREFIX = "acct:";', "account tenant prefix");
19
19
  assertContains('const LEGACY_API_KEY_TENANT_PREFIX = "key:";', "legacy key tenant prefix");
20
- assertContains('const RESERVED_AGENT_HANDLES = new Set([', "reserved handle set");
21
- assertContains('"parker-smoke-test",', "reserved parker smoke handle");
22
20
  assertContains('function accountTenantIdForUserId(userId)', "account tenant helper");
23
21
  assertContains('function identityForApiKey(key)', "api key identity helper");
24
22
  assertContains('return identityForApiKey(key);', "http auth uses identity helper");
25
23
  assertContains("const agentId = accountTenantIdForUserId(stored.userId);", "registration uses immutable account tenant");
24
+ assertContains("function sanitizeDisplayLabel(raw)", "display label sanitizer");
25
+ assertContains('replace(/[\\u0000-\\u001f\\u007f]/g, "").replace(/\\s+/g, " ").trim().slice(0, 64)', "display label sanitizer preserves label semantics");
26
+ assertContains("const displayLabel = sanitizeDisplayLabel(body?.displayName || body?.username);", "registration treats entered name as display label");
27
+ assertContains("displayLabel,", "registration challenge stores display label");
26
28
  assertContains("await saveApiKey(apiKey, agentId, { handle: credentialLabel });", "registration stores handle separately");
27
- assertContains('json(res, 409, { error: "reserved_handle"', "reserved handle rejected");
28
- assertContains('json(res, 409, { error: "handle_taken"', "duplicate handle rejected");
29
29
  assertContains("p.handle = identity.handle;", "pair stores display handle separately");
30
30
  assertContains("handle: identity.handle,", "relay metadata returns display handle");
31
31
  assertContains("codexDaemons.has(identity.agentId)", "daemon presence uses tenant id");
@@ -37,6 +37,10 @@ assertContains("const key = codexRelayKey(identity.agentId, threadId);", "web ws
37
37
  assertContains("const daemonWs = codexDaemons.get(identity.agentId);", "web sends to tenant daemon");
38
38
  assertNotContains("const agentId = stored.username || (\"passkey-\"", "registration must not use chosen handle as tenant");
39
39
  assertNotContains("const existingKey = Object.entries(API_KEYS).find(([k, v]) => v === agentId);", "oauth must not reuse chosen handle as tenant");
40
+ assertNotContains("function isUsernameTaken", "display labels must not be globally unique usernames");
41
+ assertNotContains("function sanitizeUsername", "display labels must not be modeled as usernames");
42
+ assertNotContains('json(res, 409, { error: "reserved_handle"', "display labels must not be blocked as reserved security handles");
43
+ assertNotContains('json(res, 409, { error: "handle_taken"', "duplicate display labels must be allowed");
40
44
 
41
45
  function legacyTenantIdForApiKey(key) {
42
46
  return "key:" + createHash("sha256").update(key).digest("base64url").slice(0, 32);
@@ -47,11 +51,16 @@ function accountTenantIdForUserId(userId) {
47
51
  }
48
52
 
49
53
  const chosenHandle = "parker-smoke-test";
54
+ const sharedDisplayLabel = "Parker";
50
55
  const accountA = accountTenantIdForUserId("user-a");
51
56
  const accountB = accountTenantIdForUserId("user-b");
57
+ const threadId = "thread-019dfa";
52
58
  if (accountA === accountB) {
53
59
  throw new Error("different user ids collapsed to one account tenant");
54
60
  }
61
+ if (`${sharedDisplayLabel}:${threadId}` === `${accountA}:${threadId}` || `${sharedDisplayLabel}:${threadId}` === `${accountB}:${threadId}`) {
62
+ throw new Error("display label was used as a relay route key");
63
+ }
55
64
 
56
65
  const legacyA = legacyTenantIdForApiKey("ck-a");
57
66
  const legacyB = legacyTenantIdForApiKey("ck-b");
@@ -59,7 +68,6 @@ if (legacyA === legacyB) {
59
68
  throw new Error("legacy API-key tenants collided");
60
69
  }
61
70
 
62
- const threadId = "thread-019dfa";
63
71
  const webKeyA = `${accountA}:${threadId}`;
64
72
  const webKeyB = `${accountB}:${threadId}`;
65
73
  if (webKeyA === webKeyB) {
@@ -110,9 +110,6 @@ const API_KEY_HANDLES = {};
110
110
  const ACCOUNT_TENANT_PREFIX = "acct:";
111
111
  const LEGACY_API_KEY_TENANT_PREFIX = "key:";
112
112
  const OAUTH_API_KEY_TENANT_PREFIX = "oauth:";
113
- const RESERVED_AGENT_HANDLES = new Set([
114
- "parker-smoke-test",
115
- ]);
116
113
 
117
114
  function isInternalTenantId(id) {
118
115
  return typeof id === "string"
@@ -519,21 +516,12 @@ function esc(s) {
519
516
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
520
517
  }
521
518
 
522
- function sanitizeUsername(raw) {
519
+ function sanitizeDisplayLabel(raw) {
523
520
  if (!raw || typeof raw !== "string") return null;
524
- const cleaned = raw.toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 30);
521
+ const cleaned = raw.replace(/[\u0000-\u001f\u007f]/g, "").replace(/\s+/g, " ").trim().slice(0, 64);
525
522
  return cleaned.length > 0 ? cleaned : null;
526
523
  }
527
524
 
528
- async function isUsernameTaken(username) {
529
- if (!username) return false;
530
- if (usePrisma) {
531
- const existing = await prisma.user.findFirst({ where: { name: username } });
532
- return !!existing;
533
- }
534
- return passkeys.some((entry) => entry.handle === username || entry.agentId === username);
535
- }
536
-
537
525
  // ---------- Session cleanup ----------
538
526
 
539
527
  function touchSession(sid) {
@@ -655,22 +643,17 @@ async function handleRegisterOptions(req, res) {
655
643
  let body;
656
644
  try { body = await readBody(req); } catch { body = {}; }
657
645
 
658
- // Accept optional username from request body
659
- const username = sanitizeUsername(body?.username);
660
- if (username && RESERVED_AGENT_HANDLES.has(username)) {
661
- json(res, 409, { error: "reserved_handle", error_description: "This handle is reserved." });
662
- return;
663
- }
664
- if (username && await isUsernameTaken(username)) {
665
- json(res, 409, { error: "handle_taken", error_description: "This handle is already in use." });
666
- return;
667
- }
646
+ // Accept the existing `username` field for wire compatibility, but
647
+ // treat it only as a display label for the passkey prompt. It is not
648
+ // a public username, account handle, or relay tenant boundary.
649
+ // Duplicate display labels are allowed.
650
+ const displayLabel = sanitizeDisplayLabel(body?.displayName || body?.username);
668
651
 
669
652
  const userId = randomBytes(16);
670
653
  const userIdB64 = userId.toString("base64url");
671
654
 
672
- const userName = username || ("user-" + userIdB64.slice(0, 8));
673
- const displayName = username || "Memory Crystal User";
655
+ const userName = displayLabel || ("user-" + userIdB64.slice(0, 8));
656
+ const displayName = displayLabel || "Memory Crystal User";
674
657
 
675
658
  let options;
676
659
  try {
@@ -699,7 +682,7 @@ async function handleRegisterOptions(req, res) {
699
682
  challenge: options.challenge,
700
683
  type: "registration",
701
684
  userId: userIdB64,
702
- username: username,
685
+ displayLabel,
703
686
  expires: Date.now() + 120000,
704
687
  };
705
688
 
@@ -752,8 +735,8 @@ async function handleRegisterVerify(req, res) {
752
735
 
753
736
  const { credential: cred, credentialDeviceType, credentialBackedUp } = verification.registrationInfo;
754
737
 
755
- // Internal tenancy is the immutable WebAuthn user id. The chosen
756
- // username is display metadata only and never owns a relay namespace.
738
+ // Internal tenancy is the immutable WebAuthn user id. The user-entered
739
+ // display label is metadata only and never owns a relay namespace.
757
740
  const agentId = accountTenantIdForUserId(stored.userId);
758
741
  // credentialLabel matches the userName passed to
759
742
  // generateRegistrationOptions in handleRegisterOptions, which is what
@@ -761,7 +744,7 @@ async function handleRegisterVerify(req, res) {
761
744
  // welcome view should display this, not agentId. Auth semantics are
762
745
  // unchanged; only the user-facing label is aligned with the saved
763
746
  // credential.
764
- const credentialLabel = stored.username || ("user-" + stored.userId.slice(0, 8));
747
+ const credentialLabel = stored.displayLabel || ("user-" + stored.userId.slice(0, 8));
765
748
  const apiKey = generateApiKey();
766
749
 
767
750
  const entry = {
@@ -2784,8 +2767,8 @@ async function handleCodexPairComplete(req, res) {
2784
2767
  p.agentId = identity.agentId;
2785
2768
  p.handle = identity.handle;
2786
2769
  // Phase 2.5: register the daemon's E2EE public key against the
2787
- // authenticated handle. Replaces any previous key for this handle
2788
- // (rotate-key implicitly happens here on a re-pair).
2770
+ // authenticated immutable tenant id. The display handle is returned
2771
+ // as metadata only.
2789
2772
  if (p.daemon_public_key) {
2790
2773
  codexDaemonPubkeys.set(identity.agentId, {
2791
2774
  pubkey: p.daemon_public_key,