@securecoms/agent-bridge 0.26.2 → 0.26.3

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/bin.js CHANGED
@@ -7597,6 +7597,15 @@ function systemPrompt(p, settings, agentName) {
7597
7597
  }
7598
7598
 
7599
7599
  // src/service-log.ts
7600
+ var ENUM_VALUE = /^[a-z][a-z_]{0,31}$/;
7601
+ var ServiceLogEnum = class _ServiceLogEnum {
7602
+ constructor(value) {
7603
+ this.value = value;
7604
+ }
7605
+ static of(value, allowed) {
7606
+ return new _ServiceLogEnum(allowed.includes(value) && ENUM_VALUE.test(value) ? value : "invalid");
7607
+ }
7608
+ };
7600
7609
  var TOKEN = /^[a-z][a-z0-9_.-]{0,63}$/;
7601
7610
  var CLASS = /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/;
7602
7611
  function errorClass(error) {
@@ -7626,6 +7635,7 @@ function formatServiceLog(level3, event, fields = {}, error) {
7626
7635
  if (!TOKEN.test(key)) continue;
7627
7636
  if (typeof value === "boolean") parts.push(`${key}=${value}`);
7628
7637
  else if (typeof value === "number" && Number.isFinite(value)) parts.push(`${key}=${value}`);
7638
+ else if (value instanceof ServiceLogEnum && ENUM_VALUE.test(value.value)) parts.push(`${key}=${value.value}`);
7629
7639
  }
7630
7640
  if (error !== void 0) parts.push(`error_class=${errorClass(error)}`);
7631
7641
  return parts.join(" ");
@@ -32344,6 +32354,7 @@ var BridgeAgent = class _BridgeAgent {
32344
32354
  if (ownerDirect || ownerRelayed) tool_grants = OWNER_DM_DEFAULT_READ_GRANTS;
32345
32355
  }
32346
32356
  }
32357
+ this.logAuthzDecision(channelId, senderId, relaySpeakerId, audience, tool_grants.length);
32347
32358
  return Object.freeze({
32348
32359
  version: 1,
32349
32360
  audience,
@@ -32351,6 +32362,54 @@ var BridgeAgent = class _BridgeAgent {
32351
32362
  tool_grants
32352
32363
  });
32353
32364
  }
32365
+ /**
32366
+ * One content-free INFO line per authorization decision (every call site of
32367
+ * authorizationFor stages exactly one runtime turn). It says WHY a turn got
32368
+ * the grants it got, without any identifier, name or text: every field is a
32369
+ * boolean, a count, or a closed-set label.
32370
+ *
32371
+ * owner_pin pinned | anomalous | missing (channelOwner cache)
32372
+ * owner_row_is_agent_owner the pinned owner's roster row; `absent` when
32373
+ * there is no pin or the row is not in the roster
32374
+ * sender_is_owner sender identity == pinned owner identity
32375
+ * relay sender's roster row has is_relay
32376
+ * relay_speaker_is_owner `na` unless a relay speaker was passed
32377
+ * kill_switch SECURECOMS_OWNER_DM_DEFAULT_GRANTS as read at
32378
+ * startup: `on` = default grants enabled
32379
+ * meta_fresh / roster_fresh whether the channel meta / roster caches were
32380
+ * fresh (either stale forces audience=unknown)
32381
+ */
32382
+ logAuthzDecision(channelId, senderId, relaySpeakerId, audience, grants) {
32383
+ const meta = this.freshMeta(channelId);
32384
+ const roster = this.freshRoster(channelId);
32385
+ const sender = roster?.get(senderId);
32386
+ const pinned = this.channelOwner.get(channelId);
32387
+ const ownerId = pinned && pinned !== "anomalous" ? pinned.identityId : void 0;
32388
+ const ownerRow = ownerId !== void 0 ? roster?.get(ownerId) : void 0;
32389
+ const ownerPin = pinned === void 0 ? "missing" : pinned === "anomalous" ? "anomalous" : "pinned";
32390
+ const ownerRowIsAgentOwner = ownerRow === void 0 ? "absent" : ownerRow.is_agent_owner ? "true" : "false";
32391
+ const speakerIsOwner = relaySpeakerId === void 0 ? "na" : ownerId !== void 0 && relaySpeakerId === ownerId ? "true" : "false";
32392
+ writeServiceLog("info", "authz.decision", {
32393
+ audience: ServiceLogEnum.of(audience, [
32394
+ "owner_dm",
32395
+ "other_human_dm",
32396
+ "team_room",
32397
+ "partner_room",
32398
+ "agent_to_agent",
32399
+ "unknown"
32400
+ ]),
32401
+ grants,
32402
+ owner_pin: ServiceLogEnum.of(ownerPin, ["pinned", "anomalous", "missing"]),
32403
+ owner_row_is_agent_owner: ServiceLogEnum.of(ownerRowIsAgentOwner, ["true", "false", "absent"]),
32404
+ sender_is_owner: ownerId !== void 0 && senderId === ownerId,
32405
+ relay: sender?.is_relay === true,
32406
+ relay_speaker_is_owner: ServiceLogEnum.of(speakerIsOwner, ["true", "false", "na"]),
32407
+ is_dm: meta?.is_dm === true,
32408
+ kill_switch: ServiceLogEnum.of(this.ownerDmDefaultGrants ? "on" : "off", ["on", "off"]),
32409
+ meta_fresh: meta !== void 0,
32410
+ roster_fresh: roster !== void 0
32411
+ });
32412
+ }
32354
32413
  /**
32355
32414
  * Task 8 (owner-attested Reset Secure Session): STRUCTURAL owner selection —
32356
32415
  * NEVER read the server's `role` field to pick the owner. In a DM the owner
@@ -1967,6 +1967,24 @@ export declare class BridgeAgent {
1967
1967
  * Everything else gets the frozen empty list.
1968
1968
  */
