@volter/supercode-terminal 0.2.62 → 0.2.64
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/index.d.ts +1 -1
- package/index.mjs +30 -7
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -253,6 +253,6 @@ export type { TerminalInputDelivery, TmuxCommand, TmuxWindowSize };
|
|
|
253
253
|
|
|
254
254
|
/** The bounded live composer text, or null for an unrecognized screen. */
|
|
255
255
|
export function composerText(kind: string, screen: string | undefined): string | null;
|
|
256
|
-
export function restoreComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string): Promise<{ restored: boolean; reason?: string; remaining?: string }>;
|
|
256
|
+
export function restoreComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string, options?: { pasted?: string | null }): Promise<{ restored: boolean; reason?: string; remaining?: string }>;
|
|
257
257
|
/** Failed input is restored or returns an explicit cleanup obligation; never blindly resend. */
|
|
258
258
|
export function deliverComposerInput(host: TmuxTerminalHost | PtyTerminalHost | PaneServerHost, sessionId: string, kind: string, text: string, options?: { deadlineMs?: number; signal?: AbortSignal }): Promise<{ delivered: boolean; reason?: string; restored?: boolean; cleanup_required?: boolean; cleanup_reason?: string; cleanup_remaining?: string; delivery?: TerminalInputDelivery }>;
|
package/index.mjs
CHANGED
|
@@ -1843,13 +1843,27 @@ function composerMatches(value, wanted) {
|
|
|
1843
1843
|
return offsets.has(wanted.length);
|
|
1844
1844
|
}
|
|
1845
1845
|
|
|
1846
|
+
/** Claude collapses a multi-line paste to `[Pasted text #N +K lines]`, and its composer then shows that placeholder,
|
|
1847
|
+
* never the text. The placeholder (as shown) when it can stand for `text`: K is the pasted text's line breaks (the
|
|
1848
|
+
* trimmed text's, or the sent payload's with its closing newline). Which paste it is, #N, only the paste itself
|
|
1849
|
+
* tells: a caller holds the placeholder its own paste produced and accepts no other. */
|
|
1850
|
+
function pastePlaceholder(composer, text) {
|
|
1851
|
+
const shown=String(composer??'').trim();
|
|
1852
|
+
const placeholder=/^\[Pasted text #\d+ \+(\d+) lines?\]$/u.exec(shown);
|
|
1853
|
+
if(!placeholder)return null;
|
|
1854
|
+
const breaks=value=>(value.match(/\n/gu)??[]).length;
|
|
1855
|
+
return [breaks(String(text).trim()),breaks(`${text}\n`)].includes(Number(placeholder[1]))?shown:null;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1846
1858
|
/** Undo only text this input wrote, including when a turn started meanwhile.
|
|
1847
|
-
* Never Enter, Escape, or Ctrl-C: restoration must not submit or stop work.
|
|
1848
|
-
|
|
1859
|
+
* Never Enter, Escape, or Ctrl-C: restoration must not submit or stop work.
|
|
1860
|
+
* `pasted`: the `[Pasted text #N …]` placeholder this input's own paste produced. Only that one is cleared: another
|
|
1861
|
+
* of the same size is a person's own paste. */
|
|
1862
|
+
export async function restoreComposerInput(host, sessionId, kind, text, { pasted = null } = {}) {
|
|
1849
1863
|
const read=async()=>composerText(kind,await host.capture(sessionId,{preserveEscapes:true}));
|
|
1850
1864
|
let remaining=await read();
|
|
1851
1865
|
if(remaining==='')return {restored:true};
|
|
1852
|
-
if(!composerMatches(remaining,String(text).trim()))return {restored:false,reason:'composer-changed-or-unreadable'};
|
|
1866
|
+
if(!composerMatches(remaining,String(text).trim())&&!(pasted&&remaining.trim()===pasted))return {restored:false,reason:'composer-changed-or-unreadable'};
|
|
1853
1867
|
const lines=remaining.split('\n').length+2;
|
|
1854
1868
|
for(let line=0;line<lines;line++) {
|
|
1855
1869
|
if(await read()!==remaining)return {restored:false,reason:'composer-changed',remaining};
|
|
@@ -1876,14 +1890,23 @@ export async function deliverComposerInput(host, sessionId, kind, text, { deadli
|
|
|
1876
1890
|
const read=async()=>composerText(kind,await capture());
|
|
1877
1891
|
const expired=()=>signal?.aborted||Date.now()>=deadlineMs;
|
|
1878
1892
|
let touched=false;
|
|
1893
|
+
// The placeholder this paste produced: the first one seen after pasting into a composer read empty twice. From then
|
|
1894
|
+
// on only that one (its #N) is this text, and only that one is restored away.
|
|
1895
|
+
let pasted=null;
|
|
1879
1896
|
const failed=async (reason, evidence={})=>{
|
|
1880
1897
|
if(!touched)return {delivered:false,reason,...evidence};
|
|
1881
1898
|
let restored;
|
|
1882
|
-
try{restored=await restoreComposerInput(host,sessionId,kind,text);}catch(error){restored={restored:false,reason:error.message};}
|
|
1899
|
+
try{restored=await restoreComposerInput(host,sessionId,kind,text,{pasted});}catch(error){restored={restored:false,reason:error.message};}
|
|
1883
1900
|
return {delivered:false,reason,...evidence,restored:restored.restored,cleanup_required:!restored.restored,cleanup_reason:restored.reason,cleanup_remaining:restored.remaining};
|
|
1884
1901
|
};
|
|
1885
1902
|
if(expired())return failed('deadline');
|
|
1886
1903
|
const wanted=String(text).trim();
|
|
1904
|
+
const holds=composer=>{
|
|
1905
|
+
if(composerMatches(composer,wanted))return true;
|
|
1906
|
+
const placeholder=pastePlaceholder(composer,text);
|
|
1907
|
+
if(placeholder&&touched&&pasted===null)pasted=placeholder;
|
|
1908
|
+
return placeholder!==null&&placeholder===pasted;
|
|
1909
|
+
};
|
|
1887
1910
|
if(!wanted||/[\x00-\x08\x0b-\x1f\x7f]/u.test(text))return failed('unsupported-text');
|
|
1888
1911
|
for(let check=0;check<2;check++) {
|
|
1889
1912
|
const screen=await capture(),before=composerText(kind,screen);
|
|
@@ -1897,20 +1920,20 @@ export async function deliverComposerInput(host, sessionId, kind, text, { deadli
|
|
|
1897
1920
|
let pasted=false;
|
|
1898
1921
|
for(let attempt=0;attempt<10&&!expired();attempt++) {
|
|
1899
1922
|
await sleep(100);
|
|
1900
|
-
if(
|
|
1923
|
+
if(holds(await read())){pasted=true;break;}
|
|
1901
1924
|
}
|
|
1902
1925
|
if(!pasted)return failed('paste-unconfirmed');
|
|
1903
1926
|
for(let attempt=0;attempt<3;attempt++) {
|
|
1904
1927
|
if(expired())return failed('deadline');
|
|
1905
1928
|
const screen=await capture();
|
|
1906
|
-
if(!
|
|
1929
|
+
if(!holds(composerText(kind,screen)))return failed('composer-changed');
|
|
1907
1930
|
if(expired())return failed('deadline');
|
|
1908
1931
|
await host.sendInput(sessionId,'\r',{submitMode:'single',deadlineMs});
|
|
1909
1932
|
for(let poll=0;poll<10&&!expired();poll++) {
|
|
1910
1933
|
await sleep(100);
|
|
1911
1934
|
const current=await read();
|
|
1912
1935
|
if(current==='')return kind === 'claude-code' ? {delivered:true,delivery,path:'composer_submit'} : {delivered:false,reason:'native-transcript-confirmation-required',delivery};
|
|
1913
|
-
if(!
|
|
1936
|
+
if(!holds(current))return failed('submission-unconfirmed');
|
|
1914
1937
|
}
|
|
1915
1938
|
}
|
|
1916
1939
|
return failed('submit-unconfirmed');
|