@primusclaw/personal-agent-runtime 0.2.0 → 0.4.0

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.js CHANGED
@@ -2031,7 +2031,8 @@ function groupContextSettings(read2) {
2031
2031
  return {
2032
2032
  messages,
2033
2033
  maxAgeSeconds: read2.positiveInt("TEAMS_GROUP_CONTEXT_HOURS", DEFAULT_GROUP_CONTEXT.maxAgeSeconds / 3600) * 3600,
2034
- messageChars: read2.positiveInt("TEAMS_GROUP_CONTEXT_CHARS", DEFAULT_GROUP_CONTEXT.messageChars)
2034
+ messageChars: read2.positiveInt("TEAMS_GROUP_CONTEXT_CHARS", DEFAULT_GROUP_CONTEXT.messageChars),
2035
+ granted: read2.raw("TEAMS_GROUP_CONTEXT_GRANTED") === "1"
2035
2036
  };
2036
2037
  }
2037
2038
  function secret(read2, store, name) {
@@ -3360,6 +3361,7 @@ var SqliteIdempotencyRepository = class {
3360
3361
  };
3361
3362
 
3362
3363
  // packages/runtime/src/sqlite/link-repository.ts
3364
+ import { createHash as createHash2 } from "node:crypto";
3363
3365
  import {
3364
3366
  CODE_ALPHABET,
3365
3367
  CODE_LENGTH,
@@ -3378,8 +3380,12 @@ function forgetRepos(db, userId) {
3378
3380
  }
3379
3381
 
3380
3382
  // packages/runtime/src/sqlite/link-repository.ts
3383
+ function tokenHash(credential) {
3384
+ return createHash2("sha256").update(credential).digest("hex");
3385
+ }
3381
3386
  var PERSON_TABLES = [
3382
3387
  "links",
3388
+ "bound_users",
3383
3389
  "sessions",
3384
3390
  "conversation_refs",
3385
3391
  "held_events",
@@ -3478,6 +3484,7 @@ var SqliteLinkRepository = class {
3478
3484
  col.bytes(row, "key_enc"),
3479
3485
  now
3480
3486
  );
3487
+ db.run("DELETE FROM bound_users WHERE aad_id = ?", userId);
3481
3488
  forgetNotifySub(db, userId);
3482
3489
  forgetRepos(db, userId);
3483
3490
  db.run("DELETE FROM sessions WHERE aad_id = ?", userId);
@@ -3510,12 +3517,39 @@ var SqliteLinkRepository = class {
3510
3517
  setClawUserId(userId, clawUserId) {
3511
3518
  this.ctx.db.run("UPDATE links SET claw_user_id = ? WHERE aad_id = ?", clawUserId, userId);
3512
3519
  }
3520
+ boundClawUser(userId, credential) {
3521
+ const row = this.ctx.db.get("SELECT claw_user_id, token_hash FROM bound_users WHERE aad_id = ?", userId);
3522
+ if (row === void 0) return null;
3523
+ return { clawUserId: col.str(row, "claw_user_id"), current: col.str(row, "token_hash") === tokenHash(credential) };
3524
+ }
3525
+ /** Ignored for a linked person (a link's id is read for its key). Another account than before forgets the subscription. */
3526
+ setBoundClawUser(userId, clawUserId, credential) {
3527
+ const { db } = this.ctx;
3528
+ if (db.get("SELECT 1 AS one FROM links WHERE aad_id = ?", userId) !== void 0) return;
3529
+ const before = db.get("SELECT claw_user_id FROM bound_users WHERE aad_id = ?", userId);
3530
+ if (before !== void 0 && col.str(before, "claw_user_id") !== clawUserId) forgetNotifySub(db, userId);
3531
+ db.run(
3532
+ "INSERT OR REPLACE INTO bound_users (aad_id, claw_user_id, token_hash, updated_at) VALUES (?, ?, ?, ?)",
3533
+ userId,
3534
+ clawUserId,
3535
+ tokenHash(credential),
3536
+ this.ctx.now()
3537
+ );
3538
+ }
3539
+ /** Ignored for a linked person: their link keeps its own subscription. */
3540
+ forgetBound(userId) {
3541
+ const { db } = this.ctx;
3542
+ if (db.get("SELECT 1 AS one FROM links WHERE aad_id = ?", userId) !== void 0) return;
3543
+ if (db.run("DELETE FROM bound_users WHERE aad_id = ?", userId) > 0) forgetNotifySub(db, userId);
3544
+ }
3513
3545
  needsClawUserId(userId) {
3514
3546
  const row = this.ctx.db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId);
3515
3547
  return row !== void 0 && !col.bool(row, "claw_user_id");
3516
3548
  }
3549
+ /** The link's (a linked person's id is never taken from anywhere else), else the bound person's. */
3517
3550
  clawUserIdOf(userId) {
3518
- const row = this.ctx.db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId);
3551
+ const { db } = this.ctx;
3552
+ const row = db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId) ?? db.get("SELECT claw_user_id FROM bound_users WHERE aad_id = ?", userId);
3519
3553
  return row && col.bool(row, "claw_user_id") ? col.str(row, "claw_user_id") : null;
3520
3554
  }
3521
3555
  linkedUsers() {
@@ -122,3 +122,5 @@ CREATE TABLE IF NOT EXISTS observe_cards (
122
122
  CREATE INDEX IF NOT EXISTS observe_cards_by_suggestion ON observe_cards (aad_id, run_id, seq);
123
123
  CREATE TABLE IF NOT EXISTS group_context_off (
124
124
  conversation_id TEXT PRIMARY KEY, updated_at REAL NOT NULL);
125
+ CREATE TABLE IF NOT EXISTS bound_users (
126
+ aad_id TEXT PRIMARY KEY, claw_user_id TEXT NOT NULL, token_hash TEXT NOT NULL, updated_at REAL NOT NULL);
@@ -1,4 +1,3 @@
1
- /** LinkRepository on SQLite (tables `links`, `pending_keys`, `link_attempts`). */
2
1
  import { type LinkLimits, type LinkRepository, type RedeemOutcome } from "@primusclaw/personal-agent";
3
2
  import { type StoreContext, type SecurityEventSink } from "./context.js";
4
3
  export declare class SqliteLinkRepository implements LinkRepository {
@@ -19,7 +18,16 @@ export declare class SqliteLinkRepository implements LinkRepository {
19
18
  getKey(userId: string): string | null;
20
19
  unlink(userId: string): void;
21
20
  setClawUserId(userId: string, clawUserId: string): void;
21
+ boundClawUser(userId: string, credential: string): {
22
+ clawUserId: string;
23
+ current: boolean;
24
+ } | null;
25
+ /** Ignored for a linked person (a link's id is read for its key). Another account than before forgets the subscription. */
26
+ setBoundClawUser(userId: string, clawUserId: string, credential: string): void;
27
+ /** Ignored for a linked person: their link keeps its own subscription. */
28
+ forgetBound(userId: string): void;
22
29
  needsClawUserId(userId: string): boolean;
30
+ /** The link's (a linked person's id is never taken from anywhere else), else the bound person's. */
23
31
  clawUserIdOf(userId: string): string | null;
24
32
  linkedUsers(): string[];
25
33
  userForClawUser(clawUserId: string): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primusclaw/personal-agent-runtime",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Node adapters for the personal agent's ports: clock, randomness, logging (and, as the port lands, storage, Claw, metrics, HTTP).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@primusclaw/personal-agent": "0.2.0",
32
+ "@primusclaw/personal-agent": "0.4.0",
33
33
  "fastify": "^5.12.8",
34
34
  "zod": "^4.6.7"
35
35
  }