crowdplaysdk 0.3.4 → 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/ios/CrowdPlayRNModule.swift +14 -1
- package/ios/CrowdPlayReactNative.m +1 -0
- package/ios/wire.rb +1 -1
- package/lib/index.d.ts +23 -0
- package/lib/index.js +14 -2
- package/llms.txt +18 -3
- package/package.json +1 -1
- package/src/index.ts +42 -4
|
@@ -278,8 +278,20 @@ public final class CrowdPlayRNModule: RCTEventEmitter {
|
|
|
278
278
|
|
|
279
279
|
@objc public func join(_ displayName: NSString, roomCode: NSString,
|
|
280
280
|
consentGrantedAtMs: NSNumber,
|
|
281
|
+
agent: NSDictionary?,
|
|
281
282
|
resolver: @escaping RCTPromiseResolveBlock,
|
|
282
283
|
rejecter: @escaping RCTPromiseRejectBlock) {
|
|
284
|
+
// Per-join voice-AI overrides (D-095): the JS layer already clamped
|
|
285
|
+
// and dropped blanks; nil means "dashboard defaults".
|
|
286
|
+
var agentOptions: CrowdPlayAgentOptions?
|
|
287
|
+
if let agent {
|
|
288
|
+
let voice = agent["voice"] as? String
|
|
289
|
+
let prompt = agent["systemPrompt"] as? String
|
|
290
|
+
let context = agent["context"] as? String
|
|
291
|
+
if voice != nil || prompt != nil || context != nil {
|
|
292
|
+
agentOptions = CrowdPlayAgentOptions(voice: voice, systemPrompt: prompt, context: context)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
283
295
|
Task { @MainActor in
|
|
284
296
|
let core = CrowdPlayRNCore.shared
|
|
285
297
|
core.displayName = displayName as String
|
|
@@ -292,7 +304,8 @@ public final class CrowdPlayRNModule: RCTEventEmitter {
|
|
|
292
304
|
)
|
|
293
305
|
await core.engine.join(displayName: displayName as String,
|
|
294
306
|
roomCode: roomCode as String,
|
|
295
|
-
consent: consent
|
|
307
|
+
consent: consent,
|
|
308
|
+
agent: agentOptions)
|
|
296
309
|
if case let .failed(message) = core.engine.phase {
|
|
297
310
|
rejecter("join_failed", message, nil)
|
|
298
311
|
} else {
|
|
@@ -13,6 +13,7 @@ RCT_EXTERN_METHOD(consentText : (RCTPromiseResolveBlock)resolve
|
|
|
13
13
|
RCT_EXTERN_METHOD(join : (NSString *)displayName
|
|
14
14
|
roomCode : (NSString *)roomCode
|
|
15
15
|
consentGrantedAtMs : (nonnull NSNumber *)consentGrantedAtMs
|
|
16
|
+
agent : (NSDictionary *)agent
|
|
16
17
|
resolver : (RCTPromiseResolveBlock)resolve
|
|
17
18
|
rejecter : (RCTPromiseRejectBlock)reject)
|
|
18
19
|
RCT_EXTERN_METHOD(leave : (RCTPromiseResolveBlock)resolve
|
package/ios/wire.rb
CHANGED
|
@@ -25,7 +25,7 @@ SDK_URL = 'https://github.com/symbiateam/crowdplaysdk'
|
|
|
25
25
|
# The ENGINE release this bridge was tested against. The npm package
|
|
26
26
|
# version may be AHEAD of this (bridge/docs fixes ship without a new
|
|
27
27
|
# engine binary); that is deliberate, not drift.
|
|
28
|
-
SDK_VERSION = '0.
|
|
28
|
+
SDK_VERSION = '0.4.0'
|
|
29
29
|
BRIDGE_FILES = ['CrowdPlayRNModule.swift', 'CrowdPlayRNVideoView.swift', 'CrowdPlayReactNative.m'].freeze
|
|
30
30
|
|
|
31
31
|
project_name = ARGV[0] or abort 'usage: ruby wire.rb <YourProjectName>'
|
package/lib/index.d.ts
CHANGED
|
@@ -58,6 +58,28 @@ export interface ConsentGrant {
|
|
|
58
58
|
/** Date.now() at the moment the participant agreed. */
|
|
59
59
|
grantedAtMs: number;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Per-join settings for the voice AI (D-095), for apps with the Voice AI
|
|
63
|
+
* switch on. The dashboard config stays the default; anything passed here
|
|
64
|
+
* overrides it for THIS session only. Fixed once the session starts (leave
|
|
65
|
+
* and join again to change). The options of the join that creates the AI
|
|
66
|
+
* apply to the whole room. The app can never change the provider, the
|
|
67
|
+
* model, or the API key from here.
|
|
68
|
+
*/
|
|
69
|
+
export interface AgentOptions {
|
|
70
|
+
/** A voice of the app's configured provider. Gemini: Charon, Aoede,
|
|
71
|
+
* Fenrir, Kore, Puck. OpenAI: marin, cedar, alloy, ash, ballad, coral,
|
|
72
|
+
* echo, sage, shimmer, verse. Grok: Ara, Rex, Sal, Eve, Leo. Unknown
|
|
73
|
+
* names fall back to the dashboard voice. */
|
|
74
|
+
voice?: string;
|
|
75
|
+
/** Replaces the dashboard persona for this session. Max 8192 chars. */
|
|
76
|
+
systemPrompt?: string;
|
|
77
|
+
/** Short text appended to the persona: what the app knows about this
|
|
78
|
+
* user, where they left off. This is how an app gives the AI memory:
|
|
79
|
+
* keep a few sentences per user in your own storage and pass them
|
|
80
|
+
* here. Max 4000 chars. */
|
|
81
|
+
context?: string;
|
|
82
|
+
}
|
|
61
83
|
export type Phase = 'idle' | 'connecting' | 'connected' | 'failed';
|
|
62
84
|
export interface RecordingSnapshot {
|
|
63
85
|
/** Seconds of audio safely written so far. */
|
|
@@ -161,6 +183,7 @@ declare const CrowdPlay: {
|
|
|
161
183
|
displayName: string;
|
|
162
184
|
roomCode: string;
|
|
163
185
|
consent: ConsentGrant;
|
|
186
|
+
agent?: AgentOptions;
|
|
164
187
|
}): Promise<void>;
|
|
165
188
|
/** Leave the room. Recording stops; uploads proceed automatically (they
|
|
166
189
|
* survive backgrounding; force-quit pauses them until next launch). */
|
package/lib/index.js
CHANGED
|
@@ -81,7 +81,7 @@ const CrowdPlay = {
|
|
|
81
81
|
if (!configured) {
|
|
82
82
|
throw new Error('crowdplaysdk: call CrowdPlay.configure() before join()');
|
|
83
83
|
}
|
|
84
|
-
const { displayName, roomCode, consent } = options ?? {};
|
|
84
|
+
const { displayName, roomCode, consent, agent } = options ?? {};
|
|
85
85
|
if (!displayName?.trim())
|
|
86
86
|
throw new Error('crowdplaysdk: displayName is required');
|
|
87
87
|
if (!roomCode?.trim())
|
|
@@ -91,7 +91,19 @@ const CrowdPlay = {
|
|
|
91
91
|
'(or your own UI displaying CrowdPlay.consentText()) and pass its result. ' +
|
|
92
92
|
'Recording without consent is not supported.');
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
// Flat strings, clamped to the server's caps; blanks are not sent.
|
|
95
|
+
let agentFields = null;
|
|
96
|
+
if (agent && typeof agent === 'object') {
|
|
97
|
+
const put = (key, value, max) => {
|
|
98
|
+
if (typeof value === 'string' && value.trim()) {
|
|
99
|
+
agentFields = { ...(agentFields ?? {}), [key]: value.slice(0, max) };
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
put('voice', agent.voice, 60);
|
|
103
|
+
put('systemPrompt', agent.systemPrompt, 8192);
|
|
104
|
+
put('context', agent.context, 4000);
|
|
105
|
+
}
|
|
106
|
+
return native().join(displayName.trim(), roomCode, consent.grantedAtMs, agentFields);
|
|
95
107
|
},
|
|
96
108
|
/** Leave the room. Recording stops; uploads proceed automatically (they
|
|
97
109
|
* survive backgrounding; force-quit pauses them until next launch). */
|
package/llms.txt
CHANGED
|
@@ -132,7 +132,9 @@ Default export `CrowdPlay`:
|
|
|
132
132
|
consentText(): Promise<string> // the REQUIRED consent wording
|
|
133
133
|
warmUp(): void // pre-warm backend from the join screen
|
|
134
134
|
join(opts: { displayName: string; roomCode: string;
|
|
135
|
-
consent: ConsentGrant
|
|
135
|
+
consent: ConsentGrant;
|
|
136
|
+
agent?: AgentOptions }): Promise<void> // THROWS without consent
|
|
137
|
+
AgentOptions { voice?: string; systemPrompt?: string; context?: string } // 0.4.0+, voice-AI apps
|
|
136
138
|
leave(): Promise<void>
|
|
137
139
|
setMicMuted(muted: boolean): Promise<void>
|
|
138
140
|
setCameraEnabled(enabled: boolean): Promise<void>
|
|
@@ -159,8 +161,21 @@ call, CrowdPlay's servers put a live AI participant into the room within
|
|
|
159
161
|
the user, and holds a real spoken conversation (speech-to-speech: it
|
|
160
162
|
hears the user directly, answers in natural voice, can be interrupted).
|
|
161
163
|
The app writes ZERO AI code — no model API calls, no AI keys, no chat
|
|
162
|
-
backend, no STT/TTS.
|
|
163
|
-
the
|
|
164
|
+
backend, no STT/TTS. Provider, model and API key are set by the BUILDER on
|
|
165
|
+
the CrowdPlay dashboard, and so are the DEFAULT persona and voice.
|
|
166
|
+
Per-join overrides (0.4.0+): the app MAY pass `agent` to join() to change
|
|
167
|
+
the voice and the persona and give a short context text for one session:
|
|
168
|
+
await CrowdPlay.join({ displayName, roomCode, consent: grant,
|
|
169
|
+
agent: { voice: 'Aoede', context: "The user's name is Sam. Last time you practised past tense." } });
|
|
170
|
+
voice must be one of the configured provider's voices (Gemini: Charon,
|
|
171
|
+
Aoede, Fenrir, Kore, Puck; OpenAI: marin, cedar, alloy, ash, ballad,
|
|
172
|
+
coral, echo, sage, shimmer, verse; Grok: Ara, Rex, Sal, Eve, Leo), unknown
|
|
173
|
+
falls back to the dashboard voice. Caps: systemPrompt 8192 chars, context
|
|
174
|
+
4000. The options of the join that CREATES the AI apply to the whole room;
|
|
175
|
+
fixed for the session (leave and join again to change); provider, model
|
|
176
|
+
and key cannot be changed from the app. MEMORY is built with `context`:
|
|
177
|
+
keep a few sentences per user in the app's own storage and pass them at
|
|
178
|
+
every join.
|
|
164
179
|
Conversations are recorded and transcribed by the platform automatically.
|
|
165
180
|
RECOMMENDED: voice-AI apps should be AUDIO-ONLY (audioOnly: true — no
|
|
166
181
|
camera permission, no video anywhere); a camera adds nothing to talking
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crowdplaysdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CrowdPlay lossless conversation capture for React Native (iOS). Studio-grade per-participant recording during live video calls, delivered to CrowdPlay automatically.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -68,6 +68,29 @@ export interface ConsentGrant {
|
|
|
68
68
|
grantedAtMs: number;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Per-join settings for the voice AI (D-095), for apps with the Voice AI
|
|
73
|
+
* switch on. The dashboard config stays the default; anything passed here
|
|
74
|
+
* overrides it for THIS session only. Fixed once the session starts (leave
|
|
75
|
+
* and join again to change). The options of the join that creates the AI
|
|
76
|
+
* apply to the whole room. The app can never change the provider, the
|
|
77
|
+
* model, or the API key from here.
|
|
78
|
+
*/
|
|
79
|
+
export interface AgentOptions {
|
|
80
|
+
/** A voice of the app's configured provider. Gemini: Charon, Aoede,
|
|
81
|
+
* Fenrir, Kore, Puck. OpenAI: marin, cedar, alloy, ash, ballad, coral,
|
|
82
|
+
* echo, sage, shimmer, verse. Grok: Ara, Rex, Sal, Eve, Leo. Unknown
|
|
83
|
+
* names fall back to the dashboard voice. */
|
|
84
|
+
voice?: string;
|
|
85
|
+
/** Replaces the dashboard persona for this session. Max 8192 chars. */
|
|
86
|
+
systemPrompt?: string;
|
|
87
|
+
/** Short text appended to the persona: what the app knows about this
|
|
88
|
+
* user, where they left off. This is how an app gives the AI memory:
|
|
89
|
+
* keep a few sentences per user in your own storage and pass them
|
|
90
|
+
* here. Max 4000 chars. */
|
|
91
|
+
context?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
71
94
|
export type Phase = 'idle' | 'connecting' | 'connected' | 'failed';
|
|
72
95
|
|
|
73
96
|
export interface RecordingSnapshot {
|
|
@@ -144,7 +167,8 @@ export type CrowdPlayEventName = keyof CrowdPlayEvents;
|
|
|
144
167
|
interface NativeCrowdPlay {
|
|
145
168
|
configure(config: Record<string, unknown>): void;
|
|
146
169
|
consentText(): Promise<string>;
|
|
147
|
-
join(displayName: string, roomCode: string, consentGrantedAtMs: number
|
|
170
|
+
join(displayName: string, roomCode: string, consentGrantedAtMs: number,
|
|
171
|
+
agent: Record<string, string> | null): Promise<void>;
|
|
148
172
|
leave(): Promise<void>;
|
|
149
173
|
setMicMuted(muted: boolean): Promise<void>;
|
|
150
174
|
setCameraEnabled(enabled: boolean): Promise<void>;
|
|
@@ -220,11 +244,13 @@ const CrowdPlay = {
|
|
|
220
244
|
* your own UI at the moment of agreement. There is no way to record
|
|
221
245
|
* without it, here or natively.
|
|
222
246
|
*/
|
|
223
|
-
async join(options: {
|
|
247
|
+
async join(options: {
|
|
248
|
+
displayName: string; roomCode: string; consent: ConsentGrant; agent?: AgentOptions;
|
|
249
|
+
}): Promise<void> {
|
|
224
250
|
if (!configured) {
|
|
225
251
|
throw new Error('crowdplaysdk: call CrowdPlay.configure() before join()');
|
|
226
252
|
}
|
|
227
|
-
const { displayName, roomCode, consent } = options ?? ({} as never);
|
|
253
|
+
const { displayName, roomCode, consent, agent } = options ?? ({} as never);
|
|
228
254
|
if (!displayName?.trim()) throw new Error('crowdplaysdk: displayName is required');
|
|
229
255
|
if (!roomCode?.trim()) throw new Error('crowdplaysdk: roomCode is required');
|
|
230
256
|
if (!consent || typeof consent.grantedAtMs !== 'number' || consent.grantedAtMs <= 0) {
|
|
@@ -234,7 +260,19 @@ const CrowdPlay = {
|
|
|
234
260
|
'Recording without consent is not supported.'
|
|
235
261
|
);
|
|
236
262
|
}
|
|
237
|
-
|
|
263
|
+
// Flat strings, clamped to the server's caps; blanks are not sent.
|
|
264
|
+
let agentFields: Record<string, string> | null = null;
|
|
265
|
+
if (agent && typeof agent === 'object') {
|
|
266
|
+
const put = (key: string, value: unknown, max: number) => {
|
|
267
|
+
if (typeof value === 'string' && value.trim()) {
|
|
268
|
+
agentFields = { ...(agentFields ?? {}), [key]: value.slice(0, max) };
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
put('voice', agent.voice, 60);
|
|
272
|
+
put('systemPrompt', agent.systemPrompt, 8192);
|
|
273
|
+
put('context', agent.context, 4000);
|
|
274
|
+
}
|
|
275
|
+
return native().join(displayName.trim(), roomCode, consent.grantedAtMs, agentFields);
|
|
238
276
|
},
|
|
239
277
|
|
|
240
278
|
/** Leave the room. Recording stops; uploads proceed automatically (they
|