@pouchy_ai/companion-sdk 0.40.1 → 0.42.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/CHANGELOG.md +55 -0
- package/README.md +16 -2
- package/dist/a2ui.d.ts +216 -0
- package/dist/a2ui.d.ts.map +1 -0
- package/dist/a2ui.js +622 -0
- package/dist/a2ui.js.map +1 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +26 -4
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/a2ui.ts +881 -0
- package/src/client.ts +49 -5
- package/src/index.ts +15 -0
package/src/client.ts
CHANGED
|
@@ -221,6 +221,12 @@ export type CallCredentials =
|
|
|
221
221
|
/** First-message override ('' = suppress the canned greeting so the call
|
|
222
222
|
* opens in-character / context-aware). Pass as overrides.agent.firstMessage. */
|
|
223
223
|
firstMessage?: string;
|
|
224
|
+
/** Generation handle of the call-active window this mint stamped (0.41.0).
|
|
225
|
+
* Self-plumbed integrations: pass it to endSession({ callGen }) at call
|
|
226
|
+
* end so the server clears exactly this call's window — a stale handle's
|
|
227
|
+
* late teardown then can't clear a newer call's window. connectCall()
|
|
228
|
+
* handles this automatically. */
|
|
229
|
+
callGen?: string;
|
|
224
230
|
}
|
|
225
231
|
| {
|
|
226
232
|
provider: 'openai-realtime';
|
|
@@ -229,6 +235,8 @@ export type CallCredentials =
|
|
|
229
235
|
model: string;
|
|
230
236
|
voice: string;
|
|
231
237
|
expiresAt: number | null;
|
|
238
|
+
/** See the elevenlabs-convai branch — same window-generation handle. */
|
|
239
|
+
callGen?: string;
|
|
232
240
|
};
|
|
233
241
|
|
|
234
242
|
export interface RecalledMemory {
|
|
@@ -460,6 +468,12 @@ export class CompanionClient {
|
|
|
460
468
|
private readonly doFetch: typeof fetch;
|
|
461
469
|
private _session: string | null = null;
|
|
462
470
|
private cursor = 0;
|
|
471
|
+
/** Generation handle of the last call-active window THIS client minted
|
|
472
|
+
* (startCall → server callGen, 0.41.0). endSession sends it by default so
|
|
473
|
+
* the server's window clear is fenced to our own call — and sends an
|
|
474
|
+
* explicit null when this client never minted one, so a text client's
|
|
475
|
+
* close() can no longer kill a live call's window on the same session. */
|
|
476
|
+
private lastCallGen: string | null = null;
|
|
463
477
|
/** Pause-orphaned call replay fired (0.37.0) — once per client instance, so
|
|
464
478
|
* a manual second connect() can't double-deliver to a live handler. */
|
|
465
479
|
private replayedPendingToolCalls = false;
|
|
@@ -1442,7 +1456,13 @@ export class CompanionClient {
|
|
|
1442
1456
|
async sendWorldState(
|
|
1443
1457
|
event: WorldStateInput | WorldStateInput[],
|
|
1444
1458
|
opts?: { signal?: AbortSignal }
|
|
1445
|
-
): Promise<{
|
|
1459
|
+
): Promise<{
|
|
1460
|
+
accepted: number;
|
|
1461
|
+
dropped: number;
|
|
1462
|
+
injected: number;
|
|
1463
|
+
reacted: boolean;
|
|
1464
|
+
duplicates: number;
|
|
1465
|
+
}> {
|
|
1446
1466
|
const id = this.requireSession();
|
|
1447
1467
|
const fill = (e: WorldStateInput): WorldStateEvent => ({
|
|
1448
1468
|
...e,
|
|
@@ -1456,14 +1476,20 @@ export class CompanionClient {
|
|
|
1456
1476
|
dropped: number;
|
|
1457
1477
|
injected?: number;
|
|
1458
1478
|
reacted?: boolean;
|
|
1479
|
+
duplicates?: number;
|
|
1459
1480
|
}>(`/api/companion/session/${id}/context`, body, opts?.signal);
|
|
1460
1481
|
// injected/reacted tell the host a live-call inject or a proactive text
|
|
1461
1482
|
// reaction was triggered by this batch (0.28.0 — previously discarded).
|
|
1483
|
+
// duplicates (0.41.0) is how much of `accepted` was replay dedup: the
|
|
1484
|
+
// server is envelope-id idempotent, so stamp stable `id`s on host-level
|
|
1485
|
+
// retries and a re-sent batch folds nothing while reporting the same
|
|
1486
|
+
// accepted count.
|
|
1462
1487
|
return {
|
|
1463
1488
|
accepted: json.accepted ?? 0,
|
|
1464
1489
|
dropped: json.dropped ?? 0,
|
|
1465
1490
|
injected: json.injected ?? 0,
|
|
1466
|
-
reacted: json.reacted === true
|
|
1491
|
+
reacted: json.reacted === true,
|
|
1492
|
+
duplicates: json.duplicates ?? 0
|
|
1467
1493
|
};
|
|
1468
1494
|
}
|
|
1469
1495
|
|
|
@@ -1487,11 +1513,15 @@ export class CompanionClient {
|
|
|
1487
1513
|
opts?: { voice?: string; locale?: string; signal?: AbortSignal }
|
|
1488
1514
|
): Promise<CallCredentials> {
|
|
1489
1515
|
const id = this.requireSession();
|
|
1490
|
-
|
|
1516
|
+
const creds = await this.postJson<CallCredentials>(
|
|
1491
1517
|
`/api/companion/session/${id}/call`,
|
|
1492
1518
|
{ voice: opts?.voice, locale: opts?.locale },
|
|
1493
1519
|
opts?.signal
|
|
1494
1520
|
);
|
|
1521
|
+
// Remember the minted window's generation so a later endSession()/close()
|
|
1522
|
+
// (without an explicit callGen) fences its clear to OUR call (0.41.0).
|
|
1523
|
+
this.lastCallGen = creds.callGen ?? null;
|
|
1524
|
+
return creds;
|
|
1495
1525
|
}
|
|
1496
1526
|
|
|
1497
1527
|
/** Open a live voice call end-to-end: mints credentials (startCall) and opens a
|
|
@@ -1515,12 +1545,17 @@ export class CompanionClient {
|
|
|
1515
1545
|
// assigned after the call opens; a connect-phase onError finds the no-op.
|
|
1516
1546
|
let unsub: () => void = () => {};
|
|
1517
1547
|
let cleaned = false;
|
|
1548
|
+
// Captured PER HANDLE at mint time (0.41.0): a second connectCall on this
|
|
1549
|
+
// client overwrites lastCallGen, so the first handle's (possibly late)
|
|
1550
|
+
// teardown must carry the gen of ITS OWN mint — the server then no-ops the
|
|
1551
|
+
// clear instead of silencing the new call's window (CR-142 leg d).
|
|
1552
|
+
let mintedGen: string | null = null;
|
|
1518
1553
|
const cleanup = () => {
|
|
1519
1554
|
if (cleaned) return;
|
|
1520
1555
|
cleaned = true;
|
|
1521
1556
|
unsub();
|
|
1522
1557
|
// Consolidate this call into durable memory (best-effort, keepalive).
|
|
1523
|
-
void this.endSession({ transcript });
|
|
1558
|
+
void this.endSession({ transcript, callGen: mintedGen });
|
|
1524
1559
|
};
|
|
1525
1560
|
const wrappedOpts: CompanionCallOptions = {
|
|
1526
1561
|
...opts,
|
|
@@ -1563,6 +1598,7 @@ export class CompanionClient {
|
|
|
1563
1598
|
: undefined;
|
|
1564
1599
|
|
|
1565
1600
|
const creds = await this.startCall({ voice: opts?.voice, locale: opts?.locale });
|
|
1601
|
+
mintedGen = creds.callGen ?? null;
|
|
1566
1602
|
let call: CompanionCall;
|
|
1567
1603
|
try {
|
|
1568
1604
|
call = await openCompanionCall(creds, wrappedOpts, bridge);
|
|
@@ -1612,9 +1648,17 @@ export class CompanionClient {
|
|
|
1612
1648
|
* beacon — harmless). `facts` is how many memories were consolidated. */
|
|
1613
1649
|
async endSession(opts?: {
|
|
1614
1650
|
transcript?: { role: string; text: string }[];
|
|
1651
|
+
/** Which call-active window this teardown owns (0.41.0). A string fences
|
|
1652
|
+
* the server's window clear to that call's mint; `null` says "I hold no
|
|
1653
|
+
* call — leave any live window alone". Defaults to the gen of the last
|
|
1654
|
+
* call THIS client minted (null if none), so a text client's close() no
|
|
1655
|
+
* longer kills a live call's window on the same session. Self-plumbed
|
|
1656
|
+
* integrations pass startCall's `creds.callGen` here at call end. */
|
|
1657
|
+
callGen?: string | null;
|
|
1615
1658
|
}): Promise<EndSessionResult | null> {
|
|
1616
1659
|
const id = this._session;
|
|
1617
1660
|
if (!id) return null;
|
|
1661
|
+
const callGen = opts?.callGen !== undefined ? opts.callGen : this.lastCallGen;
|
|
1618
1662
|
// Cap the payload (fetch keepalive bodies are size-limited): last 60 lines.
|
|
1619
1663
|
const transcript = (opts?.transcript ?? [])
|
|
1620
1664
|
.filter((t) => t && typeof t.text === 'string' && t.text.trim())
|
|
@@ -1628,7 +1672,7 @@ export class CompanionClient {
|
|
|
1628
1672
|
const res = await this.fetchAuthed(this.url(`/api/companion/session/${id}/end`), {
|
|
1629
1673
|
method: 'POST',
|
|
1630
1674
|
headers: this.jsonHeaders(),
|
|
1631
|
-
body: JSON.stringify({ transcript }),
|
|
1675
|
+
body: JSON.stringify({ transcript, callGen }),
|
|
1632
1676
|
keepalive: true
|
|
1633
1677
|
});
|
|
1634
1678
|
return (await res.json().catch(() => null)) as EndSessionResult | null;
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,21 @@ export {
|
|
|
91
91
|
} from './protocol';
|
|
92
92
|
export type { CompanionErrorCode, ControlErrorCode } from './protocol';
|
|
93
93
|
|
|
94
|
+
// Instant UI ⇄ Google A2UI v0.9 projection (0.42.0) — put `toA2UI` in front of
|
|
95
|
+
// any renderer built against the A2UI catalog and it draws Pouchy panels;
|
|
96
|
+
// `fromA2UI` ingests an A2UI surface back into the Instant UI panel shape.
|
|
97
|
+
// The node-type catalog rides along as data for renderer capability checks.
|
|
98
|
+
export { toA2UI, fromA2UI, INSTANT_UI_NODE_TYPES } from './a2ui';
|
|
99
|
+
export type {
|
|
100
|
+
A2UISurface,
|
|
101
|
+
A2UIComponent,
|
|
102
|
+
A2UIValue,
|
|
103
|
+
InstantUIInput,
|
|
104
|
+
DynamicInterface,
|
|
105
|
+
GenuiNode,
|
|
106
|
+
EmbeddedAction
|
|
107
|
+
} from './a2ui';
|
|
108
|
+
|
|
94
109
|
// The framework-agnostic view controller behind the /svelte and /vue subpath
|
|
95
110
|
// adapters (and the separate `@pouchy_ai/react` package) — exported from the
|
|
96
111
|
// root too, so a host on any OTHER framework (or none) can reuse the same
|