1969
1969
  private authorizationFor;
1970
+ /**
1971
+ * One content-free INFO line per authorization decision (every call site of
1972
+ * authorizationFor stages exactly one runtime turn). It says WHY a turn got
1973
+ * the grants it got, without any identifier, name or text: every field is a
1974
+ * boolean, a count, or a closed-set label.
1975
+ *
1976
+ * owner_pin pinned | anomalous | missing (channelOwner cache)
1977
+ * owner_row_is_agent_owner the pinned owner's roster row; `absent` when
1978
+ * there is no pin or the row is not in the roster
1979
+ * sender_is_owner sender identity == pinned owner identity
1980
+ * relay sender's roster row has is_relay
1981
+ * relay_speaker_is_owner `na` unless a relay speaker was passed
1982
+ * kill_switch SECURECOMS_OWNER_DM_DEFAULT_GRANTS as read at
1983
+ * startup: `on` = default grants enabled
1984
+ * meta_fresh / roster_fresh whether the channel meta / roster caches were
1985
+ * fresh (either stale forces audience=unknown)
1986
+ */
1987
+ private logAuthzDecision;
1970
1988
  /**
1971
1989
  * Task 8 (owner-attested Reset Secure Session): STRUCTURAL owner selection —
1972
1990
  * NEVER read the server's `role` field to pick the owner. In a DM the owner
package/dist/index.js CHANGED
@@ -27308,6 +27308,15 @@ var TokenBucketFloor = class {
27308
27308
  };
27309
27309
 
27310
27310
  // src/service-log.ts
27311
+ var ENUM_VALUE = /^[a-z][a-z_]{0,31}$/;
27312
+ var ServiceLogEnum = class _ServiceLogEnum {
27313
+ constructor(value) {
27314
+ this.value = value;
27315
+ }
27316
+ static of(value, allowed) {
27317
+ return new _ServiceLogEnum(allowed.includes(value) && ENUM_VALUE.test(value) ? value : "invalid");
27318
+ }
27319
+ };
27311
27320
  var TOKEN2 = /^[a-z][a-z0-9_.-]{0,63}$/;
27312
27321
  var CLASS2 = /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/;
27313
27322
  function errorClass2(error) {
@@ -27337,6 +27346,7 @@ function formatServiceLog2(level3, event, fields = {}, error) {
27337
27346
  if (!TOKEN2.test(key)) continue;
27338
27347
  if (typeof value === "boolean") parts.push(`${key}=${value}`);
27339
27348
  else if (typeof value === "number" && Number.isFinite(value)) parts.push(`${key}=${value}`);
27349
+ else if (value instanceof ServiceLogEnum && ENUM_VALUE.test(value.value)) parts.push(`${key}=${value.value}`);
27340
27350
  }
27341
27351
  if (error !== void 0) parts.push(`error_class=${errorClass2(error)}`);
27342
27352
  return parts.join(" ");
@@ -32192,6 +32202,7 @@ var BridgeAgent = class _BridgeAgent {
32192
32202
  if (ownerDirect || ownerRelayed) tool_grants = OWNER_DM_DEFAULT_READ_GRANTS;
32193
32203
  }
32194
32204
  }
32205
+ this.logAuthzDecision(channelId, senderId, relaySpeakerId, audience, tool_grants.length);
32195
32206
  return Object.freeze({
32196
32207
  version: 1,
32197
32208
  audience,
@@ -32199,6 +32210,54 @@ var BridgeAgent = class _BridgeAgent {
32199
32210
  tool_grants
32200
32211
  });
32201
32212
  }
32213
+ /**
32214
+ * One content-free INFO line per authorization decision (every call site of
32215
+ * authorizationFor stages exactly one runtime turn). It says WHY a turn got
32216
+ * the grants it got, without any identifier, name or text: every field is a
32217
+ * boolean, a count, or a closed-set label.
32218
+ *
32219
+ * owner_pin pinned | anomalous | missing (channelOwner cache)
32220
+ * owner_row_is_agent_owner the pinned owner's roster row; `absent` when
32221
+ * there is no pin or the row is not in the roster
32222
+ * sender_is_owner sender identity == pinned owner identity
32223
+ * relay sender's roster row has is_relay
32224
+ * relay_speaker_is_owner `na` unless a relay speaker was passed
32225
+ * kill_switch SECURECOMS_OWNER_DM_DEFAULT_GRANTS as read at
32226
+ * startup: `on` = default grants enabled
32227
+ * meta_fresh / roster_fresh whether the channel meta / roster caches were
32228
+ * fresh (either stale forces audience=unknown)
32229
+ */
32230
+ logAuthzDecision(channelId, senderId, relaySpeakerId, audience, grants) {
32231
+ const meta = this.freshMeta(channelId);
32232
+ const roster = this.freshRoster(channelId);
32233
+ const sender = roster?.get(senderId);
32234
+ const pinned = this.channelOwner.get(channelId);
32235
+ const ownerId = pinned && pinned !== "anomalous" ? pinned.identityId : void 0;
32236
+ const ownerRow = ownerId !== void 0 ? roster?.get(ownerId) : void 0;
32237
+ const ownerPin = pinned === void 0 ? "missing" : pinned === "anomalous" ? "anomalous" : "pinned";
32238
+ const ownerRowIsAgentOwner = ownerRow === void 0 ? "absent" : ownerRow.is_agent_owner ? "true" : "false";
32239
+ const speakerIsOwner = relaySpeakerId === void 0 ? "na" : ownerId !== void 0 && relaySpeakerId === ownerId ? "true" : "false";
32240
+ writeServiceLog2("info", "authz.decision", {
32241
+ audience: ServiceLogEnum.of(audience, [
32242
+ "owner_dm",
32243
+ "other_human_dm",
32244
+ "team_room",
32245
+ "partner_room",
32246
+ "agent_to_agent",
32247
+ "unknown"
32248
+ ]),
32249
+ grants,
32250
+ owner_pin: ServiceLogEnum.of(ownerPin, ["pinned", "anomalous", "missing"]),
32251
+ owner_row_is_agent_owner: ServiceLogEnum.of(ownerRowIsAgentOwner, ["true", "false", "absent"]),
32252
+ sender_is_owner: ownerId !== void 0 && senderId === ownerId,
32253
+ relay: sender?.is_relay === true,
32254
+ relay_speaker_is_owner: ServiceLogEnum.of(speakerIsOwner, ["true", "false", "na"]),
32255
+ is_dm: meta?.is_dm === true,
32256
+ kill_switch: ServiceLogEnum.of(this.ownerDmDefaultGrants ? "on" : "off", ["on", "off"]),
32257
+ meta_fresh: meta !== void 0,
32258
+ roster_fresh: roster !== void 0
32259
+ });
32260
+ }
32202
32261
  /**
32203
32262
  * Task 8 (owner-attested Reset Secure Session): STRUCTURAL owner selection —
32204
32263
  * NEVER read the server's `role` field to pick the owner. In a DM the owner
@@ -1,5 +1,18 @@
1
1
  export type ServiceLogLevel = "debug" | "info" | "warn" | "error";
2
- export type ServiceLogFields = Readonly<Record<string, number | boolean>>;
2
+ /**
3
+ * A closed-set label for a service-log field. The only way to build one is
4
+ * `ServiceLogEnum.of(value, allowed)`: a value outside `allowed`, or one that
5
+ * is not a short lowercase word, renders as `invalid`. Raw strings are still
6
+ * dropped by `formatServiceLog`, so an identifier, name or message text can
7
+ * never reach the log through a field. The privacy test requires `allowed` to
8
+ * be an inline array of string literals at every call site.
9
+ */
10
+ export declare class ServiceLogEnum {
11
+ readonly value: string;
12
+ private constructor();
13
+ static of<T extends string>(value: T, allowed: readonly T[]): ServiceLogEnum;
14
+ }
15
+ export type ServiceLogFields = Readonly<Record<string, number | boolean | ServiceLogEnum>>;
3
16
  export type ServiceLog = (level: ServiceLogLevel, event: string, fields?: ServiceLogFields, error?: unknown) => void;
4
17
  /** Reduce a thrown value without reading its message, stack, or string form. */
5
18
  export declare function errorClass(error: unknown): string;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const BRIDGE_VERSION = "0.26.2";
1
+ export declare const BRIDGE_VERSION = "0.26.3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securecoms/agent-bridge",
3
- "version": "0.26.2",
3
+ "version": "0.26.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.5.0"