@fiodos/web-core 0.1.9 → 0.1.11
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/cjs/api/backendClient.js +20 -0
- package/dist/cjs/config/types.d.ts +18 -0
- package/dist/cjs/config/types.js +6 -1
- package/dist/cjs/controller/AgentController.d.ts +84 -1
- package/dist/cjs/controller/AgentController.js +515 -75
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/orb/mountOrb.d.ts +6 -0
- package/dist/cjs/orb/mountOrb.js +307 -53
- package/dist/cjs/orb/orbView.d.ts +2 -0
- package/dist/cjs/orb/publishedConfig.d.ts +18 -0
- package/dist/cjs/orb/publishedConfig.js +6 -0
- package/dist/cjs/speech/bubbleDictation.d.ts +11 -0
- package/dist/cjs/speech/bubbleDictation.js +88 -0
- package/dist/cjs/ui/messages.d.ts +15 -0
- package/dist/cjs/ui/messages.js +12 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/api/backendClient.js +20 -0
- package/dist/esm/config/types.d.ts +18 -0
- package/dist/esm/config/types.js +5 -0
- package/dist/esm/controller/AgentController.d.ts +84 -1
- package/dist/esm/controller/AgentController.js +517 -77
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/orb/mountOrb.d.ts +6 -0
- package/dist/esm/orb/mountOrb.js +308 -54
- package/dist/esm/orb/orbView.d.ts +2 -0
- package/dist/esm/orb/publishedConfig.d.ts +18 -0
- package/dist/esm/orb/publishedConfig.js +6 -0
- package/dist/esm/speech/bubbleDictation.d.ts +11 -0
- package/dist/esm/speech/bubbleDictation.js +84 -0
- package/dist/esm/ui/messages.d.ts +15 -0
- package/dist/esm/ui/messages.js +12 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { SDK_VERSION, SDK_PACKAGE } from './version';
|
|
11
11
|
export { AgentController } from './controller/AgentController';
|
|
12
|
-
export type { AgentState, AgentExchange } from './controller/AgentController';
|
|
12
|
+
export type { AgentState, AgentExchange, AgentActivity } from './controller/AgentController';
|
|
13
13
|
export { createFiodosAgent } from './dropin/createFiodosAgent';
|
|
14
14
|
export type { CreateFiodosAgentOptions, FiodosAgentHandle } from './dropin/createFiodosAgent';
|
|
15
15
|
export { DEFAULT_WEB_API_URL, fetchClientManifest, sendOrbSeen, } from './api/clientBootstrap';
|
|
@@ -38,8 +38,8 @@ export { createScreenContextStore } from './context/screenContextStore';
|
|
|
38
38
|
export type { ScreenContextStore, AgentScreenSnapshot } from './context/screenContextStore';
|
|
39
39
|
export { createSpeechSession } from './speech/speechSession';
|
|
40
40
|
export type { SpeechSession, SpeechSessionOptions, SpeechSessionState, SpeechSessionSnapshot, } from './speech/speechSession';
|
|
41
|
-
export { DEFAULT_AGENT_TIMINGS } from './config/types';
|
|
42
|
-
export type { FiodosWebAgentConfig, AgentPhase, AgentTimings, TurnGateVerdict, } from './config/types';
|
|
41
|
+
export { DEFAULT_AGENT_TIMINGS, DEFAULT_AGENT_CHAINING } from './config/types';
|
|
42
|
+
export type { FiodosWebAgentConfig, AgentPhase, AgentTimings, AgentChaining, TurnGateVerdict, } from './config/types';
|
|
43
43
|
export { resolveUiMessages } from './ui/messages';
|
|
44
44
|
export type { WebUiMessages, ResolveUiMessagesOptions } from './ui/messages';
|
|
45
45
|
export { decideAction, classifyPendingReply } from './core/turnEngine';
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* behaviour and security. No framework imports live here.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.classifyPendingReply = exports.decideAction = exports.resolveUiMessages = exports.DEFAULT_AGENT_TIMINGS = exports.createSpeechSession = exports.createScreenContextStore = exports.createBridge = exports.isAgentApiError = exports.AgentApiError = exports.createFiodosTelemetry = exports.buildManifestPayload = exports.createFiodosBackendClient = exports.createWebVoiceAdapter = exports.BACK_ROUTE = exports.createWebNavigationAdapter = exports.createWebStorageAdapter = exports.watchPublishedConfig = exports.DEFAULT_ORB_APPEARANCE = exports.buildKeyboardChip = exports.createOrbVisual = exports.mountOrb = exports.sendOrbSeen = exports.fetchClientManifest = exports.DEFAULT_WEB_API_URL = exports.createFiodosAgent = exports.AgentController = exports.SDK_PACKAGE = exports.SDK_VERSION = void 0;
|
|
12
|
+
exports.classifyPendingReply = exports.decideAction = exports.resolveUiMessages = exports.DEFAULT_AGENT_CHAINING = exports.DEFAULT_AGENT_TIMINGS = exports.createSpeechSession = exports.createScreenContextStore = exports.createBridge = exports.isAgentApiError = exports.AgentApiError = exports.createFiodosTelemetry = exports.buildManifestPayload = exports.createFiodosBackendClient = exports.createWebVoiceAdapter = exports.BACK_ROUTE = exports.createWebNavigationAdapter = exports.createWebStorageAdapter = exports.watchPublishedConfig = exports.DEFAULT_ORB_APPEARANCE = exports.buildKeyboardChip = exports.createOrbVisual = exports.mountOrb = exports.sendOrbSeen = exports.fetchClientManifest = exports.DEFAULT_WEB_API_URL = exports.createFiodosAgent = exports.AgentController = exports.SDK_PACKAGE = exports.SDK_VERSION = void 0;
|
|
13
13
|
// ── Version (reported by the orb heartbeat) ───────────────────────────────────
|
|
14
14
|
var version_1 = require("./version");
|
|
15
15
|
Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_1.SDK_VERSION; } });
|
|
@@ -63,6 +63,7 @@ Object.defineProperty(exports, "createSpeechSession", { enumerable: true, get: f
|
|
|
63
63
|
// ── Config / i18n ─────────────────────────────────────────────────────────────
|
|
64
64
|
var types_1 = require("./config/types");
|
|
65
65
|
Object.defineProperty(exports, "DEFAULT_AGENT_TIMINGS", { enumerable: true, get: function () { return types_1.DEFAULT_AGENT_TIMINGS; } });
|
|
66
|
+
Object.defineProperty(exports, "DEFAULT_AGENT_CHAINING", { enumerable: true, get: function () { return types_1.DEFAULT_AGENT_CHAINING; } });
|
|
66
67
|
var messages_1 = require("./ui/messages");
|
|
67
68
|
Object.defineProperty(exports, "resolveUiMessages", { enumerable: true, get: function () { return messages_1.resolveUiMessages; } });
|
|
68
69
|
// ── Decision engine (shared, framework-free; advanced use / testing) ──────────
|
|
@@ -17,6 +17,12 @@ export interface MountOrbOptions {
|
|
|
17
17
|
appearancePollMs?: number;
|
|
18
18
|
/** Honour the dashboard's published screen position. Default: true. */
|
|
19
19
|
usePublishedPosition?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Internal-profile orbs only render on development hosts (localhost, LAN
|
|
22
|
+
* IPs, *.local). Set true to assert this page IS internal software (e.g. a
|
|
23
|
+
* hosted admin panel) so an internal orb may render here. Default: false.
|
|
24
|
+
*/
|
|
25
|
+
allowInternalOrb?: boolean;
|
|
20
26
|
}
|
|
21
27
|
export interface MountedOrb {
|
|
22
28
|
unmount(): void;
|
package/dist/cjs/orb/mountOrb.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.mountOrb = mountOrb;
|
|
|
22
22
|
const core_1 = require("@fiodos/core");
|
|
23
23
|
const orbView_1 = require("./orbView");
|
|
24
24
|
const publishedConfig_1 = require("./publishedConfig");
|
|
25
|
+
const bubbleDictation_1 = require("../speech/bubbleDictation");
|
|
25
26
|
const STYLE_ID = 'fyodos-orb-styles';
|
|
26
27
|
const CSS = `
|
|
27
28
|
.fyodos-orb-root{position:fixed;display:inline-flex;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;touch-action:none;}
|
|
@@ -30,20 +31,32 @@ const CSS = `
|
|
|
30
31
|
.fyodos-orb-btn:focus-visible{outline:2px solid #8ab6ff;outline-offset:4px;border-radius:50%;}
|
|
31
32
|
.fyodos-orb-anchor{position:relative;display:inline-flex;}
|
|
32
33
|
.fyodos-bubble{position:absolute;display:flex;flex-direction:column;width:min(320px,calc(100vw - 48px));max-height:340px;overflow:hidden;border-radius:16px;box-shadow:0 16px 40px rgba(0,0,0,0.28);border:1px solid rgba(160,180,220,0.22);animation:fyodos-fade-in .14s ease-out;}
|
|
33
|
-
.fyodos-bubble-close{
|
|
34
|
+
.fyodos-bubble-close{position:absolute;top:4px;right:6px;z-index:1;display:flex;align-items:center;justify-content:center;border:none;background:transparent;font-size:18px;line-height:18px;width:28px;height:28px;cursor:pointer;padding:0;opacity:.75;}
|
|
34
35
|
.fyodos-bubble-user-row{display:flex;flex-direction:row;align-items:flex-start;gap:6px;}
|
|
35
|
-
.fyodos-bubble-scroll{overflow-y:auto;padding:12px 14px 4px;display:flex;flex-direction:column;gap:6px;}
|
|
36
|
+
.fyodos-bubble-scroll{overflow-y:auto;padding:12px 14px 4px;display:flex;flex-direction:column;gap:6px;min-height:18px;scrollbar-width:none;-ms-overflow-style:none;}
|
|
37
|
+
.fyodos-bubble-scroll::-webkit-scrollbar{display:none;width:0;height:0;}
|
|
38
|
+
.fyodos-bubble-loader{display:flex;justify-content:center;align-items:center;padding:2px 0 6px;flex-shrink:0;}
|
|
39
|
+
.fyodos-bubble-loader span{width:14px;height:14px;border-radius:50%;border:2px solid rgba(148,163,184,0.35);border-top-color:rgba(148,163,184,0.9);animation:fy-spin .7s linear infinite;}
|
|
40
|
+
.fyodos-bubble-exchanges{display:flex;flex-direction:column;gap:12px;}
|
|
41
|
+
.fyodos-bubble-exchange{display:flex;flex-direction:column;gap:6px;}
|
|
42
|
+
.fyodos-bubble-exchange:first-child .fyodos-bubble-user{padding-right:22px;}
|
|
36
43
|
.fyodos-bubble-user{flex:1;min-width:0;font-size:13px;line-height:18px;opacity:.75;}
|
|
37
44
|
.fyodos-bubble-reply{font-size:15px;line-height:21px;white-space:pre-wrap;}
|
|
38
45
|
.fyodos-bubble-busy{display:flex;gap:5px;padding:8px 14px;align-items:center;}
|
|
39
46
|
.fyodos-bubble-busy span{width:6px;height:6px;border-radius:50%;opacity:.5;animation:fyodos-blink 1s infinite;}
|
|
40
47
|
.fyodos-bubble-busy span:nth-child(2){animation-delay:.18s;}
|
|
41
48
|
.fyodos-bubble-busy span:nth-child(3){animation-delay:.36s;}
|
|
49
|
+
.fyodos-busy-step{margin-right:6px;font-size:12.5px;line-height:18px;font-style:italic;animation:fyodos-fade-in .28s ease-out;}
|
|
42
50
|
.fyodos-bubble-row{display:flex;gap:8px;align-items:flex-end;padding:8px 10px;border-top:1px solid rgba(160,180,220,0.18);}
|
|
43
|
-
.fyodos-bubble-row input{flex:1;
|
|
51
|
+
.fyodos-bubble-row input{flex:1;min-width:0;border:none;outline:none;border-radius:10px;padding:9px 11px;font-size:15px;}
|
|
52
|
+
.fyodos-bubble-row input::placeholder{color:var(--fyodos-input-placeholder,#94a3b8);opacity:1;}
|
|
53
|
+
.fyodos-bubble-mic{appearance:none;flex-shrink:0;display:flex;align-items:center;justify-content:center;width:32px;height:32px;min-width:32px;padding:0;border:1px solid rgba(148,163,184,0.4);border-radius:999px;background:rgba(255,255,255,0.06);color:inherit;cursor:pointer;}
|
|
44
54
|
.fyodos-bubble-row button{appearance:none;border:none;flex-shrink:0;display:flex;align-items:center;justify-content:center;width:32px;height:32px;min-width:32px;padding:0;font-size:17px;font-weight:700;line-height:1;color:#000;cursor:pointer;}
|
|
45
|
-
.fyodos-thought{position:absolute;appearance:none;display:
|
|
55
|
+
.fyodos-thought{position:absolute;appearance:none;box-sizing:border-box;display:flex;align-items:center;width:max-content;max-width:min(280px,70vw);white-space:nowrap;overflow:hidden;text-align:left;line-height:1.25;font-weight:500;cursor:pointer;box-shadow:0 8px 24px rgba(0,0,0,0.22);animation:fyodos-thought-in .18s ease-out;}
|
|
46
56
|
@keyframes fyodos-thought-in{from{opacity:0;transform:translateY(4px) scale(0.96)}to{opacity:1;transform:none}}
|
|
57
|
+
@keyframes fyodos-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
|
|
58
|
+
.fyodos-thought-track{display:flex;flex-shrink:0;width:max-content;will-change:transform;}
|
|
59
|
+
.fyodos-thought-track>span{flex-shrink:0;padding-right:48px;white-space:nowrap;}
|
|
47
60
|
@keyframes fyodos-fade-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
|
48
61
|
@keyframes fyodos-blink{0%,100%{opacity:.25}50%{opacity:.9}}
|
|
49
62
|
@keyframes fy-spin{to{transform:rotate(360deg)}}
|
|
@@ -127,7 +140,7 @@ function mountOrb(controller, options = {}) {
|
|
|
127
140
|
}
|
|
128
141
|
// Subtle haptic feedback on every relevant orb interaction (silent no-op on
|
|
129
142
|
// browsers without the Vibration API — desktop, iOS Safari).
|
|
130
|
-
const haptics = (0, core_1.createWebOrbHaptics)();
|
|
143
|
+
const haptics = (0, core_1.createWebOrbHaptics)(() => effectiveAppearance().hapticIntensity);
|
|
131
144
|
// ── Effective appearance: defaults ← published ← explicit option overrides ──
|
|
132
145
|
let publishedAppearance = null;
|
|
133
146
|
let chipTheme = null;
|
|
@@ -137,6 +150,9 @@ function mountOrb(controller, options = {}) {
|
|
|
137
150
|
// Dashboard VOICE switch. Default true (voice on). When false the orb never
|
|
138
151
|
// listens/speaks: a tap opens the keyboard directly and text turns are silent.
|
|
139
152
|
let voiceInputEnabled = true;
|
|
153
|
+
// Dashboard "thinking steps" switch. When true the busy row walks through
|
|
154
|
+
// what the agent is doing ("Analyzing your request…") instead of only dots.
|
|
155
|
+
let thinkingStepsEnabled = false;
|
|
140
156
|
let currentRoute = null;
|
|
141
157
|
let screenPosition = options.corner
|
|
142
158
|
? cornerToPosition(options.corner)
|
|
@@ -170,19 +186,32 @@ function mountOrb(controller, options = {}) {
|
|
|
170
186
|
let chipEl = null;
|
|
171
187
|
// Thought bubble: a short, screen-aware CTA beside the orb. Created once and
|
|
172
188
|
// shown/hidden by syncThoughtBubble; tapping it opens the keyboard.
|
|
173
|
-
const thoughtBubbleEl = document.createElement('
|
|
174
|
-
thoughtBubbleEl.type = 'button';
|
|
189
|
+
const thoughtBubbleEl = document.createElement('div');
|
|
175
190
|
thoughtBubbleEl.className = 'fyodos-thought';
|
|
191
|
+
thoughtBubbleEl.setAttribute('role', 'button');
|
|
192
|
+
thoughtBubbleEl.tabIndex = 0;
|
|
176
193
|
thoughtBubbleEl.style.display = 'none';
|
|
177
194
|
thoughtBubbleEl.addEventListener('click', () => {
|
|
178
195
|
haptics.trigger('orbTap');
|
|
179
196
|
controller.primeAudio();
|
|
180
197
|
if (enableTextInput) {
|
|
181
|
-
|
|
198
|
+
const prompt = resolveCurrentBubblePrompt();
|
|
199
|
+
openTextPanel(prompt
|
|
200
|
+
? (0, core_1.bubblePromptToAffirmative)(prompt, {
|
|
201
|
+
locale: controller.config.locale,
|
|
202
|
+
messages: controller.messages,
|
|
203
|
+
})
|
|
204
|
+
: undefined);
|
|
182
205
|
return;
|
|
183
206
|
}
|
|
184
207
|
void controller.toggleListening();
|
|
185
208
|
});
|
|
209
|
+
thoughtBubbleEl.addEventListener('keydown', (event) => {
|
|
210
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
thoughtBubbleEl.click();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
186
215
|
anchor.appendChild(thoughtBubbleEl);
|
|
187
216
|
container.appendChild(root);
|
|
188
217
|
// ── Position (dashboard position; user drag is SESSION-only, never persisted) ─
|
|
@@ -372,35 +401,168 @@ function mountOrb(controller, options = {}) {
|
|
|
372
401
|
}
|
|
373
402
|
const exchangeScroll = document.createElement('div');
|
|
374
403
|
exchangeScroll.className = 'fyodos-bubble-scroll';
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const userLine = document.createElement('div');
|
|
378
|
-
userLine.className = 'fyodos-bubble-user';
|
|
404
|
+
// Close button overlaid at the bubble corner — in line with the first message,
|
|
405
|
+
// exactly like the dashboard preview (no extra header row/space).
|
|
379
406
|
const closeBtn = document.createElement('button');
|
|
380
407
|
closeBtn.type = 'button';
|
|
381
408
|
closeBtn.className = 'fyodos-bubble-close';
|
|
382
409
|
closeBtn.textContent = '×';
|
|
383
410
|
closeBtn.setAttribute('aria-label', 'Close');
|
|
384
411
|
closeBtn.addEventListener('click', () => setBubble(false));
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
412
|
+
// Loading row shown at the top while an older page of the session history
|
|
413
|
+
// streams in (scroll-to-top pagination).
|
|
414
|
+
const loaderRow = document.createElement('div');
|
|
415
|
+
loaderRow.className = 'fyodos-bubble-loader';
|
|
416
|
+
loaderRow.append(document.createElement('span'));
|
|
417
|
+
loaderRow.style.display = 'none';
|
|
418
|
+
const exchangeList = document.createElement('div');
|
|
419
|
+
exchangeList.className = 'fyodos-bubble-exchanges';
|
|
420
|
+
exchangeScroll.append(loaderRow, exchangeList);
|
|
421
|
+
// Signature of the rendered list so state emits that do not change the
|
|
422
|
+
// transcript (volume, phase…) never re-render it nor fight the user's scroll.
|
|
423
|
+
let renderedSignature = '';
|
|
424
|
+
function renderBubbleExchanges(exchanges) {
|
|
425
|
+
const signature = exchanges.map((ex) => `${ex.userText}\u0000${ex.replyText}`).join('\u0001');
|
|
426
|
+
if (signature === renderedSignature)
|
|
427
|
+
return;
|
|
428
|
+
// Prepend (older page loaded): the previous tail is intact at the end.
|
|
429
|
+
// Keep the user's reading position anchored instead of jumping to bottom.
|
|
430
|
+
const prepended = renderedSignature.length > 0 &&
|
|
431
|
+
signature.length > renderedSignature.length &&
|
|
432
|
+
signature.endsWith(renderedSignature);
|
|
433
|
+
const fromBottom = exchangeScroll.scrollHeight - exchangeScroll.scrollTop;
|
|
434
|
+
renderedSignature = signature;
|
|
435
|
+
exchangeList.replaceChildren();
|
|
436
|
+
for (const ex of exchanges) {
|
|
437
|
+
const block = document.createElement('div');
|
|
438
|
+
block.className = 'fyodos-bubble-exchange';
|
|
439
|
+
const user = document.createElement('div');
|
|
440
|
+
user.className = 'fyodos-bubble-user';
|
|
441
|
+
user.textContent = ex.userText;
|
|
442
|
+
block.append(user);
|
|
443
|
+
if (ex.replyText) {
|
|
444
|
+
const reply = document.createElement('div');
|
|
445
|
+
reply.className = 'fyodos-bubble-reply';
|
|
446
|
+
reply.textContent = ex.replyText;
|
|
447
|
+
block.append(reply);
|
|
448
|
+
}
|
|
449
|
+
exchangeList.append(block);
|
|
450
|
+
}
|
|
451
|
+
exchangeScroll.scrollTop = prepended
|
|
452
|
+
? exchangeScroll.scrollHeight - fromBottom
|
|
453
|
+
: exchangeScroll.scrollHeight;
|
|
454
|
+
}
|
|
455
|
+
// Scroll-to-top pagination: reaching the top with older messages available
|
|
456
|
+
// pages the next block in (the controller shows the loader while it mounts).
|
|
457
|
+
exchangeScroll.addEventListener('scroll', () => {
|
|
458
|
+
if (!bubbleOpen || exchangeScroll.scrollTop > 12)
|
|
459
|
+
return;
|
|
460
|
+
const state = controller.getState();
|
|
461
|
+
if (state.hasOlderExchanges && !state.loadingOlderExchanges) {
|
|
462
|
+
controller.loadOlderExchanges();
|
|
463
|
+
}
|
|
464
|
+
});
|
|
389
465
|
const busyRow = document.createElement('div');
|
|
390
466
|
busyRow.className = 'fyodos-bubble-busy';
|
|
467
|
+
// REAL live activity FIRST in the row — flush left, aligned with the
|
|
468
|
+
// messages above (an <em> so the dot styling, which targets the spans,
|
|
469
|
+
// never applies to it). Hidden unless the dashboard advanced option is on.
|
|
470
|
+
const busyStep = document.createElement('em');
|
|
471
|
+
busyStep.className = 'fyodos-busy-step';
|
|
472
|
+
busyStep.style.display = 'none';
|
|
473
|
+
busyRow.appendChild(busyStep);
|
|
391
474
|
busyRow.append(document.createElement('span'), document.createElement('span'), document.createElement('span'));
|
|
475
|
+
// ── REAL activity line (dashboard advanced option) ─────────────────────────
|
|
476
|
+
// Replaces the old canned walk-through: the controller reports what the
|
|
477
|
+
// agent is ACTUALLY doing (thinking / navigating / running an action /
|
|
478
|
+
// waiting for confirmation) and the line updates only on real changes.
|
|
479
|
+
let lastActivityText = null;
|
|
480
|
+
function activityText(state) {
|
|
481
|
+
if (!thinkingStepsEnabled || !state.liveActivity)
|
|
482
|
+
return null;
|
|
483
|
+
const { kind, label } = state.liveActivity;
|
|
484
|
+
switch (kind) {
|
|
485
|
+
case 'navigating':
|
|
486
|
+
return ui.activityNavigating.replace('{label}', label ?? '');
|
|
487
|
+
case 'executing':
|
|
488
|
+
return ui.activityExecuting.replace('{label}', label ?? '');
|
|
489
|
+
case 'waiting_confirmation':
|
|
490
|
+
return ui.activityWaitingConfirmation;
|
|
491
|
+
default:
|
|
492
|
+
return ui.activityThinking;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function syncActivityStep(show, state) {
|
|
496
|
+
const text = show ? activityText(state) : null;
|
|
497
|
+
if (text === lastActivityText)
|
|
498
|
+
return;
|
|
499
|
+
lastActivityText = text;
|
|
500
|
+
if (!text) {
|
|
501
|
+
busyStep.textContent = '';
|
|
502
|
+
busyStep.style.display = 'none';
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
busyStep.style.display = '';
|
|
506
|
+
busyStep.textContent = text;
|
|
507
|
+
// Restart the fade-in for each new real step.
|
|
508
|
+
busyStep.style.animation = 'none';
|
|
509
|
+
void busyStep.offsetWidth;
|
|
510
|
+
busyStep.style.animation = '';
|
|
511
|
+
}
|
|
392
512
|
const inputRow = document.createElement('div');
|
|
393
513
|
inputRow.className = 'fyodos-bubble-row';
|
|
394
514
|
const textInput = document.createElement('input');
|
|
395
515
|
textInput.type = 'text';
|
|
396
516
|
textInput.placeholder = ui.textPlaceholder;
|
|
517
|
+
const micBtn = document.createElement('button');
|
|
518
|
+
micBtn.type = 'button';
|
|
519
|
+
micBtn.className = 'fyodos-bubble-mic';
|
|
520
|
+
micBtn.innerHTML =
|
|
521
|
+
'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="3" width="6" height="11" rx="3"></rect><path d="M5 11a7 7 0 0014 0 M12 18v3"></path></svg>';
|
|
522
|
+
micBtn.style.display = 'none';
|
|
397
523
|
const sendBtn = document.createElement('button');
|
|
398
524
|
// Unified send affordance: an up-arrow glyph on the accent square (parity
|
|
399
525
|
// with the dashboard preview and every native SDK), not a text label.
|
|
400
526
|
sendBtn.textContent = '↑';
|
|
401
527
|
sendBtn.setAttribute('aria-label', ui.sendLabel);
|
|
402
|
-
inputRow.append(textInput, sendBtn);
|
|
403
|
-
|
|
528
|
+
inputRow.append(textInput, micBtn, sendBtn);
|
|
529
|
+
const bubbleDictation = (0, bubbleDictation_1.createBubbleDictation)(controller.config.locale);
|
|
530
|
+
if (bubbleDictation.isSupported()) {
|
|
531
|
+
micBtn.style.display = 'flex';
|
|
532
|
+
micBtn.setAttribute('aria-label', ui.micLabel);
|
|
533
|
+
micBtn.title = ui.micLabel;
|
|
534
|
+
micBtn.addEventListener('click', () => {
|
|
535
|
+
if (controller.getState().isBusy)
|
|
536
|
+
return;
|
|
537
|
+
bubbleDictation.toggle(() => textInput.value, (next) => {
|
|
538
|
+
textInput.value = next;
|
|
539
|
+
(0, bubbleDictation_1.scrollTextInputToEnd)(textInput);
|
|
540
|
+
});
|
|
541
|
+
syncMicButton();
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
function syncMicButton() {
|
|
545
|
+
const accent = modalTheme?.primaryButtonColor ?? effectiveAppearance().accentColor;
|
|
546
|
+
const sendRadius = `${modalTheme?.sendButtonRadius ?? 10}px`;
|
|
547
|
+
const bodyColor = { ...DEFAULT_MODAL_THEME, ...(modalTheme ?? {}) }.bodyColor;
|
|
548
|
+
const active = bubbleDictation.isListening();
|
|
549
|
+
micBtn.classList.toggle('is-listening', active);
|
|
550
|
+
if (active) {
|
|
551
|
+
micBtn.style.background = accent;
|
|
552
|
+
micBtn.style.border = 'none';
|
|
553
|
+
micBtn.style.color = '#000';
|
|
554
|
+
micBtn.style.borderRadius = sendRadius;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
micBtn.style.background = 'rgba(255,255,255,0.06)';
|
|
558
|
+
micBtn.style.border = '1px solid rgba(148,163,184,0.4)';
|
|
559
|
+
micBtn.style.color = bodyColor;
|
|
560
|
+
micBtn.style.borderRadius = '999px';
|
|
561
|
+
}
|
|
562
|
+
micBtn.setAttribute('aria-label', active ? ui.micStopLabel : ui.micLabel);
|
|
563
|
+
micBtn.title = active ? ui.micStopLabel : ui.micLabel;
|
|
564
|
+
}
|
|
565
|
+
bubble.append(closeBtn, exchangeScroll, busyRow, inputRow);
|
|
404
566
|
anchor.appendChild(bubble);
|
|
405
567
|
// Initial placement (after the bubble exists, so applyDeployment can lay it out).
|
|
406
568
|
reposition();
|
|
@@ -411,16 +573,25 @@ function mountOrb(controller, options = {}) {
|
|
|
411
573
|
bubble.style.borderColor = t.cardBorderColor;
|
|
412
574
|
if (modalTheme?.borderRadius != null)
|
|
413
575
|
bubble.style.borderRadius = `${modalTheme.borderRadius}px`;
|
|
414
|
-
userLine.style.color = t.bodyColor;
|
|
415
|
-
replyLine.style.color = t.titleColor;
|
|
416
576
|
closeBtn.style.color = t.bodyColor;
|
|
417
|
-
|
|
418
|
-
|
|
577
|
+
exchangeList.querySelectorAll('.fyodos-bubble-user').forEach((node) => {
|
|
578
|
+
node.style.color = t.bodyColor;
|
|
579
|
+
});
|
|
580
|
+
exchangeList.querySelectorAll('.fyodos-bubble-reply').forEach((node) => {
|
|
581
|
+
node.style.color = t.titleColor;
|
|
582
|
+
});
|
|
583
|
+
const inputBg = modalTheme?.inputBackgroundColor ?? '#ffffff';
|
|
584
|
+
textInput.style.background = inputBg;
|
|
585
|
+
textInput.style.color = (0, core_1.fiodosInputInkColor)(inputBg);
|
|
586
|
+
textInput.style.setProperty('--fyodos-input-placeholder', (0, core_1.fiodosInputMutedInkColor)(inputBg));
|
|
419
587
|
sendBtn.style.background = accent;
|
|
420
588
|
sendBtn.style.borderRadius = `${modalTheme?.sendButtonRadius ?? 10}px`;
|
|
589
|
+
syncMicButton();
|
|
421
590
|
busyRow.querySelectorAll('span').forEach((s) => {
|
|
422
591
|
s.style.background = accent;
|
|
423
592
|
});
|
|
593
|
+
// Live-activity line: white (reply color), italic — not the faint gray.
|
|
594
|
+
busyStep.style.color = t.titleColor;
|
|
424
595
|
}
|
|
425
596
|
function setBubble(open) {
|
|
426
597
|
bubbleOpen = open;
|
|
@@ -431,6 +602,10 @@ function mountOrb(controller, options = {}) {
|
|
|
431
602
|
textInput.focus();
|
|
432
603
|
}
|
|
433
604
|
else {
|
|
605
|
+
bubbleDictation.stop();
|
|
606
|
+
syncMicButton();
|
|
607
|
+
// Collapse the paged history back to the initial page for the next open.
|
|
608
|
+
controller.resetBubbleWindow();
|
|
434
609
|
// Closing the panel returns the orb to idle (also clears mic fallback).
|
|
435
610
|
micFallbackActive = false;
|
|
436
611
|
}
|
|
@@ -444,15 +619,27 @@ function mountOrb(controller, options = {}) {
|
|
|
444
619
|
const value = textInput.value.trim();
|
|
445
620
|
if (!value)
|
|
446
621
|
return;
|
|
622
|
+
bubbleDictation.stop();
|
|
623
|
+
syncMicButton();
|
|
447
624
|
haptics.trigger('send');
|
|
448
625
|
textInput.value = '';
|
|
449
626
|
// Silent turn: the reply is SHOWN in the bubble (not spoken), and the bubble
|
|
450
627
|
// STAYS OPEN. This is the unified, reference-matching behaviour.
|
|
451
628
|
void controller.submitTextTurn(value);
|
|
452
629
|
}
|
|
453
|
-
function openTextPanel() {
|
|
454
|
-
|
|
630
|
+
function openTextPanel(seed, autoSend = false) {
|
|
631
|
+
// Opening the keyboard cancels any in-flight turn; a cancelled message
|
|
632
|
+
// becomes the editable input content unless an explicit seed was given.
|
|
633
|
+
const cancelled = controller.cancelAll();
|
|
634
|
+
const explicitSeed = seed?.trim() ?? '';
|
|
635
|
+
const draft = explicitSeed || cancelled || '';
|
|
636
|
+
textInput.value = draft;
|
|
455
637
|
setBubble(true);
|
|
638
|
+
// Only an explicit seed may auto-send; a restored cancelled message must
|
|
639
|
+
// wait for the user to edit/resend it.
|
|
640
|
+
if (autoSend && explicitSeed) {
|
|
641
|
+
queueMicrotask(() => submitText());
|
|
642
|
+
}
|
|
456
643
|
}
|
|
457
644
|
// ── Keyboard chip (rebuilt when theme changes / visibility toggles) ──────────
|
|
458
645
|
// Appears beside the ACTIVE orb — whether listening for real (voice granted)
|
|
@@ -490,26 +677,62 @@ function mountOrb(controller, options = {}) {
|
|
|
490
677
|
function resolveCurrentBubblePrompt() {
|
|
491
678
|
return (0, core_1.resolveBubblePrompt)((0, core_1.matchRouteIntent)(currentRoute, controller.config.manifest.routes), controller.messages, { locale: controller.config.locale, actions: controller.config.manifest.actions });
|
|
492
679
|
}
|
|
680
|
+
// Render the CTA on ONE line at its NATURAL size. When the phrase is too long
|
|
681
|
+
// to fit the max width, scroll it (marquee, right-to-left, looping) instead of
|
|
682
|
+
// shrinking the font or truncating — the bubble keeps a constant height.
|
|
683
|
+
function layoutThoughtContent(prompt) {
|
|
684
|
+
const t = bubbleTheme;
|
|
685
|
+
const { padH } = (0, core_1.resolveBubblePadding)(t);
|
|
686
|
+
// Measure the natural (single-copy) text width at the base font size.
|
|
687
|
+
thoughtBubbleEl.style.width = '';
|
|
688
|
+
thoughtBubbleEl.textContent = prompt;
|
|
689
|
+
const measured = thoughtBubbleEl.scrollWidth;
|
|
690
|
+
const viewport = typeof window !== 'undefined' ? window.innerWidth : 280;
|
|
691
|
+
const maxW = Math.min(280, viewport * 0.7);
|
|
692
|
+
const overflow = measured > maxW - t.borderWidth * 2;
|
|
693
|
+
if (!overflow) {
|
|
694
|
+
thoughtBubbleEl.style.width = '';
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const textWidth = Math.max(0, measured - padH * 2);
|
|
698
|
+
const durationSec = Math.max(4, (textWidth + 48) / 30);
|
|
699
|
+
thoughtBubbleEl.style.width = `${maxW}px`;
|
|
700
|
+
thoughtBubbleEl.textContent = '';
|
|
701
|
+
const track = document.createElement('div');
|
|
702
|
+
track.className = 'fyodos-thought-track';
|
|
703
|
+
track.style.animation = `fyodos-marquee ${durationSec}s linear infinite`;
|
|
704
|
+
const first = document.createElement('span');
|
|
705
|
+
first.textContent = prompt;
|
|
706
|
+
const second = document.createElement('span');
|
|
707
|
+
second.textContent = prompt;
|
|
708
|
+
second.setAttribute('aria-hidden', 'true');
|
|
709
|
+
track.appendChild(first);
|
|
710
|
+
track.appendChild(second);
|
|
711
|
+
thoughtBubbleEl.appendChild(track);
|
|
712
|
+
}
|
|
493
713
|
function applyThoughtBubbleTheme() {
|
|
494
714
|
const t = bubbleTheme;
|
|
495
|
-
const { fontPx,
|
|
496
|
-
const
|
|
715
|
+
const { fontPx, padH, approxHeight } = (0, core_1.resolveBubblePadding)(t);
|
|
716
|
+
const shapeCss = (0, core_1.bubbleShapeToCss)(t, approxHeight);
|
|
497
717
|
thoughtBubbleEl.style.fontSize = `${fontPx}px`;
|
|
498
|
-
thoughtBubbleEl.style.
|
|
718
|
+
thoughtBubbleEl.style.height = `${approxHeight}px`;
|
|
719
|
+
thoughtBubbleEl.style.padding = `0 ${padH}px`;
|
|
499
720
|
thoughtBubbleEl.style.color = t.textColor;
|
|
500
721
|
thoughtBubbleEl.style.background = t.backgroundColor;
|
|
501
722
|
thoughtBubbleEl.style.border = `${t.borderWidth}px solid ${t.borderColor}`;
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
723
|
+
const setImportant = (prop, value) => {
|
|
724
|
+
if (!value) {
|
|
725
|
+
thoughtBubbleEl.style.removeProperty(prop);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
thoughtBubbleEl.style.setProperty(prop, value, 'important');
|
|
729
|
+
};
|
|
730
|
+
setImportant('border-radius', shapeCss.borderRadius);
|
|
731
|
+
setImportant('border-top-left-radius', shapeCss.borderTopLeftRadius);
|
|
732
|
+
setImportant('border-top-right-radius', shapeCss.borderTopRightRadius);
|
|
733
|
+
setImportant('border-bottom-left-radius', shapeCss.borderBottomLeftRadius);
|
|
734
|
+
setImportant('border-bottom-right-radius', shapeCss.borderBottomRightRadius);
|
|
735
|
+
setImportant('clip-path', shapeCss.clipPath);
|
|
513
736
|
}
|
|
514
737
|
function positionThoughtBubble() {
|
|
515
738
|
const dep = (0, core_1.resolveOrbDeployment)(effectivePosition());
|
|
@@ -539,11 +762,12 @@ function mountOrb(controller, options = {}) {
|
|
|
539
762
|
thoughtBubbleEl.style.display = 'none';
|
|
540
763
|
return;
|
|
541
764
|
}
|
|
542
|
-
thoughtBubbleEl.
|
|
543
|
-
|
|
765
|
+
thoughtBubbleEl.setAttribute('aria-label', prompt);
|
|
766
|
+
// Display before measuring so `scrollWidth` (used to detect overflow) is real.
|
|
767
|
+
thoughtBubbleEl.style.display = 'flex';
|
|
544
768
|
applyThoughtBubbleTheme();
|
|
769
|
+
layoutThoughtContent(prompt);
|
|
545
770
|
positionThoughtBubble();
|
|
546
|
-
thoughtBubbleEl.style.display = 'block';
|
|
547
771
|
}
|
|
548
772
|
// ── Overlays (confirmation + consent) ────────────────────────────────────────
|
|
549
773
|
let overlayEl = null;
|
|
@@ -649,7 +873,12 @@ function mountOrb(controller, options = {}) {
|
|
|
649
873
|
// (aborts the request, no reply is delivered) and returns the orb to idle.
|
|
650
874
|
if (state.showThinking) {
|
|
651
875
|
haptics.trigger('orbTap');
|
|
652
|
-
controller.cancelAll();
|
|
876
|
+
const draft = controller.cancelAll();
|
|
877
|
+
// The cancelled message goes back into the input for editing.
|
|
878
|
+
if (draft && bubbleOpen) {
|
|
879
|
+
textInput.value = draft;
|
|
880
|
+
(0, bubbleDictation_1.scrollTextInputToEnd)(textInput);
|
|
881
|
+
}
|
|
653
882
|
return;
|
|
654
883
|
}
|
|
655
884
|
haptics.trigger('orbTap');
|
|
@@ -683,10 +912,17 @@ function mountOrb(controller, options = {}) {
|
|
|
683
912
|
};
|
|
684
913
|
sendBtn.onclick = () => {
|
|
685
914
|
// Send (arrow) while idle; Stop (square) while a turn is processing — tapping
|
|
686
|
-
// it cancels the in-flight turn so the user can type
|
|
687
|
-
|
|
915
|
+
// it cancels the in-flight turn (or running chain) so the user can type again.
|
|
916
|
+
const st = controller.getState();
|
|
917
|
+
if (st.isBusy || st.chainActive) {
|
|
688
918
|
haptics.trigger('orbTap');
|
|
689
|
-
|
|
919
|
+
// STOP: the cancelled message leaves the panel and goes back into the
|
|
920
|
+
// input so the user can edit and resend it.
|
|
921
|
+
const draft = controller.cancelAll();
|
|
922
|
+
if (draft) {
|
|
923
|
+
textInput.value = draft;
|
|
924
|
+
(0, bubbleDictation_1.scrollTextInputToEnd)(textInput);
|
|
925
|
+
}
|
|
690
926
|
textInput.focus();
|
|
691
927
|
return;
|
|
692
928
|
}
|
|
@@ -698,6 +934,7 @@ function mountOrb(controller, options = {}) {
|
|
|
698
934
|
if (e.key === 'Escape')
|
|
699
935
|
setBubble(false);
|
|
700
936
|
};
|
|
937
|
+
textInput.addEventListener('input', () => (0, bubbleDictation_1.scrollTextInputToEnd)(textInput));
|
|
701
938
|
if (!enableTextInput) {
|
|
702
939
|
if (chipEl)
|
|
703
940
|
chipEl.style.display = 'none';
|
|
@@ -740,20 +977,28 @@ function mountOrb(controller, options = {}) {
|
|
|
740
977
|
? 'speaking'
|
|
741
978
|
: 'idle';
|
|
742
979
|
orbVisual.applyState(visual, state.volumeLevel);
|
|
743
|
-
//
|
|
980
|
+
// Busy through the WHOLE task: single turns and every autonomous chain
|
|
981
|
+
// step (so the real narration never flickers between steps).
|
|
982
|
+
const rowBusy = state.showThinking || state.chainActive;
|
|
983
|
+
// REAL activity line: updates when the agent actually does something new,
|
|
984
|
+
// and clears when the reply lands.
|
|
985
|
+
syncActivityStep(rowBusy, state);
|
|
986
|
+
// Text bubble: show recent exchanges + busy state.
|
|
744
987
|
if (bubbleOpen) {
|
|
745
|
-
|
|
988
|
+
loaderRow.style.display = state.loadingOlderExchanges ? 'flex' : 'none';
|
|
989
|
+
renderBubbleExchanges(state.recentExchanges);
|
|
746
990
|
exchangeScroll.style.display = 'flex';
|
|
747
|
-
|
|
748
|
-
replyLine.textContent = ex?.replyText ?? '';
|
|
749
|
-
replyLine.style.display = ex?.replyText ? 'block' : 'none';
|
|
750
|
-
busyRow.style.display = state.showThinking ? 'flex' : 'none';
|
|
991
|
+
busyRow.style.display = rowBusy ? 'flex' : 'none';
|
|
751
992
|
// Send (arrow) ↔ Stop (square): while busy the action becomes "cancel".
|
|
752
|
-
|
|
993
|
+
// A running autonomous chain counts as busy so the user can interrupt it.
|
|
994
|
+
const busy = state.isBusy || state.chainActive;
|
|
753
995
|
sendBtn.textContent = busy ? '◼' : '↑';
|
|
754
996
|
sendBtn.setAttribute('aria-label', busy ? ui.cancelLabel : ui.sendLabel);
|
|
755
997
|
sendBtn.style.opacity = '1';
|
|
756
998
|
sendBtn.disabled = false;
|
|
999
|
+
micBtn.style.opacity = busy ? '0.45' : '1';
|
|
1000
|
+
micBtn.disabled = busy;
|
|
1001
|
+
syncMicButton();
|
|
757
1002
|
}
|
|
758
1003
|
syncChip(state);
|
|
759
1004
|
syncThoughtBubble(state);
|
|
@@ -801,7 +1046,11 @@ function mountOrb(controller, options = {}) {
|
|
|
801
1046
|
// Dashboard master switch: hide the whole orb (button + chip + bubble live
|
|
802
1047
|
// inside `root`) when explicitly turned off, show it otherwise. No host
|
|
803
1048
|
// code change — it flips on the next poll and reappears when re-enabled.
|
|
804
|
-
|
|
1049
|
+
// Internal-profile orbs additionally hide on public (non-development)
|
|
1050
|
+
// hosts: they are for the developer/company, never for end users.
|
|
1051
|
+
const hiddenByProfile = config != null &&
|
|
1052
|
+
(0, core_1.shouldHideInternalOrb)(config.internalProfile, options.allowInternalOrb === true);
|
|
1053
|
+
root.style.display = (config && config.enabled === false) || hiddenByProfile ? 'none' : '';
|
|
805
1054
|
if (config) {
|
|
806
1055
|
publishedAppearance = config.appearance;
|
|
807
1056
|
chipTheme = config.keyboardChip;
|
|
@@ -809,6 +1058,10 @@ function mountOrb(controller, options = {}) {
|
|
|
809
1058
|
bubbleTheme = config.bubbleTheme;
|
|
810
1059
|
bubbleEnabled = config.bubbleEnabled;
|
|
811
1060
|
voiceInputEnabled = config.voiceInputEnabled;
|
|
1061
|
+
thinkingStepsEnabled = config.thinkingStepsEnabled;
|
|
1062
|
+
// Idle context retention chosen in the dashboard (0 = never clear) +
|
|
1063
|
+
// expanded memory window when the project's orb profile is 'internal'.
|
|
1064
|
+
controller.setConversationRetention(config.conversationRetentionMinutes, config.internalProfile);
|
|
812
1065
|
if (usePublishedPosition && config.screenPosition)
|
|
813
1066
|
screenPosition = config.screenPosition;
|
|
814
1067
|
}
|
|
@@ -826,6 +1079,7 @@ function mountOrb(controller, options = {}) {
|
|
|
826
1079
|
});
|
|
827
1080
|
return {
|
|
828
1081
|
unmount() {
|
|
1082
|
+
bubbleDictation.dispose();
|
|
829
1083
|
unsubscribe();
|
|
830
1084
|
stopWatch();
|
|
831
1085
|
window.clearInterval(routeTimer);
|
|
@@ -16,6 +16,8 @@ export interface OrbAppearance {
|
|
|
16
16
|
glowColor?: string;
|
|
17
17
|
colorKey?: string;
|
|
18
18
|
glowIntensity?: number;
|
|
19
|
+
/** 0–100 orb vibration/haptic strength (dashboard advanced option). */
|
|
20
|
+
hapticIntensity?: number;
|
|
19
21
|
innerBorderColor?: string;
|
|
20
22
|
innerBorderWidth?: number;
|
|
21
23
|
size: number;
|
|
@@ -27,12 +27,30 @@ export interface ResolvedOrbConfig {
|
|
|
27
27
|
* directly on tap and never plays TTS. Default true (absent = voice on).
|
|
28
28
|
*/
|
|
29
29
|
voiceInputEnabled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Dashboard on/off for the agent's live "thinking steps" in the chat panel
|
|
32
|
+
* (`agentThinkingStepsEnabled`). True ONLY when the developer turned it on;
|
|
33
|
+
* then the busy row shows what the agent is doing instead of only the dots.
|
|
34
|
+
* Default false (absent = dots only).
|
|
35
|
+
*/
|
|
36
|
+
thinkingStepsEnabled: boolean;
|
|
30
37
|
/**
|
|
31
38
|
* Dashboard master visibility switch (`orbEnabled`). False ONLY when the
|
|
32
39
|
* developer explicitly turned the orb off. The orb is hidden when false and
|
|
33
40
|
* shown otherwise (including when nothing is published / on error).
|
|
34
41
|
*/
|
|
35
42
|
enabled: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Idle context retention in minutes (`conversationRetentionMinutes`).
|
|
45
|
+
* 0 = never clear; null = not published (the SDK's 30-minute default).
|
|
46
|
+
*/
|
|
47
|
+
conversationRetentionMinutes: number | null;
|
|
48
|
+
/**
|
|
49
|
+
* True when the project's orb profile is 'internal' (internal/developer
|
|
50
|
+
* software): the agent uses the EXPANDED conversation memory window.
|
|
51
|
+
* Default false ('public' — today's behavior unchanged).
|
|
52
|
+
*/
|
|
53
|
+
internalProfile: boolean;
|
|
36
54
|
}
|
|
37
55
|
export interface WatchPublishedConfigOptions {
|
|
38
56
|
/** Poll interval (ms) to pick up dashboard changes in real time. Default 12000. */
|