@fonoster/common 0.30.0 → 0.31.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.
Files changed (88) hide show
  1. package/dist/{assistants → agents}/index.d.ts +1 -1
  2. package/dist/{assistants → agents}/index.js +1 -1
  3. package/dist/{assistants/sessionAssistantSchema.d.ts → agents/sessionAgentSchema.d.ts} +20 -6
  4. package/dist/{assistants/sessionAssistantSchema.js → agents/sessionAgentSchema.js} +16 -11
  5. package/dist/cellsecrets/index.d.ts +2 -2
  6. package/dist/cellsecrets/index.js +3 -3
  7. package/dist/config/cli/render.d.ts +1 -1
  8. package/dist/config/cli/render.js +3 -3
  9. package/dist/config/configHints.d.ts +1 -14
  10. package/dist/config/configHints.js +16 -30
  11. package/dist/config/index.d.ts +0 -1
  12. package/dist/config/index.js +0 -1
  13. package/dist/config/interpolateEnv.d.ts +1 -1
  14. package/dist/config/loadServiceConfig.d.ts +0 -5
  15. package/dist/config/loadServiceConfig.js +3 -12
  16. package/dist/constants.d.ts +1 -3
  17. package/dist/constants.js +3 -5
  18. package/dist/errors/handleZodError.js +3 -1
  19. package/dist/filters/index.d.ts +70 -0
  20. package/dist/filters/index.js +101 -0
  21. package/dist/identity/roles.js +11 -1
  22. package/dist/index.d.ts +4 -1
  23. package/dist/index.js +5 -2
  24. package/dist/protos/applications.proto +16 -2
  25. package/dist/protos/authz.proto +3 -0
  26. package/dist/protos/billing.proto +92 -0
  27. package/dist/protos/calls.proto +4 -0
  28. package/dist/protos/capabilities.proto +8 -0
  29. package/dist/protos/providers.proto +6 -2
  30. package/dist/protos/roomr.proto +146 -0
  31. package/dist/protos/session.proto +194 -0
  32. package/dist/protos/usage.proto +59 -0
  33. package/dist/registry/Registry.d.ts +13 -5
  34. package/dist/registry/Registry.js +49 -35
  35. package/dist/registry/cli.js +19 -7
  36. package/dist/registry/features.d.ts +2 -2
  37. package/dist/registry/features.js +7 -5
  38. package/dist/registry/prisma.d.ts +2 -2
  39. package/dist/registry/prisma.js +26 -18
  40. package/dist/registry/types.d.ts +17 -5
  41. package/dist/registry/types.js +2 -0
  42. package/dist/roomr/health.d.ts +9 -0
  43. package/dist/roomr/health.js +75 -0
  44. package/dist/roomr/index.d.ts +75 -0
  45. package/dist/roomr/index.js +146 -0
  46. package/dist/roomr/session.d.ts +133 -0
  47. package/dist/roomr/session.js +132 -0
  48. package/dist/session/schema.d.ts +196 -7
  49. package/dist/session/schema.js +59 -8
  50. package/dist/sessions/index.d.ts +15 -12
  51. package/dist/sessions/index.js +15 -12
  52. package/dist/telemetry.d.ts +1 -1
  53. package/dist/usage/CallUsage.d.ts +38 -0
  54. package/dist/usage/CallUsage.js +76 -0
  55. package/dist/{utils/types.d.ts → usage/index.d.ts} +4 -5
  56. package/dist/usage/index.js +38 -0
  57. package/dist/usage/meters.d.ts +113 -0
  58. package/dist/usage/meters.js +90 -0
  59. package/dist/usage/types.d.ts +159 -0
  60. package/dist/usage/types.js +84 -0
  61. package/dist/usage/usageId.d.ts +6 -0
  62. package/dist/usage/usageId.js +45 -0
  63. package/dist/utils/index.d.ts +0 -2
  64. package/dist/utils/index.js +0 -2
  65. package/dist/utils/withErrorHandling.js +1 -1
  66. package/dist/validators/calls.d.ts +5 -2
  67. package/dist/validators/calls.js +8 -1
  68. package/dist/validators/sip.d.ts +2 -2
  69. package/dist/validators/sip.js +3 -3
  70. package/dist/webhooks/bus.d.ts +29 -10
  71. package/dist/webhooks/bus.js +58 -50
  72. package/dist/webhooks/events.d.ts +1 -1
  73. package/dist/webhooks/events.js +1 -1
  74. package/package.json +9 -8
  75. package/schema/session.v1.json +82 -2
  76. package/dist/config/integrationsSchema.d.ts +0 -32
  77. package/dist/config/integrationsSchema.js +0 -56
  78. package/dist/utils/createFetchSingleCallByCallId.d.ts +0 -22
  79. package/dist/utils/createFetchSingleCallByCallId.js +0 -33
  80. package/dist/utils/types.js +0 -2
  81. /package/dist/{assistants → agents}/testCasesSchema.d.ts +0 -0
  82. /package/dist/{assistants → agents}/testCasesSchema.js +0 -0
  83. /package/dist/{assistants → agents}/tools/index.d.ts +0 -0
  84. /package/dist/{assistants → agents}/tools/index.js +0 -0
  85. /package/dist/{assistants → agents}/tools/propertySchema.d.ts +0 -0
  86. /package/dist/{assistants → agents}/tools/propertySchema.js +0 -0
  87. /package/dist/{assistants → agents}/tools/toolSchema.d.ts +0 -0
  88. /package/dist/{assistants → agents}/tools/toolSchema.js +0 -0
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AudioFilterConfigError = exports.audioFilterConfigSchema = exports.FILTER_NAMES = exports.FILTER_BY_PROVIDER = exports.FILTER_SCHEMAS = exports.aiCousticsPublicOptionsSchema = exports.AI_COUSTICS_FILTER = void 0;
4
+ exports.providerOfFilter = providerOfFilter;
5
+ exports.parseFilters = parseFilters;
6
+ /**
7
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
8
+ * http://github.com/fonoster/fonoster
9
+ *
10
+ * This file is part of Fonoster
11
+ *
12
+ * Licensed under the MIT License (the "License");
13
+ * you may not use this file except in compliance with
14
+ * the License. You may obtain a copy of the License at
15
+ *
16
+ * https://opensource.org/licenses/MIT
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+ const zod_1 = require("zod");
25
+ /**
26
+ * The audio filters an app may configure (voice-session, "An app's audio filters are set through
27
+ * the Applications API"). Apps are untrusted input, so each filter exposes a public option schema
28
+ * here, shared by the apiserver (validation on write) and the appbridge (the filter factory).
29
+ * Anything that touches the media server's filesystem, environment or credentials stays out.
30
+ */
31
+ exports.AI_COUSTICS_FILTER = "aiCoustics";
32
+ /** The ai-coustics filter's public options. */
33
+ exports.aiCousticsPublicOptionsSchema = zod_1.z
34
+ .object({
35
+ // The vendor's model id; restricted to its shape so it cannot name a path
36
+ model: zod_1.z
37
+ .string()
38
+ .regex(/^[a-z0-9]+(-[a-z0-9.]+)*$/, { message: "Invalid model name" })
39
+ .default("quail-vf-2.2-s-16khz"),
40
+ // Higher values suppress more, competing speech included
41
+ enhancementLevel: zod_1.z.number().min(0).max(1).default(1),
42
+ // Keeps the delay but stops enhancing (for A/B tests)
43
+ bypass: zod_1.z.boolean().default(false)
44
+ })
45
+ // Reject anything else outright, so a server-only option (or a key) is refused, not ignored
46
+ .strict();
47
+ /** Public option schemas by filter name. */
48
+ exports.FILTER_SCHEMAS = {
49
+ [exports.AI_COUSTICS_FILTER]: exports.aiCousticsPublicOptionsSchema
50
+ };
51
+ /** The filter a noise-cancelling (`nc`) provider runs, by provider id. */
52
+ exports.FILTER_BY_PROVIDER = {
53
+ aicoustics: exports.AI_COUSTICS_FILTER
54
+ };
55
+ exports.FILTER_NAMES = Object.keys(exports.FILTER_SCHEMAS);
56
+ /** The `nc` provider whose credential a filter runs with, or undefined for a filter without one. */
57
+ function providerOfFilter(name) {
58
+ var _a;
59
+ return (_a = Object.entries(exports.FILTER_BY_PROVIDER).find(([, f]) => f === name)) === null || _a === void 0 ? void 0 : _a[0];
60
+ }
61
+ exports.audioFilterConfigSchema = zod_1.z
62
+ .object({
63
+ name: zod_1.z.string().min(1),
64
+ options: zod_1.z.record(zod_1.z.unknown()).optional()
65
+ })
66
+ .strict();
67
+ class AudioFilterConfigError extends Error {
68
+ constructor(message) {
69
+ super(message);
70
+ this.name = "AudioFilterConfigError";
71
+ }
72
+ }
73
+ exports.AudioFilterConfigError = AudioFilterConfigError;
74
+ /**
75
+ * Validates an app's `filters` list: known names and public options only. Returns the list with
76
+ * each filter's options as given (defaults are applied by the filter itself). Throws
77
+ * AudioFilterConfigError naming the first offending filter or option.
78
+ */
79
+ function parseFilters(input) {
80
+ const list = zod_1.z.array(exports.audioFilterConfigSchema).safeParse(input);
81
+ if (!list.success) {
82
+ throw new AudioFilterConfigError(`filters must be a list of { name, options? }: ${issues(list.error)}`);
83
+ }
84
+ return list.data.map((f) => {
85
+ var _a;
86
+ const schema = exports.FILTER_SCHEMAS[f.name];
87
+ if (!schema) {
88
+ throw new AudioFilterConfigError(`unknown audio filter "${f.name}" (known: ${exports.FILTER_NAMES.join(", ")})`);
89
+ }
90
+ const parsed = schema.safeParse((_a = f.options) !== null && _a !== void 0 ? _a : {});
91
+ if (!parsed.success) {
92
+ throw new AudioFilterConfigError(`invalid options for audio filter "${f.name}": ${issues(parsed.error)}`);
93
+ }
94
+ return f.options ? { name: f.name, options: f.options } : { name: f.name };
95
+ });
96
+ }
97
+ function issues(error) {
98
+ return error.issues
99
+ .map((i) => i.path.length ? `${i.path.join(".")}: ${i.message}` : i.message)
100
+ .join("; ");
101
+ }
@@ -77,6 +77,7 @@ const workspaceResourceAccess = [
77
77
  "/fonoster.secrets.v1beta2.Secrets/GetSecret",
78
78
  "/fonoster.secrets.v1beta2.Secrets/DeleteSecret",
79
79
  "/fonoster.secrets.v1beta2.Secrets/ListSecrets",
80
+ "/fonoster.usage.v1beta2.Usage/GetUsage",
80
81
  "/fonoster.calls.v1beta2.Calls/CreateCall",
81
82
  "/fonoster.calls.v1beta2.Calls/ListCalls",
82
83
  "/fonoster.calls.v1beta2.Calls/GetCall",
@@ -99,6 +100,14 @@ const workspaceResourceOwnerOrAdminAccess = [
99
100
  "/fonoster.identity.v1beta2.Identity/RegenerateApiKey"
100
101
  ];
101
102
  exports.workspaceResourceOwnerOrAdminAccess = workspaceResourceOwnerOrAdminAccess;
103
+ // Billing belongs to the owner (add-billing-service): only owners see or change it.
104
+ const workspaceOwnerAccess = [
105
+ "/fonoster.billing.v1beta2.Billing/GetBilling",
106
+ "/fonoster.billing.v1beta2.Billing/UpdateAutoRecharge",
107
+ "/fonoster.billing.v1beta2.Billing/CreateTopUp",
108
+ "/fonoster.billing.v1beta2.Billing/Subscribe",
109
+ "/fonoster.billing.v1beta2.Billing/CreatePortalSession"
110
+ ];
102
111
  const fullIdentityAccess = [
103
112
  "/fonoster.identity.v1beta2.Identity/GetUser",
104
113
  "/fonoster.identity.v1beta2.Identity/UpdateUser",
@@ -122,7 +131,8 @@ const roles = [
122
131
  access: [
123
132
  ...fullIdentityAccess,
124
133
  ...workspaceResourceAccess,
125
- ...workspaceResourceOwnerOrAdminAccess
134
+ ...workspaceResourceOwnerOrAdminAccess,
135
+ ...workspaceOwnerAccess
126
136
  ]
127
137
  },
128
138
  {
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
- export * from "./assistants";
19
+ export * from "./agents";
20
20
  export * from "./config";
21
21
  export * from "./GrpcError";
22
22
  export * from "./constants";
@@ -36,9 +36,12 @@ export * from "./identity";
36
36
  export { getRootDomain, setRootDomain } from "./envs";
37
37
  export * from "./session";
38
38
  export * as Registry from "./registry";
39
+ export * as AudioFilters from "./filters";
39
40
  export * as Webhooks from "./webhooks";
41
+ export * as Usage from "./usage";
40
42
  export * as CellSecrets from "./cellsecrets";
41
43
  export * from "./telemetry";
42
44
  export * from "./net";
43
45
  export * as Sessions from "./sessions";
44
46
  export * as RegionHub from "./regions/hub";
47
+ export * as Roomr from "./roomr";
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.RegionHub = exports.Sessions = exports.CellSecrets = exports.Webhooks = exports.Registry = exports.setRootDomain = exports.getRootDomain = exports.Validators = exports.Messages = void 0;
39
+ exports.Roomr = exports.RegionHub = exports.Sessions = exports.CellSecrets = exports.Usage = exports.Webhooks = exports.AudioFilters = exports.Registry = exports.setRootDomain = exports.getRootDomain = exports.Validators = exports.Messages = void 0;
40
40
  /**
41
41
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
42
42
  * http://github.com/fonoster/fonoster
@@ -55,7 +55,7 @@ exports.RegionHub = exports.Sessions = exports.CellSecrets = exports.Webhooks =
55
55
  * See the License for the specific language governing permissions and
56
56
  * limitations under the License.
57
57
  */
58
- __exportStar(require("./assistants"), exports);
58
+ __exportStar(require("./agents"), exports);
59
59
  __exportStar(require("./config"), exports);
60
60
  __exportStar(require("./GrpcError"), exports);
61
61
  __exportStar(require("./constants"), exports);
@@ -77,9 +77,12 @@ Object.defineProperty(exports, "getRootDomain", { enumerable: true, get: functio
77
77
  Object.defineProperty(exports, "setRootDomain", { enumerable: true, get: function () { return envs_1.setRootDomain; } });
78
78
  __exportStar(require("./session"), exports);
79
79
  exports.Registry = __importStar(require("./registry"));
80
+ exports.AudioFilters = __importStar(require("./filters"));
80
81
  exports.Webhooks = __importStar(require("./webhooks"));
82
+ exports.Usage = __importStar(require("./usage"));
81
83
  exports.CellSecrets = __importStar(require("./cellsecrets"));
82
84
  __exportStar(require("./telemetry"), exports);
83
85
  __exportStar(require("./net"), exports);
84
86
  exports.Sessions = __importStar(require("./sessions"));
85
87
  exports.RegionHub = __importStar(require("./regions/hub"));
88
+ exports.Roomr = __importStar(require("./roomr"));
@@ -53,7 +53,7 @@ message SpeechDefaults {
53
53
  string stt_model = 3;
54
54
  // Text-to-speech model (empty: the voice's default)
55
55
  string tts_model = 4;
56
- // Silence timeout in milliseconds (0: the gateway default)
56
+ // Silence timeout in milliseconds (0: AppBridge default)
57
57
  int32 silence_timeout_ms = 5;
58
58
  // The Pacing policy's patience: low, normal or high (empty: normal)
59
59
  string patience = 6;
@@ -114,6 +114,14 @@ message ProductContainer {
114
114
  google.protobuf.Struct credentials = 3;
115
115
  }
116
116
 
117
+ // An audio filter run on the caller's audio before speech recognition
118
+ message AudioFilter {
119
+ // Filter name (aiCoustics)
120
+ string name = 1;
121
+ // The filter's public options (model, enhancement_level, bypass)
122
+ google.protobuf.Struct options = 2;
123
+ }
124
+
117
125
  // Application definition
118
126
  message Application {
119
127
  // Reference to the application
@@ -134,7 +142,7 @@ message Application {
134
142
  int32 created_at = 8;
135
143
  // Update time
136
144
  int32 updated_at = 9;
137
- // The WebSocket URL the gateway dials
145
+ // The WebSocket URL AppBridge dials
138
146
  string url = 10;
139
147
  // Stream protocol: fonoster or twilio
140
148
  string protocol = 11;
@@ -154,6 +162,8 @@ message Application {
154
162
  string recording = 18;
155
163
  // Answering-machine detection (off when unset)
156
164
  AmdSettings amd = 19;
165
+ // Audio filters in order (empty: none)
166
+ repeated AudioFilter filters = 20;
157
167
  }
158
168
 
159
169
  // Request to create a new application
@@ -188,6 +198,8 @@ message CreateApplicationRequest {
188
198
  string recording = 14;
189
199
  // Answering-machine detection (off when unset)
190
200
  AmdSettings amd = 15;
201
+ // Audio filters in order (empty: none)
202
+ repeated AudioFilter filters = 20;
191
203
  }
192
204
 
193
205
  // Response for create application
@@ -256,6 +268,8 @@ message UpdateApplicationRequest {
256
268
  string recording = 15;
257
269
  // Answering-machine detection (off when unset)
258
270
  AmdSettings amd = 16;
271
+ // Audio filters in order (the update replaces the app: left out clears them)
272
+ repeated AudioFilter filters = 20;
259
273
  }
260
274
 
261
275
  // Response for update application
@@ -60,6 +60,9 @@ message CheckSessionAuthorizedResponse {
60
60
  // to determine if the request is authorized. If the request is authorized, the
61
61
  // response will have the value true.
62
62
  bool authorized = 1;
63
+ // Why the session was refused, when it was: insufficient_balance, concurrency_limit or
64
+ // trial_ended (add-billing-service D7). Empty when authorized.
65
+ string reason = 2;
63
66
  }
64
67
 
65
68
  // Request message for the CheckMethodAuthorized method
@@ -0,0 +1,92 @@
1
+ /*
2
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
+ * http://github.com/fonoster/fonoster
4
+ *
5
+ * This file is part of Fonoster
6
+ *
7
+ * Licensed under the MIT License (the "License");
8
+ * you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ *
11
+ * https://opensource.org/licenses/MIT
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ syntax = "proto3";
20
+
21
+ package fonoster.billing.v1beta2;
22
+
23
+ // Billing for Fonoster Cloud (add-billing-service): the owner's credits, plan, auto-recharge,
24
+ // top-ups and the Stripe Customer Portal. The billing service serves it with owner_ref set; the
25
+ // apiserver serves it to the dashboard for workspace owners and sets owner_ref from the caller.
26
+ service Billing {
27
+ // The balance by source, the plan and auto-recharge
28
+ rpc GetBilling (GetBillingRequest) returns (BillingSummary) {}
29
+ // Turns auto-recharge on or off, with its threshold and amount in credits
30
+ rpc UpdateAutoRecharge (UpdateAutoRechargeRequest) returns (BillingSummary) {}
31
+ // Buys top-up credits; they are added once the payment succeeds
32
+ rpc CreateTopUp (CreateTopUpRequest) returns (BillingSummary) {}
33
+ // Moves the owner to Pay-as-you-go (needs a payment method, added in the portal)
34
+ rpc Subscribe (SubscribeRequest) returns (BillingSummary) {}
35
+ // A Stripe Customer Portal link for payment methods and invoices
36
+ rpc CreatePortalSession (CreatePortalSessionRequest) returns (CreatePortalSessionResponse) {}
37
+ }
38
+
39
+ message GetBillingRequest {
40
+ string owner_ref = 1;
41
+ }
42
+
43
+ // Credits of one source: plan, top-up or trial
44
+ message CreditSource {
45
+ string kind = 1;
46
+ double credits = 2;
47
+ // ISO 8601; empty when it does not expire
48
+ string expires_at = 3;
49
+ }
50
+
51
+ message BillingSummary {
52
+ // trial or payg
53
+ string plan = 1;
54
+ // active, past_due, closed, or ended (a Trial past its 30 days; calls are refused until the
55
+ // owner subscribes)
56
+ string status = 2;
57
+ // Credits; negative while in the overdraft
58
+ double balance = 3;
59
+ // How far below zero the balance may go, in credits
60
+ double overdraft = 4;
61
+ repeated CreditSource sources = 5;
62
+ // ISO 8601; empty on the trial
63
+ string period_end = 6;
64
+ bool auto_recharge = 7;
65
+ double recharge_threshold = 8;
66
+ double recharge_amount = 9;
67
+ }
68
+
69
+ message UpdateAutoRechargeRequest {
70
+ string owner_ref = 1;
71
+ bool enabled = 2;
72
+ double threshold = 3;
73
+ double amount = 4;
74
+ }
75
+
76
+ message CreateTopUpRequest {
77
+ string owner_ref = 1;
78
+ int32 credits = 2;
79
+ }
80
+
81
+ message SubscribeRequest {
82
+ string owner_ref = 1;
83
+ }
84
+
85
+ message CreatePortalSessionRequest {
86
+ string owner_ref = 1;
87
+ string return_url = 2;
88
+ }
89
+
90
+ message CreatePortalSessionResponse {
91
+ string url = 1;
92
+ }
@@ -104,6 +104,8 @@ message CallDetailRecord {
104
104
  int32 amd_latency_ms = 14;
105
105
  // Detector-specific cause code (e.g. Asterisk's AMDCAUSE), useful for tuning
106
106
  string amd_cause = 15;
107
+ // The reference of the app that handled the call, empty when no app did
108
+ string app_ref = 16;
107
109
  }
108
110
 
109
111
  // The request message for Calls.Call
@@ -144,6 +146,8 @@ message ListCallsRequest {
144
146
  int32 page_size = 7;
145
147
  // The page token
146
148
  string page_token = 8;
149
+ // Optional app reference; lists only the calls that app handled
150
+ string app_ref = 9;
147
151
  }
148
152
 
149
153
  // The response message for Calls.ListCalls
@@ -61,6 +61,13 @@ message RegionsCapabilities {
61
61
  bool private = 1;
62
62
  }
63
63
 
64
+ // How SIP names (trunk inbound URIs, domains) are checked (features.sip.names).
65
+ message SipCapabilities {
66
+ // "domain": one DNS label under sip.<domain> (the default). "any": any host
67
+ // or IP, for installs whose carriers and phones reach them by address.
68
+ string names = 1;
69
+ }
70
+
64
71
  message SipEndpoint {
65
72
  string host = 1;
66
73
  int32 port = 2;
@@ -91,4 +98,5 @@ message GetCapabilitiesResponse {
91
98
  string app_url = 5;
92
99
  RegionsCapabilities regions = 6;
93
100
  ConnectionCapabilities connection = 7;
101
+ SipCapabilities sip = 8;
94
102
  }
@@ -58,12 +58,16 @@ message CatalogItem {
58
58
  // Deprecated items: the suggested replacement and the retirement date (YYYY-MM-DD)
59
59
  string replacement = 11;
60
60
  string retire_on = 12;
61
+ // Providers: the adapter needs no key (fake, local models); no vendor key to ask for
62
+ bool keyless = 13;
63
+ // A display name ("Aura 2 Thalia"); empty when the entry has none
64
+ string label = 14;
61
65
  }
62
66
 
63
67
  message ListCatalogRequest {
64
68
  // Optional: provider, model or voice
65
69
  string kind = 1;
66
- // Optional: stt, tts or llm (models and providers)
70
+ // Optional: stt, tts, llm or nc (models by type; providers by capability)
67
71
  string type = 2;
68
72
  }
69
73
 
@@ -101,7 +105,7 @@ message DeleteProviderKeyResponse {
101
105
  }
102
106
 
103
107
  message WarningUse {
104
- // app or assistant
108
+ // app or agent
105
109
  string kind = 1;
106
110
  string ref = 2;
107
111
  string name = 3;
@@ -0,0 +1,146 @@
1
+ // Roomr: the Roomr node's control service for its controller.
2
+ // Spec: openspec/changes/redesign-roomr/specs/media-control ("Roomr is a gRPC service with
3
+ // three nouns"). Roomr is the SIP and media edge, not an app server: no play, record or DTMF RPCs
4
+ // (an app does those on its call's control stream; recording is a listener). Messages only grow: add fields, never renumber or reuse them, and receivers
5
+ // ignore fields they do not know.
6
+ //
7
+ // Regenerate the Go code with go/roomr/api/generate.sh.
8
+ syntax = "proto3";
9
+
10
+ package roomr.v1;
11
+
12
+ option go_package = "github.com/fonoster/roomr/api/roomr/v1;roomrv1";
13
+
14
+ service Roomr {
15
+ // Dial out. The room is created, or room_ref is joined (created when it does not exist yet).
16
+ rpc CreateCall(CreateCallRequest) returns (Call);
17
+ // End any call, inbound or outbound (CANCEL while ringing, BYE once answered).
18
+ rpc EndCall(EndCallRequest) returns (Empty);
19
+ // Rooms that do not start from a call.
20
+ rpc CreateRoom(CreateRoomRequest) returns (Room);
21
+ // End every call and participant in the room.
22
+ rpc EndRoom(EndRoomRequest) returns (Empty);
23
+ // Add an RTP participant, a listen-only participant or (not yet) a WebRTC participant.
24
+ rpc AddParticipant(AddParticipantRequest) returns (Participant);
25
+ // Remove a participant (cause "removed").
26
+ rpc RemoveParticipant(RemoveParticipantRequest) returns (Empty);
27
+ }
28
+
29
+ message Empty {}
30
+
31
+ // Digest credentials for the INVITE's 401/407 challenge.
32
+ message SipAuth {
33
+ string username = 1;
34
+ string password = 2;
35
+ }
36
+
37
+ // Where and how to dial.
38
+ message SipTarget {
39
+ // Request-URI, for example "sip:+15551234567@carrier".
40
+ string uri = 1;
41
+ // Where the INVITE is sent ("sip:proxy:5060", "sip:proxy:5061;transport=tls" or "proxy:5060");
42
+ // empty sends it to the Request-URI's host.
43
+ string proxy = 2;
44
+ // Added to the INVITE untouched. Core SIP headers (Via, From, To, Call-ID, CSeq, Contact, ...)
45
+ // cannot be set here.
46
+ map<string, string> headers = 3;
47
+ SipAuth auth = 4;
48
+ }
49
+
50
+ message CreateCallRequest {
51
+ // Chosen by the caller; repeating it returns the existing Call and sends no second INVITE.
52
+ string call_ref = 1;
53
+ SipTarget to = 2;
54
+ // The From user ("+15550001") or a full URI ("sip:+15550001@example.com").
55
+ string from = 3;
56
+ // Join this room, or create it with this ref; empty creates a new room.
57
+ string room_ref = 4;
58
+ // Session endpoint for the call's control stream (not implemented yet: UNIMPLEMENTED).
59
+ string session = 5;
60
+ // How long the callee may ring (default 60 s).
61
+ uint32 ring_timeout_s = 6;
62
+ // Passed through untouched in the events the node publishes for this call (opaque to the
63
+ // node; callers use it for their own references, e.g. app_ref).
64
+ map<string, string> metadata = 7;
65
+ // Lowers the node's maximum call duration for this call's room; higher values are ignored.
66
+ uint32 max_duration_s = 8;
67
+ }
68
+
69
+ message Call {
70
+ string call_ref = 1;
71
+ string room_ref = 2;
72
+ enum State {
73
+ RINGING = 0;
74
+ ANSWERED = 1;
75
+ ENDED = 2;
76
+ }
77
+ State state = 3;
78
+ // The call's participant in its room, once answered (inbound calls: always).
79
+ string participant_ref = 4;
80
+ }
81
+
82
+ message EndCallRequest {
83
+ string call_ref = 1;
84
+ }
85
+
86
+ message CreateRoomRequest {
87
+ // Empty: the node picks one. A ref already in use fails with ALREADY_EXISTS.
88
+ string room_ref = 1;
89
+ uint32 max_duration_s = 2;
90
+ // Overrides the node's emptyRoomTimeout for this room; 0 keeps the node's setting.
91
+ uint32 empty_room_timeout_s = 3;
92
+ // Overrides the node's maxParticipants for this room; 0 keeps the node's setting.
93
+ uint32 max_participants = 4;
94
+ }
95
+
96
+ message Room {
97
+ string room_ref = 1;
98
+ }
99
+
100
+ message EndRoomRequest {
101
+ string room_ref = 1;
102
+ }
103
+
104
+ // An RTP address. codec is an rtpmap name such as "L16/16000" or "PCMU/8000".
105
+ message MediaEndpoint {
106
+ string ip = 1;
107
+ uint32 port = 2;
108
+ string codec = 3;
109
+ // SDES key (AES_CM_128_HMAC_SHA1_80, 30 bytes of base64); empty for plain RTP.
110
+ string srtp_key = 4;
111
+ }
112
+
113
+ message WebRtcOffer {
114
+ string sdp = 1;
115
+ }
116
+
117
+ // One track forwarded, unmixed, to its own RTP destination.
118
+ message ListenTrack {
119
+ uint32 track_id = 1;
120
+ MediaEndpoint rtp = 2;
121
+ }
122
+
123
+ message AddParticipantRequest {
124
+ string room_ref = 1;
125
+ oneof endpoint {
126
+ MediaEndpoint rtp = 2;
127
+ WebRtcOffer webrtc = 3;
128
+ }
129
+ // Listen-only participant: one RTP destination per selected track. Set without an endpoint.
130
+ repeated ListenTrack listen = 4;
131
+ }
132
+
133
+ message Participant {
134
+ string participant_ref = 1;
135
+ string room_ref = 2;
136
+ // RTP participants: where the participant sends its audio (the node's side of the leg).
137
+ MediaEndpoint rtp = 3;
138
+ // WebRTC participants: the SDP answer.
139
+ string sdp_answer = 4;
140
+ uint32 track_id = 5;
141
+ }
142
+
143
+ message RemoveParticipantRequest {
144
+ string room_ref = 1;
145
+ string participant_ref = 2;
146
+ }