@securecoms/agent-bridge 0.25.1 → 0.25.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
@@ -7436,10 +7436,10 @@ var HANDOFF = {
7436
7436
  balanced: "If the message needs files, tools, memory lookups, current data, or any work you cannot do instantly, say in one sentence that you are checking and end your reply with the exact token [[DEFER]] so the full agent answers next. For greetings, small talk, and anything you can answer completely yourself, answer completely and do NOT add the token.",
7437
7437
  self: "Answer yourself whenever you reasonably can. Only when the message clearly needs files, tools, or current data you cannot see, say in one sentence that you are checking and end your reply with the exact token [[DEFER]]."
7438
7438
  };
7439
- function compileVoicePrompt(s) {
7439
+ function compileVoicePrompt(s, agentName) {
7440
7440
  const parts = [];
7441
7441
  const persona = s.persona.trim();
7442
- const displayName = s.displayName.trim();
7442
+ const displayName = (agentName ?? "").trim();
7443
7443
  if (displayName && !persona.toLowerCase().includes(displayName.toLowerCase())) {
7444
7444
  parts.push(`You are ${displayName}.`);
7445
7445
  }
@@ -7491,6 +7491,9 @@ var Row = z.object({
7491
7491
  voice_id: z.string().nullable().optional(),
7492
7492
  pace: z.enum(PACES).default("normal"),
7493
7493
  display_name: z.string().max(40).default(""),
7494
+ // Owner decision 2026-10-01 ("one name"). Lenient: a bad value is dropped
7495
+ // (see identityName below), never a reason to reject the whole row.
7496
+ identity_display_name: z.unknown().optional(),
7494
7497
  persona: z.string().max(600).default(""),
7495
7498
  warmth: z.enum(WARMTHS).default("friendly"),
7496
7499
  formality: z.enum(FORMALITIES).default("neutral"),
@@ -7501,8 +7504,14 @@ var Row = z.object({
7501
7504
  avoid_topics: z.array(z.string().max(80)).max(10).default([]),
7502
7505
  handoff: z.enum(HANDOFFS).default("balanced")
7503
7506
  }).passthrough();
7507
+ function identityName(v) {
7508
+ if (typeof v !== "string") return void 0;
7509
+ const name = v.trim();
7510
+ return name && name.length <= 200 ? name : void 0;
7511
+ }
7504
7512
  function parseVoiceSettings(raw) {
7505
7513
  const r = Row.parse(raw);
7514
+ const identityDisplayName = identityName(r.identity_display_name);
7506
7515
  return {
7507
7516
  version: r.version,
7508
7517
  voiceId: r.voice_id ?? null,
@@ -7516,7 +7525,8 @@ function parseVoiceSettings(raw) {
7516
7525
  quirks: r.quirks,
7517
7526
  greetingStyle: r.greeting_style,
7518
7527
  avoidTopics: r.avoid_topics,
7519
- handoff: r.handoff
7528
+ handoff: r.handoff,
7529
+ ...identityDisplayName ? { identityDisplayName } : {}
7520
7530
  };
7521
7531
  }
7522
7532
  function settingsToVoiceConfig(s, proxy) {
@@ -7577,10 +7587,11 @@ function parseVoiceConfig(raw, env) {
7577
7587
  }
7578
7588
  };
7579
7589
  }
7580
- function systemPrompt(p, settings) {
7590
+ function systemPrompt(p, settings, agentName) {
7581
7591
  const core = p.persona.trim().endsWith(CONTINUITY_CLAUSE) ? p.persona.trim().slice(0, -CONTINUITY_CLAUSE.length).trim() : p.persona.trim();
7582
7592
  return compileVoicePrompt(
7583
- settings ?? { ...DEFAULT_VOICE_SETTINGS, displayName: p.displayName ?? "", persona: core, quirks: p.quirks }
7593
+ settings ?? { ...DEFAULT_VOICE_SETTINGS, persona: core, quirks: p.quirks },
7594
+ agentName
7584
7595
  );
7585
7596
  }
7586
7597
 
@@ -28350,7 +28361,7 @@ function clampContent(content, max = MAX_MESSAGE_CHARS) {
28350
28361
  }
28351
28362
  function buildFastMessages(args2) {
28352
28363
  const cap = args2.charCap ?? 4e3;
28353
- const sp = systemPrompt(args2.persona, args2.settings);
28364
+ const sp = systemPrompt(args2.persona, args2.settings, args2.agentName);
28354
28365
  const head = sp.slice(0, sp.length - CONTINUITY_CLAUSE.length).trimEnd();
28355
28366
  const system = `${head} ${args2.guidance.trim()} ${CONTINUITY_CLAUSE}`;
28356
28367
  const recent = args2.memory.recent.map((t) => ({
@@ -28583,6 +28594,13 @@ function assertManagedName(name) {
28583
28594
  if (!name || name.includes("/") || name.includes("\\") || name === "." || name === ".." || name !== basename2(name)) {
28584
28595
  throw new Error(`refusing managed config file name ${JSON.stringify(name)}: not a plain filename`);
28585
28596
  }
28597
+ if (isLocalOnlyName(name)) {
28598
+ throw new Error(`refusing managed config file name ${JSON.stringify(name)}: reserved for a local writer`);
28599
+ }
28600
+ }
28601
+ function isLocalOnlyName(name) {
28602
+ const n = name.toLowerCase().replace(/[. ]+$/, "");
28603
+ return n === "llm.key" || n.startsWith("llm.key.") || n === "local-config.json";
28586
28604
  }
28587
28605
  function atomicWrite(path, body, deps) {
28588
28606
  const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
@@ -29243,6 +29261,12 @@ var BridgeAgent = class _BridgeAgent {
29243
29261
  * `roomDisplayName`. Kept across a later failed refresh, so a transient
29244
29262
  * members-fetch error does not make the agent deaf to its own name; a
29245
29263
  * successful refresh after a rename replaces it.
29264
+ *
29265
+ * 2026-10-01 ("one name"): also the name the voice fast path speaks as
29266
+ * ("You are <name>."). A voice settings fetch that carries
29267
+ * `identity_display_name` writes it too (the backend pings
29268
+ * `voice_settings_changed` on a rename), so the latest observation from
29269
+ * either source wins; both read the same identity column.
29246
29270
  */
29247
29271
  lastOwnDisplayName;
29248
29272
  /**
@@ -29646,6 +29670,7 @@ var BridgeAgent = class _BridgeAgent {
29646
29670
  const res = await this.fetchBounded("voice_settings", "read", url, { headers: { authorization: `Bearer ${this.cfg.apiKey}` } });
29647
29671
  if (!res.ok) throw new Error(`voice-settings ${res.status}`);
29648
29672
  const s = parseVoiceSettings(await res.json());
29673
+ if (s.identityDisplayName) this.lastOwnDisplayName = s.identityDisplayName;
29649
29674
  const proxy = settingsToVoiceConfig(s, { baseUrl: this.cfg.apiBase, apiKey: this.cfg.apiKey });
29650
29675
  if (s.version === 0 && this.fileVoiceConfig) {
29651
29676
  const merged = {
@@ -31543,7 +31568,8 @@ var BridgeAgent = class _BridgeAgent {
31543
31568
  memory,
31544
31569
  speaker,
31545
31570
  text,
31546
- settings: this.voiceSettings ?? void 0
31571
+ settings: this.voiceSettings ?? void 0,
31572
+ agentName: this.voiceAgentName()
31547
31573
  });
31548
31574
  const maxSentences = this.voiceSettings?.brevity === "one" ? 1 : 2;
31549
31575
  const { reply, defer } = splitFastReply(await this.fastLlm.complete(messages), 200, maxSentences);
@@ -32850,6 +32876,16 @@ var BridgeAgent = class _BridgeAgent {
32850
32876
  ownRoomDisplayName() {
32851
32877
  return this.lastOwnDisplayName;
32852
32878
  }
32879
+ /**
32880
+ * 2026-10-01 (owner decision, "one name"): the name the voice prompt uses.
32881
+ * The agent's CURRENT identity display name (roster or voice settings
32882
+ * fetch, whichever saw it last); before either has, the credential's
32883
+ * agent_name (the same identity name as of credential issue). Never the
32884
+ * legacy voice settings `display_name` or the file `voice.persona.display_name`.
32885
+ */
32886
+ voiceAgentName() {
32887
+ return this.lastOwnDisplayName ?? this.cfg.agentName;
32888
+ }
32853
32889
  /** Phase 3 F3: the loop guard's view of the sender — server roster first. */
32854
32890
  loopSenderIsAgent(channelId, senderId, envelopeSaysAgent, relayedHumanTurn) {
32855
32891
  if (relayedHumanTurn) return false;
@@ -567,6 +567,12 @@ export declare class BridgeAgent {
567
567
  * `roomDisplayName`. Kept across a later failed refresh, so a transient
568
568
  * members-fetch error does not make the agent deaf to its own name; a
569
569
  * successful refresh after a rename replaces it.
570
+ *
571
+ * 2026-10-01 ("one name"): also the name the voice fast path speaks as
572
+ * ("You are <name>."). A voice settings fetch that carries
573
+ * `identity_display_name` writes it too (the backend pings
574
+ * `voice_settings_changed` on a rename), so the latest observation from
575
+ * either source wins; both read the same identity column.
570
576
  */
571
577
  private lastOwnDisplayName;
572
578
  /**
@@ -2001,6 +2007,14 @@ export declare class BridgeAgent {
2001
2007
  * the identity id.
2002
2008
  */
2003
2009
  private ownRoomDisplayName;
2010
+ /**
2011
+ * 2026-10-01 (owner decision, "one name"): the name the voice prompt uses.
2012
+ * The agent's CURRENT identity display name (roster or voice settings
2013
+ * fetch, whichever saw it last); before either has, the credential's
2014
+ * agent_name (the same identity name as of credential issue). Never the
2015
+ * legacy voice settings `display_name` or the file `voice.persona.display_name`.
2016
+ */
2017
+ private voiceAgentName;
2004
2018
  /** Phase 3 F3: the loop guard's view of the sender — server roster first. */
2005
2019
  private loopSenderIsAgent;
2006
2020
  private noteAgentTurnSuppressed;
package/dist/index.js CHANGED
@@ -28041,10 +28041,10 @@ var HANDOFF = {
28041
28041
  balanced: "If the message needs files, tools, memory lookups, current data, or any work you cannot do instantly, say in one sentence that you are checking and end your reply with the exact token [[DEFER]] so the full agent answers next. For greetings, small talk, and anything you can answer completely yourself, answer completely and do NOT add the token.",
28042
28042
  self: "Answer yourself whenever you reasonably can. Only when the message clearly needs files, tools, or current data you cannot see, say in one sentence that you are checking and end your reply with the exact token [[DEFER]]."
28043
28043
  };
28044
- function compileVoicePrompt(s) {
28044
+ function compileVoicePrompt(s, agentName) {
28045
28045
  const parts = [];
28046
28046
  const persona = s.persona.trim();
28047
- const displayName = s.displayName.trim();
28047
+ const displayName = (agentName ?? "").trim();
28048
28048
  if (displayName && !persona.toLowerCase().includes(displayName.toLowerCase())) {
28049
28049
  parts.push(`You are ${displayName}.`);
28050
28050
  }
@@ -28096,6 +28096,9 @@ var Row = z.object({
28096
28096
  voice_id: z.string().nullable().optional(),
28097
28097
  pace: z.enum(PACES).default("normal"),
28098
28098
  display_name: z.string().max(40).default(""),
28099
+ // Owner decision 2026-10-01 ("one name"). Lenient: a bad value is dropped
28100
+ // (see identityName below), never a reason to reject the whole row.
28101
+ identity_display_name: z.unknown().optional(),
28099
28102
  persona: z.string().max(600).default(""),
28100
28103
  warmth: z.enum(WARMTHS).default("friendly"),
28101
28104
  formality: z.enum(FORMALITIES).default("neutral"),
@@ -28106,8 +28109,14 @@ var Row = z.object({
28106
28109
  avoid_topics: z.array(z.string().max(80)).max(10).default([]),
28107
28110
  handoff: z.enum(HANDOFFS).default("balanced")
28108
28111
  }).passthrough();
28112
+ function identityName(v) {
28113
+ if (typeof v !== "string") return void 0;
28114
+ const name = v.trim();
28115
+ return name && name.length <= 200 ? name : void 0;
28116
+ }
28109
28117
  function parseVoiceSettings(raw) {
28110
28118
  const r = Row.parse(raw);
28119
+ const identityDisplayName = identityName(r.identity_display_name);
28111
28120
  return {
28112
28121
  version: r.version,
28113
28122
  voiceId: r.voice_id ?? null,
@@ -28121,7 +28130,8 @@ function parseVoiceSettings(raw) {
28121
28130
  quirks: r.quirks,
28122
28131
  greetingStyle: r.greeting_style,
28123
28132
  avoidTopics: r.avoid_topics,
28124
- handoff: r.handoff
28133
+ handoff: r.handoff,
28134
+ ...identityDisplayName ? { identityDisplayName } : {}
28125
28135
  };
28126
28136
  }
28127
28137
  function settingsToVoiceConfig(s, proxy) {
@@ -28182,10 +28192,11 @@ function parseVoiceConfig(raw, env) {
28182
28192
  }
28183
28193
  };
28184
28194
  }
28185
- function systemPrompt(p, settings) {
28195
+ function systemPrompt(p, settings, agentName) {
28186
28196
  const core = p.persona.trim().endsWith(CONTINUITY_CLAUSE) ? p.persona.trim().slice(0, -CONTINUITY_CLAUSE.length).trim() : p.persona.trim();
28187
28197
  return compileVoicePrompt(
28188
- settings ?? { ...DEFAULT_VOICE_SETTINGS, displayName: p.displayName ?? "", persona: core, quirks: p.quirks }
28198
+ settings ?? { ...DEFAULT_VOICE_SETTINGS, persona: core, quirks: p.quirks },
28199
+ agentName
28189
28200
  );
28190
28201
  }
28191
28202
 
@@ -28199,7 +28210,7 @@ function clampContent(content, max = MAX_MESSAGE_CHARS) {
28199
28210
  }
28200
28211
  function buildFastMessages(args2) {
28201
28212
  const cap = args2.charCap ?? 4e3;
28202
- const sp = systemPrompt(args2.persona, args2.settings);
28213
+ const sp = systemPrompt(args2.persona, args2.settings, args2.agentName);
28203
28214
  const head = sp.slice(0, sp.length - CONTINUITY_CLAUSE.length).trimEnd();
28204
28215
  const system = `${head} ${args2.guidance.trim()} ${CONTINUITY_CLAUSE}`;
28205
28216
  const recent = args2.memory.recent.map((t) => ({
@@ -28432,6 +28443,13 @@ function assertManagedName(name) {
28432
28443
  if (!name || name.includes("/") || name.includes("\\") || name === "." || name === ".." || name !== basename2(name)) {
28433
28444
  throw new Error(`refusing managed config file name ${JSON.stringify(name)}: not a plain filename`);
28434
28445
  }
28446
+ if (isLocalOnlyName(name)) {
28447
+ throw new Error(`refusing managed config file name ${JSON.stringify(name)}: reserved for a local writer`);
28448
+ }
28449
+ }
28450
+ function isLocalOnlyName(name) {
28451
+ const n = name.toLowerCase().replace(/[. ]+$/, "");
28452
+ return n === "llm.key" || n.startsWith("llm.key.") || n === "local-config.json";
28435
28453
  }
28436
28454
  function atomicWrite(path, body, deps) {
28437
28455
  const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
@@ -29092,6 +29110,12 @@ var BridgeAgent = class _BridgeAgent {
29092
29110
  * `roomDisplayName`. Kept across a later failed refresh, so a transient
29093
29111
  * members-fetch error does not make the agent deaf to its own name; a
29094
29112
  * successful refresh after a rename replaces it.
29113
+ *
29114
+ * 2026-10-01 ("one name"): also the name the voice fast path speaks as
29115
+ * ("You are <name>."). A voice settings fetch that carries
29116
+ * `identity_display_name` writes it too (the backend pings
29117
+ * `voice_settings_changed` on a rename), so the latest observation from
29118
+ * either source wins; both read the same identity column.
29095
29119
  */
29096
29120
  lastOwnDisplayName;
29097
29121
  /**
@@ -29495,6 +29519,7 @@ var BridgeAgent = class _BridgeAgent {
29495
29519
  const res = await this.fetchBounded("voice_settings", "read", url, { headers: { authorization: `Bearer ${this.cfg.apiKey}` } });
29496
29520
  if (!res.ok) throw new Error(`voice-settings ${res.status}`);
29497
29521
  const s = parseVoiceSettings(await res.json());
29522
+ if (s.identityDisplayName) this.lastOwnDisplayName = s.identityDisplayName;
29498
29523
  const proxy = settingsToVoiceConfig(s, { baseUrl: this.cfg.apiBase, apiKey: this.cfg.apiKey });
29499
29524
  if (s.version === 0 && this.fileVoiceConfig) {
29500
29525
  const merged = {
@@ -31392,7 +31417,8 @@ var BridgeAgent = class _BridgeAgent {
31392
31417
  memory,
31393
31418
  speaker,
31394
31419
  text,
31395
- settings: this.voiceSettings ?? void 0
31420
+ settings: this.voiceSettings ?? void 0,
31421
+ agentName: this.voiceAgentName()
31396
31422
  });
31397
31423
  const maxSentences = this.voiceSettings?.brevity === "one" ? 1 : 2;
31398
31424
  const { reply, defer } = splitFastReply(await this.fastLlm.complete(messages), 200, maxSentences);
@@ -32699,6 +32725,16 @@ var BridgeAgent = class _BridgeAgent {
32699
32725
  ownRoomDisplayName() {
32700
32726
  return this.lastOwnDisplayName;
32701
32727
  }
32728
+ /**
32729
+ * 2026-10-01 (owner decision, "one name"): the name the voice prompt uses.
32730
+ * The agent's CURRENT identity display name (roster or voice settings
32731
+ * fetch, whichever saw it last); before either has, the credential's
32732
+ * agent_name (the same identity name as of credential issue). Never the
32733
+ * legacy voice settings `display_name` or the file `voice.persona.display_name`.
32734
+ */
32735
+ voiceAgentName() {
32736
+ return this.lastOwnDisplayName ?? this.cfg.agentName;
32737
+ }
32702
32738
  /** Phase 3 F3: the loop guard's view of the sender — server roster first. */
32703
32739
  loopSenderIsAgent(channelId, senderId, envelopeSaysAgent, relayedHumanTurn) {
32704
32740
  if (relayedHumanTurn) return false;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const BRIDGE_VERSION = "0.25.1";
1
+ export declare const BRIDGE_VERSION = "0.25.3";
@@ -35,6 +35,9 @@ export declare function buildFastMessages(args: {
35
35
  * from these backend-loaded dials (compileVoicePrompt) instead of the
36
36
  * credential file's persona block. */
37
37
  settings?: VoiceSettings;
38
+ /** The agent's identity display name ("one name", 2026-10-01): the only
39
+ * source of the prompt's "You are <name>." line. */
40
+ agentName?: string;
38
41
  }): ChatMessage[];
39
42
  /** The fast model appends this when the full agent must answer next. */
40
43
  export declare const DEFER_TOKEN = "[[DEFER]]";
@@ -1,5 +1,11 @@
1
1
  import { type VoiceSettings } from "./settings.js";
2
2
  export declare const CONTINUITY_CLAUSE: string;
3
+ /**
4
+ * `displayName` is legacy (the credential file's `voice.persona.display_name`,
5
+ * or the backend's voice settings `display_name`). It is still parsed so old
6
+ * credentials and payloads load, but the prompt never reads it: the agent's
7
+ * name comes from its identity display name (owner decision 2026-10-01, "one name").
8
+ */
3
9
  export interface VoicePersona {
4
10
  displayName?: string;
5
11
  persona: string;
@@ -20,4 +26,4 @@ export interface VoiceConfig {
20
26
  settings?: VoiceSettings;
21
27
  }
22
28
  export declare function parseVoiceConfig(raw: unknown, env: Record<string, string | undefined>): VoiceConfig | undefined;
23
- export declare function systemPrompt(p: VoicePersona, settings?: VoiceSettings): string;
29
+ export declare function systemPrompt(p: VoicePersona, settings?: VoiceSettings, agentName?: string): string;
@@ -1,4 +1,11 @@
1
1
  import type { VoiceSettings } from "./settings.js";
2
- export declare function compileVoicePrompt(s: VoiceSettings): string;
2
+ /**
3
+ * `agentName` is the agent's identity display name (`ConnectIdentity.display_name`,
4
+ * the one name shown in rosters and the Directory). Owner decision 2026-10-01
5
+ * ("one name"): the voice settings `display_name` (`s.displayName`) is a legacy
6
+ * field that is still parsed for compatibility but never read here, so a
7
+ * rename reaches calls too.
8
+ */
9
+ export declare function compileVoicePrompt(s: VoiceSettings, agentName?: string): string;
3
10
  export declare const VOICE_CALL_START_MARKER = "[voice call started]";
4
11
  export declare function greetingTurnFor(s: VoiceSettings, callerName?: string): string;
@@ -10,7 +10,13 @@ export interface VoiceSettings {
10
10
  version: number;
11
11
  voiceId: string | null;
12
12
  pace: (typeof PACES)[number];
13
+ /** Legacy voice-only name (backend `display_name`). Parsed for compatibility
14
+ * with older backends/web clients, never used for the prompt ("one name",
15
+ * 2026-10-01). */
13
16
  displayName: string;
17
+ /** The agent's identity display name at fetch time (backend
18
+ * `identity_display_name`), when the backend sends it. */
19
+ identityDisplayName?: string;
14
20
  persona: string;
15
21
  warmth: (typeof WARMTHS)[number];
16
22
  formality: (typeof FORMALITIES)[number];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securecoms/agent-bridge",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.5.0"