paperclip-plugin-telegram 0.7.1 → 0.8.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.
@@ -5,11 +5,43 @@ const FIELDS = [
5
5
  { key: "transcriptionApiKeyRef", required: false },
6
6
  ];
7
7
  export function isValidSecretRef(value) {
8
- return typeof value === "string" && UUID_RE.test(value.trim());
8
+ if (typeof value === "string")
9
+ return UUID_RE.test(value.trim());
10
+ if (typeof value === "object" && value !== null) {
11
+ const record = value;
12
+ return (record.type === "secret_ref" &&
13
+ typeof record.secretId === "string" &&
14
+ UUID_RE.test(record.secretId.trim()));
15
+ }
16
+ return false;
17
+ }
18
+ /**
19
+ * Coerce a secret ref into the shape THIS host accepts before calling
20
+ * ctx.secrets.resolve().
21
+ *
22
+ * Refs reach us in both shapes: config validated by a current host holds
23
+ * `{ type: "secret_ref", secretId }`, while refs persisted earlier — notably
24
+ * the board-access state written by the Board Access panel — hold a bare UUID
25
+ * string. Hosts that require the object form reject the bare string with
26
+ * "Invalid secret reference for plugin: <uuid>. Use { type: "secret_ref" ... }",
27
+ * which surfaces to the user as an unexplained 403 from whatever the token was
28
+ * needed for.
29
+ *
30
+ * Passing the object through unchanged keeps older hosts working, since they
31
+ * accept it as an opaque ref.
32
+ */
33
+ export function normalizeSecretRef(value) {
34
+ if (typeof value === "string") {
35
+ const trimmed = value.trim();
36
+ return UUID_RE.test(trimmed) ? { type: "secret_ref", secretId: trimmed } : null;
37
+ }
38
+ return isValidSecretRef(value) ? value : null;
9
39
  }
