useComposer
useComposer(selector) は hosted composer-panel 内だけで利用でき、他では throw します。@sota/core/hooks から import し、component が必要な field だけを select します。
| Field | Contract |
|---|---|
value | Current draft の plain-text view。 |
content | Structured text と app-reference node。 |
references | Resolved slash、conversation、lane-scoped app reference。 |
pendingToolResults | Panel の exact app execution に属する deferred tool result。 |
applyEdit(edit) | replace、insert-at-cursor、append の undoable transaction。 |
focus() | Composer editor へ focus。 |
acquireLock() | 保持中は send、draft edit、既存 message edit、queued auto-send を disable し、idempotent release function を返します。 |
import { useEffect } from 'react';
import { useComposer } from '@sota/core/hooks';
export function PendingActionPanel() {
const pending = useComposer((composer) => composer.pendingToolResults);
const acquireLock = useComposer((composer) => composer.acquireLock);
const active = pending.find((item) => item.toolName === 'readCalendar');
useEffect(() => {
if (!active) return;
return acquireLock();
}, [acquireLock, active]);
if (!active) return null;
return <p>Complete the app action to continue.</p>;
}Lock は reference-counted です。Core は surface unmount 時に自動 release しますが、effect から release function を返してください。Plain value から再構築せず structured content を保ちます。
import { useComposer } from '@sota/core/hooks';
export function InsertReportButton() {
const applyEdit = useComposer((composer) => composer.applyEdit);
return <button type="button" onClick={() => applyEdit({
mode: 'insert-at-cursor',
content: [
{ kind: 'text', text: 'Review ' },
{
kind: 'app-reference',
referenceType: 'report',
referenceId: 'report-42',
label: 'Q3 report',
fallbackText: 'Q3 report',
},
],
})}>Insert report</button>;
}pendingToolResults は operationId、toolCallId、canonical toolName、modelToolName、optional opaque data、exact execution を持ちます。App data は runtime で narrow します。pendingToolResults/acquireLock は platform contract 1.3.0 以降が必要です。