10
40
  function describeBadValue(value) {
11
41
  if (value === undefined || value === null)
12
42
  return "<empty>";
43
+ if (typeof value === "object")
44
+ return "<object>";
13
45
  if (typeof value !== "string")
14
46
  return `<${typeof value}>`;
15
47
  const trimmed = value.trim();
@@ -1 +1 @@
1
- {"version":3,"file":"secret-ref-validation.js","sourceRoot":"","sources":["../src/secret-ref-validation.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF,MAAM,MAAM,GAAG;IACb,EAAE,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC9C,EAAE,GAAG,EAAE,2BAA2B,EAAE,QAAQ,EAAE,KAAK,EAAE;IACrD,EAAE,GAAG,EAAE,wBAAwB,EAAE,QAAQ,EAAE,KAAK,EAAE;CAC1C,CAAC;AAEX,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,OAAO,KAAK,GAAG,CAAC;IAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,gBAAgB,CAAC;IAClD,iEAAiE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,OAAO,IAAI,MAAM,GAAG,CAAC;AACvB,CAAC;AAED,SAAS,UAAU,CAAC,GAAW,EAAE,KAAc;IAC7C,OAAO;QACL,GAAG,GAAG,yCAAyC;QAC/C,mEAAmE;QACnE,OAAO,gBAAgB,CAAC,KAAK,CAAC,GAAG;QACjC,uGAAuG;QACvG,sEAAsE;KACvE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAuB;IAC7D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,MAAM,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,SAAS,GACb,KAAK,KAAK,SAAS;YACnB,KAAK,KAAK,IAAI;YACd,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;QAE3D,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,eAAe,CAAC,CAAC;YACjD,SAAS;QACX,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"secret-ref-validation.js","sourceRoot":"","sources":["../src/secret-ref-validation.ts"],"names":[],"mappings":"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"}
package/dist/ui/index.js CHANGED
@@ -345,14 +345,23 @@ async function fetchBoardAccessIdentity(boardApiToken) {
345
345
  });
346
346
  return getIdentityLabel(identity);
347
347
  }
348
- async function fetchPluginConfig() {
349
- const record = await fetchHostJson(`/api/plugins/${encodeURIComponent(TELEGRAM_PLUGIN_ID)}/config`);
348
+ function pluginConfigPath(companyId) {
349
+ const base = `/api/plugins/${encodeURIComponent(TELEGRAM_PLUGIN_ID)}/config`;
350
+ const scopedCompanyId = companyId?.trim();
351
+ return scopedCompanyId ? `${base}?companyId=${encodeURIComponent(scopedCompanyId)}` : base;
352
+ }
353
+ async function fetchPluginConfig(companyId) {
354
+ const record = await fetchHostJson(pluginConfigPath(companyId));
350
355
  return record?.configJson && typeof record.configJson === "object" ? record.configJson : {};
351
356
  }
352
- async function savePluginConfig(configJson) {
353
- await fetchHostJson(`/api/plugins/${encodeURIComponent(TELEGRAM_PLUGIN_ID)}/config`, {
357
+ async function fetchCompanySecrets(companyId) {
358
+ return fetchHostJson(`/api/companies/${encodeURIComponent(companyId)}/secrets`);
359
+ }
360
+ async function savePluginConfig(configJson, companyId) {
361
+ const scopedCompanyId = companyId?.trim();
362
+ await fetchHostJson(pluginConfigPath(), {
354
363
  method: "POST",
355
- body: JSON.stringify({ configJson }),
364
+ body: JSON.stringify(scopedCompanyId ? { configJson, companyId: scopedCompanyId } : { configJson }),
356
365
  });
357
366
  }
358
367
  async function resolveOrCreateCompanySecret(companyId, name, value) {
@@ -426,7 +435,7 @@ export function TelegramSettingsPage({ context }) {
426
435
  setRoutingLoading(true);
427
436
  setRoutingMessage(null);
428
437
  try {
429
- const config = await fetchPluginConfig();
438
+ const config = await fetchPluginConfig(companyId);
430
439
  if (cancelled)
431
440
  return;
432
441
  const nextRoutingConfig = extractRoutingConfig(config);
@@ -452,14 +461,14 @@ export function TelegramSettingsPage({ context }) {
452
461
  return () => {
453
462
  cancelled = true;
454
463
  };
455
- }, []);
464
+ }, [companyId]);
456
465
  useEffect(() => {
457
466
  let cancelled = false;
458
467
  async function loadProactiveConfig() {
459
468
  setProactiveLoading(true);
460
469
  setProactiveMessage(null);
461
470
  try {
462
- const config = await fetchPluginConfig();
471
+ const config = await fetchPluginConfig(companyId);
463
472
  if (cancelled)
464
473
  return;
465
474
  const nextProactiveConfig = extractProactiveConfig(config);
@@ -485,14 +494,14 @@ export function TelegramSettingsPage({ context }) {
485
494
  return () => {
486
495
  cancelled = true;
487
496
  };
488
- }, []);
497
+ }, [companyId]);
489
498
  useEffect(() => {
490
499
  let cancelled = false;
491
500
  async function loadEscalationConfig() {
492
501
  setEscalationLoading(true);
493
502
  setEscalationMessage(null);
494
503
  try {
495
- const config = await fetchPluginConfig();
504
+ const config = await fetchPluginConfig(companyId);
496
505
  if (cancelled)
497
506
  return;
498
507
  const nextEscalationConfig = extractEscalationConfig(config);
@@ -518,14 +527,14 @@ export function TelegramSettingsPage({ context }) {
518
527
  return () => {
519
528
  cancelled = true;
520
529
  };
521
- }, []);
530
+ }, [companyId]);
522
531
  useEffect(() => {
523
532
  let cancelled = false;
524
533
  async function loadMediaConfig() {
525
534
  setMediaLoading(true);
526
535
  setMediaMessage(null);
527
536
  try {
528
- const config = await fetchPluginConfig();
537
+ const config = await fetchPluginConfig(companyId);
529
538
  if (cancelled)
530
539
  return;
531
540
  const nextMediaConfig = extractMediaConfig(config);
@@ -551,14 +560,14 @@ export function TelegramSettingsPage({ context }) {
551
560
  return () => {
552
561
  cancelled = true;
553
562
  };
554
- }, []);
563
+ }, [companyId]);
555
564
  useEffect(() => {
556
565
  let cancelled = false;
557
566
  async function loadAccessConfig() {
558
567
  setAccessLoading(true);
559
568
  setAccessMessage(null);
560
569
  try {
561
- const config = await fetchPluginConfig();
570
+ const config = await fetchPluginConfig(companyId);
562
571
  if (cancelled)
563
572
  return;
564
573
  const nextAccessConfig = extractAccessConfig(config);
@@ -584,14 +593,14 @@ export function TelegramSettingsPage({ context }) {
584
593
  return () => {
585
594
  cancelled = true;
586
595
  };
587
- }, []);
596
+ }, [companyId]);
588
597
  useEffect(() => {
589
598
  let cancelled = false;
590
599
  async function loadConnectionConfig() {
591
600
  setConnectionLoading(true);
592
601
  setConnectionMessage(null);
593
602
  try {
594
- const config = await fetchPluginConfig();
603
+ const config = await fetchPluginConfig(companyId);
595
604
  if (cancelled)
596
605
  return;
597
606
  const nextConnectionConfig = extractConnectionConfig(config);
@@ -617,14 +626,14 @@ export function TelegramSettingsPage({ context }) {
617
626
  return () => {
618
627
  cancelled = true;
619
628
  };
620
- }, []);
629
+ }, [companyId]);
621
630
  useEffect(() => {
622
631
  let cancelled = false;
623
632
  async function loadBoardConfig() {
624
633
  setBoardConfigLoading(true);
625
634
  setBoardConfigMessage(null);
626
635
  try {
627
- const config = await fetchPluginConfig();
636
+ const config = await fetchPluginConfig(companyId);
628
637
  if (cancelled)
629
638
  return;
630
639
  const nextBoardConfig = extractBoardConfig(config);
@@ -650,7 +659,7 @@ export function TelegramSettingsPage({ context }) {
650
659
  return () => {
651
660
  cancelled = true;
652
661
  };
653
- }, []);
662
+ }, [companyId]);
654
663
  function updateRoutingField(key, value) {
655
664
  setRoutingConfig((current) => ({ ...current, [key]: value }));
656
665
  setRoutingMessage(null);
@@ -683,9 +692,9 @@ export function TelegramSettingsPage({ context }) {
683
692
  setBoardConfigSaving(true);
684
693
  setBoardConfigMessage(null);
685
694
  try {
686
- const currentConfig = await fetchPluginConfig();
695
+ const currentConfig = await fetchPluginConfig(companyId);
687
696
  const nextConfig = { ...currentConfig, ...boardConfig };
688
- await savePluginConfig(nextConfig);
697
+ await savePluginConfig(nextConfig, companyId);
689
698
  setBoardSnapshot(boardConfig);
690
699
  setBoardConfigMessage({
691
700
  tone: "success",
@@ -708,9 +717,9 @@ export function TelegramSettingsPage({ context }) {
708
717
  setAccessSaving(true);
709
718
  setAccessMessage(null);
710
719
  try {
711
- const currentConfig = await fetchPluginConfig();
720
+ const currentConfig = await fetchPluginConfig(companyId);
712
721
  const nextConfig = { ...currentConfig, ...accessConfig };
713
- await savePluginConfig(nextConfig);
722
+ await savePluginConfig(nextConfig, companyId);
714
723
  setAccessSnapshot(accessConfig);
715
724
  setAccessMessage({
716
725
  tone: "success",
@@ -733,9 +742,9 @@ export function TelegramSettingsPage({ context }) {
733
742
  setRoutingSaving(true);
734
743
  setRoutingMessage(null);
735
744
  try {
736
- const currentConfig = await fetchPluginConfig();
745
+ const currentConfig = await fetchPluginConfig(companyId);
737
746
  const nextConfig = { ...currentConfig, ...routingConfig };
738
- await savePluginConfig(nextConfig);
747
+ await savePluginConfig(nextConfig, companyId);
739
748
  setRoutingSnapshot(routingConfig);
740
749
  setRoutingMessage({
741
750
  tone: "success",
@@ -758,9 +767,9 @@ export function TelegramSettingsPage({ context }) {
758
767
  setConnectionSaving(true);
759
768
  setConnectionMessage(null);
760
769
  try {
761
- const currentConfig = await fetchPluginConfig();
770
+ const currentConfig = await fetchPluginConfig(companyId);
762
771
  const nextConfig = { ...currentConfig, ...connectionConfig };
763
- await savePluginConfig(nextConfig);
772
+ await savePluginConfig(nextConfig, companyId);
764
773
  setConnectionSnapshot(connectionConfig);
765
774
  setConnectionMessage({
766
775
  tone: "success",
@@ -783,9 +792,9 @@ export function TelegramSettingsPage({ context }) {
783
792
  setMediaSaving(true);
784
793
  setMediaMessage(null);
785
794
  try {
786
- const currentConfig = await fetchPluginConfig();
795
+ const currentConfig = await fetchPluginConfig(companyId);
787
796
  const nextConfig = { ...currentConfig, ...mediaConfig };
788
- await savePluginConfig(nextConfig);
797
+ await savePluginConfig(nextConfig, companyId);
789
798
  setMediaSnapshot(mediaConfig);
790
799
  setMediaMessage({
791
800
  tone: "success",
@@ -808,9 +817,9 @@ export function TelegramSettingsPage({ context }) {
808
817
  setEscalationSaving(true);
809
818
  setEscalationMessage(null);
810
819
  try {
811
- const currentConfig = await fetchPluginConfig();
820
+ const currentConfig = await fetchPluginConfig(companyId);
812
821
  const nextConfig = { ...currentConfig, ...escalationConfig };
813
- await savePluginConfig(nextConfig);
822
+ await savePluginConfig(nextConfig, companyId);
814
823
  setEscalationSnapshot(escalationConfig);
815
824
  setEscalationMessage({
816
825
  tone: "success",
@@ -833,9 +842,9 @@ export function TelegramSettingsPage({ context }) {
833
842
  setProactiveSaving(true);
834
843
  setProactiveMessage(null);
835
844
  try {
836
- const currentConfig = await fetchPluginConfig();
845
+ const currentConfig = await fetchPluginConfig(companyId);
837
846
  const nextConfig = { ...currentConfig, ...proactiveConfig };
838
- await savePluginConfig(nextConfig);
847
+ await savePluginConfig(nextConfig, companyId);
839
848
  setProactiveSnapshot(proactiveConfig);
840
849
  setProactiveMessage({
841
850
  tone: "success",
@@ -929,7 +938,7 @@ export function TelegramSettingsPage({ context }) {
929
938
  display: "grid",
930
939
  gap: 18,
931
940
  padding: 18,
932
- }, children: [_jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsx("h2", { style: { fontSize: 18, fontWeight: 700, lineHeight: "28px", margin: 0 }, children: "Connection & URLs" }), _jsx("p", { style: { color: "#6b7280", margin: 0 }, children: "Core connection values used by the Telegram worker. Save the bot token as a Paperclip secret and paste its secret UUID here." })] }), _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsx(TextField, { disabled: connectionLoading || connectionSaving, label: "Telegram bot token secret ref", onChange: (value) => updateConnectionField("telegramBotTokenRef", value), placeholder: "Secret UUID from Paperclip settings", value: connectionConfig.telegramBotTokenRef, children: "Secret UUID for your Telegram bot token from @BotFather. The plugin resolves this secret before polling Telegram." }), _jsx(TextField, { disabled: connectionLoading || connectionSaving, label: "Paperclip API URL", onChange: (value) => updateConnectionField("paperclipBaseUrl", value), placeholder: "http://localhost:3100", value: connectionConfig.paperclipBaseUrl, children: "Internal Paperclip API URL used by the plugin for actions such as approvals and comments. Keep localhost for same-server deployments." }), _jsx(TextField, { disabled: connectionLoading || connectionSaving, label: "Paperclip public URL", onChange: (value) => updateConnectionField("paperclipPublicUrl", value), placeholder: "https://paperclip.example.com", value: connectionConfig.paperclipPublicUrl, children: "Public URL used in Telegram links. Leave empty to fall back to the API URL." })] }), connectionMessage ? _jsx(NoticeBlock, { notice: connectionMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: connectionLoading || connectionSaving, onClick: () => {
941
+ }, children: [_jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsx("h2", { style: { fontSize: 18, fontWeight: 700, lineHeight: "28px", margin: 0 }, children: "Connection & URLs" }), _jsx("p", { style: { color: "#6b7280", margin: 0 }, children: "Core connection values used by the Telegram worker. Select company secrets for sensitive values; secret values are never shown here." })] }), _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsx(SecretRefField, { companyId: companyId, disabled: connectionLoading || connectionSaving, label: "Telegram bot token secret ref", onChange: (value) => updateConnectionField("telegramBotTokenRef", value), placeholder: "Secret UUID fallback", value: connectionConfig.telegramBotTokenRef, children: "Select the company secret that stores your Telegram bot token from @BotFather. The plugin resolves this secret before polling Telegram." }), _jsx(TextField, { disabled: connectionLoading || connectionSaving, label: "Paperclip API URL", onChange: (value) => updateConnectionField("paperclipBaseUrl", value), placeholder: "http://localhost:3100", value: connectionConfig.paperclipBaseUrl, children: "Internal Paperclip API URL used by the plugin for actions such as approvals and comments. Keep localhost for same-server deployments." }), _jsx(TextField, { disabled: connectionLoading || connectionSaving, label: "Paperclip public URL", onChange: (value) => updateConnectionField("paperclipPublicUrl", value), placeholder: "https://paperclip.example.com", value: connectionConfig.paperclipPublicUrl, children: "Public URL used in Telegram links. Leave empty to fall back to the API URL." })] }), connectionMessage ? _jsx(NoticeBlock, { notice: connectionMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: connectionLoading || connectionSaving, onClick: () => {
933
942
  setConnectionConfig(connectionSnapshot);
934
943
  setConnectionMessage(null);
935
944
  }, style: {
@@ -993,7 +1002,7 @@ export function TelegramSettingsPage({ context }) {
993
1002
  fontWeight: 700,
994
1003
  minWidth: 190,
995
1004
  padding: "10px 14px",
996
- }, type: "button", children: connecting ? "Waiting for approval..." : configured ? "Reconnect board access" : "Connect board access" })] }), _jsxs("div", { style: { borderTop: "1px solid #e5e7eb", display: "grid", gap: 12, paddingTop: 14 }, children: [_jsx(TextField, { disabled: boardConfigLoading || boardConfigSaving, label: "Board API token secret ref fallback", onChange: (value) => updateBoardField("paperclipBoardApiTokenRef", value), placeholder: "Optional Paperclip secret UUID", value: boardConfig.paperclipBoardApiTokenRef, children: "Optional manual fallback for approval buttons and /approve. The Board Access Connection above is preferred because it creates and tracks the company-scoped secret for you." }), boardConfigMessage ? _jsx(NoticeBlock, { notice: boardConfigMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: boardConfigLoading || boardConfigSaving, onClick: () => {
1005
+ }, type: "button", children: connecting ? "Waiting for approval..." : configured ? "Reconnect board access" : "Connect board access" })] }), _jsxs("div", { style: { borderTop: "1px solid #e5e7eb", display: "grid", gap: 12, paddingTop: 14 }, children: [_jsx(SecretRefField, { companyId: companyId, disabled: boardConfigLoading || boardConfigSaving, label: "Board API token secret ref fallback", onChange: (value) => updateBoardField("paperclipBoardApiTokenRef", value), placeholder: "Optional secret UUID fallback", value: boardConfig.paperclipBoardApiTokenRef, children: "Optional manual fallback for approval buttons and /approve. The Board Access Connection above is preferred because it creates and tracks the company-scoped secret for you." }), boardConfigMessage ? _jsx(NoticeBlock, { notice: boardConfigMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: boardConfigLoading || boardConfigSaving, onClick: () => {
997
1006
  setBoardConfig(boardSnapshot);
998
1007
  setBoardConfigMessage(null);
999
1008
  }, style: {
@@ -1132,7 +1141,7 @@ export function TelegramSettingsPage({ context }) {
1132
1141
  display: "grid",
1133
1142
  gap: 18,
1134
1143
  padding: 18,
1135
- }, children: [_jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsx("h2", { style: { fontSize: 18, fontWeight: 700, lineHeight: "28px", margin: 0 }, children: "Media Intake / Brief Agent" }), _jsx("p", { style: { color: "#6b7280", margin: 0 }, children: "Routes Telegram voice, audio, documents, and photos either to a Brief Agent intake flow or to active agent sessions inside forum topics." })] }), _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsx(TextField, { disabled: mediaLoading || mediaSaving, label: "Transcription API key secret ref", onChange: (value) => updateMediaField("transcriptionApiKeyRef", value), placeholder: "OpenAI API key secret UUID", value: mediaConfig.transcriptionApiKeyRef, children: "Secret UUID for the OpenAI API key used to transcribe voice and audio before routing media to the Brief Agent or an active topic agent session." }), _jsx(TextField, { disabled: mediaLoading || mediaSaving, label: "Brief Agent ID", onChange: (value) => updateMediaField("briefAgentId", value), placeholder: "Paperclip agent ID", value: mediaConfig.briefAgentId, children: "Agent ID that processes media intake briefs. Leave empty to disable the dedicated Brief Agent intake flow." }), _jsx(ArrayField, { disabled: mediaLoading || mediaSaving, emptyValueLabel: "No intake chat IDs configured", label: "Brief Agent intake chat IDs", newItemLabel: "Add intake chat ID", onChange: (value) => updateMediaField("briefAgentChatIds", value), placeholder: "-1003800613668", value: mediaConfig.briefAgentChatIds, children: "Telegram chat IDs where media is routed to the Brief Agent. Media in other chats goes to active agent sessions when a matching forum topic session exists." })] }), mediaMessage ? _jsx(NoticeBlock, { notice: mediaMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: mediaLoading || mediaSaving, onClick: () => {
1144
+ }, children: [_jsxs("div", { style: { display: "grid", gap: 4 }, children: [_jsx("h2", { style: { fontSize: 18, fontWeight: 700, lineHeight: "28px", margin: 0 }, children: "Media Intake / Brief Agent" }), _jsx("p", { style: { color: "#6b7280", margin: 0 }, children: "Routes Telegram voice, audio, documents, and photos either to a Brief Agent intake flow or to active agent sessions inside forum topics." })] }), _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsx(SecretRefField, { companyId: companyId, disabled: mediaLoading || mediaSaving, label: "OpenAI transcription secret", onChange: (value) => updateMediaField("transcriptionApiKeyRef", value), placeholder: "OpenAI API key secret UUID fallback", value: mediaConfig.transcriptionApiKeyRef, children: "Select the company secret used for audio transcription. Secret values are never shown to the plugin UI." }), _jsx(TextField, { disabled: mediaLoading || mediaSaving, label: "Brief Agent ID", onChange: (value) => updateMediaField("briefAgentId", value), placeholder: "Paperclip agent ID", value: mediaConfig.briefAgentId, children: "Agent ID that processes media intake briefs. Leave empty to disable the dedicated Brief Agent intake flow." }), _jsx(ArrayField, { disabled: mediaLoading || mediaSaving, emptyValueLabel: "No intake chat IDs configured", label: "Brief Agent intake chat IDs", newItemLabel: "Add intake chat ID", onChange: (value) => updateMediaField("briefAgentChatIds", value), placeholder: "-1003800613668", value: mediaConfig.briefAgentChatIds, children: "Telegram chat IDs where media is routed to the Brief Agent. Media in other chats goes to active agent sessions when a matching forum topic session exists." })] }), mediaMessage ? _jsx(NoticeBlock, { notice: mediaMessage }) : null, _jsxs("div", { style: { display: "flex", gap: 10, justifyContent: "flex-end" }, children: [_jsx("button", { disabled: mediaLoading || mediaSaving, onClick: () => {
1136
1145
  setMediaConfig(mediaSnapshot);
1137
1146
  setMediaMessage(null);
1138
1147
  }, style: {
@@ -1230,6 +1239,51 @@ function NoticeBlock({ notice }) {
1230
1239
  padding: 12,
1231
1240
  }, children: [_jsx("strong", { children: notice.title }), notice.text ? _jsx("p", { style: { margin: "6px 0 0" }, children: notice.text }) : null] }));
1232
1241
  }
1242
+ function SecretRefField({ label, value, placeholder, disabled, companyId, children, onChange, }) {
1243
+ const [secrets, setSecrets] = useState([]);
1244
+ const [loading, setLoading] = useState(false);
1245
+ const [error, setError] = useState(null);
1246
+ useEffect(() => {
1247
+ let cancelled = false;
1248
+ async function loadSecrets() {
1249
+ if (!companyId) {
1250
+ setSecrets([]);
1251
+ return;
1252
+ }
1253
+ setLoading(true);
1254
+ setError(null);
1255
+ try {
1256
+ const nextSecrets = await fetchCompanySecrets(companyId);
1257
+ if (!cancelled) {
1258
+ setSecrets(nextSecrets.filter((secret) => secret.status !== "deleted"));
1259
+ }
1260
+ }
1261
+ catch (err) {
1262
+ if (!cancelled) {
1263
+ setError(getErrorMessage(err));
1264
+ }
1265
+ }
1266
+ finally {
1267
+ if (!cancelled) {
1268
+ setLoading(false);
1269
+ }
1270
+ }
1271
+ }
1272
+ void loadSecrets();
1273
+ return () => {
1274
+ cancelled = true;
1275
+ };
1276
+ }, [companyId]);
1277
+ const selectedValue = secrets.some((secret) => secret.id === value) ? value : "";
1278
+ const fieldDisabled = disabled || loading || !companyId;
1279
+ return (_jsxs("label", { style: { display: "grid", gap: 5 }, children: [_jsx("span", { style: { color: "#4b5563", fontSize: 12, fontWeight: 700 }, children: label }), _jsxs("select", { disabled: fieldDisabled, onChange: (event) => onChange(event.currentTarget.value), style: standardInputStyle, value: selectedValue, children: [_jsx("option", { value: "", children: !companyId
1280
+ ? "Open inside a company to select a secret"
1281
+ : loading
1282
+ ? "Loading company secrets..."
1283
+ : value && !selectedValue
1284
+ ? "Manual UUID selected"
1285
+ : "Select a company secret" }), secrets.map((secret) => (_jsxs("option", { value: secret.id, children: [secret.name || secret.key || secret.id, secret.key && secret.key !== secret.name ? ` (${secret.key})` : "", secret.latestVersion ? ` · v${secret.latestVersion}` : ""] }, secret.id)))] }), _jsx("input", { disabled: disabled, onChange: (event) => onChange(event.currentTarget.value), placeholder: placeholder, style: standardInputStyle, type: "text", value: value }), _jsx("span", { style: { color: "#6b7280", fontSize: 12 }, children: children }), error ? _jsxs("span", { style: { color: "#b91c1c", fontSize: 12 }, children: ["Could not load company secrets: ", error] }) : null] }));
1286
+ }
1233
1287
  function TextField({ label, value, placeholder, disabled, children, onChange, }) {
1234
1288
  return (_jsxs("label", { style: { display: "grid", gap: 5 }, children: [_jsx("span", { style: { color: "#4b5563", fontSize: 12, fontWeight: 700 }, children: label }), _jsx("input", { disabled: disabled, onChange: (event) => onChange(event.currentTarget.value), placeholder: placeholder, style: {
1235
1289
  border: "1px solid #d1d5db",