@tyhld/conductor 0.12.52 → 0.12.53
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/relay.js +224 -38
- package/package.json +1 -1
package/dist/relay.js
CHANGED
|
@@ -1697,12 +1697,8 @@ function runTmux(args) {
|
|
|
1697
1697
|
* ★tmux の言い分(stderr)をログに出す。以前は `failed` としか出ず原因が追えなかった。
|
|
1698
1698
|
*/
|
|
1699
1699
|
export function sendToTmux(site, text) {
|
|
1700
|
-
const bufferName = `${RELAY_SEND_BUFFER_PREFIX}-${site}-${process.pid}-${Date.now()}`;
|
|
1701
|
-
let file = null;
|
|
1702
1700
|
try {
|
|
1703
|
-
|
|
1704
|
-
runTmux(['load-buffer', '-b', bufferName, file]);
|
|
1705
|
-
runTmux(['paste-buffer', '-d', '-p', '-b', bufferName, '-t', site]);
|
|
1701
|
+
pasteToTmux(site, text);
|
|
1706
1702
|
// 貼り付けの枠の外に、打った字として添え書きを置く(RELAY_DELIVERY_NOTE の説明)。
|
|
1707
1703
|
runTmux(['send-keys', '-t', site, '-l', ` ${RELAY_DELIVERY_NOTE}`]);
|
|
1708
1704
|
// ★Enter の前に一拍おく(RELAY_SUBMIT_PAUSE_MS の説明・便 b69c1608)。
|
|
@@ -1712,13 +1708,30 @@ export function sendToTmux(site, text) {
|
|
|
1712
1708
|
}
|
|
1713
1709
|
catch (err) {
|
|
1714
1710
|
log(`WARN site=${site} 本文の投入に失敗(${text.length}文字): ${describeExecError(err)}`);
|
|
1711
|
+
return false;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* 本文を一時ファイル → tmux のバッファ → 職人の窓へ貼る(貼るだけ。添え書きも Enter も送らない)。
|
|
1716
|
+
* 失敗したら例外がそのまま飛ぶ。一時ファイルは必ず消し、貼る前に転んだバッファも消す
|
|
1717
|
+
* (sendToTmux の【後始末】の説明)。
|
|
1718
|
+
*/
|
|
1719
|
+
function pasteToTmux(site, text) {
|
|
1720
|
+
const bufferName = `${RELAY_SEND_BUFFER_PREFIX}-${site}-${process.pid}-${Date.now()}`;
|
|
1721
|
+
let file = null;
|
|
1722
|
+
try {
|
|
1723
|
+
file = writeSendTmpFile(text);
|
|
1724
|
+
runTmux(['load-buffer', '-b', bufferName, file]);
|
|
1725
|
+
runTmux(['paste-buffer', '-d', '-p', '-b', bufferName, '-t', site]);
|
|
1726
|
+
}
|
|
1727
|
+
catch (err) {
|
|
1715
1728
|
try {
|
|
1716
1729
|
execFileSync('tmux', ['delete-buffer', '-b', bufferName], { stdio: 'ignore' });
|
|
1717
1730
|
}
|
|
1718
1731
|
catch {
|
|
1719
1732
|
// 貼れていれば -d で既に消えている。無いバッファを消そうとしたエラーは無視してよい。
|
|
1720
1733
|
}
|
|
1721
|
-
|
|
1734
|
+
throw err;
|
|
1722
1735
|
}
|
|
1723
1736
|
finally {
|
|
1724
1737
|
if (file !== null) {
|
|
@@ -1732,6 +1745,165 @@ export function sendToTmux(site, text) {
|
|
|
1732
1745
|
}
|
|
1733
1746
|
}
|
|
1734
1747
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1748
|
+
// 貼り付けが入力欄に入ったのを確かめてから送る(便 963ddca3・★添え書きだけが届く事故の根治)
|
|
1749
|
+
//
|
|
1750
|
+
// 【何が起きたか(実障害 2026-09-23 minoru 便 af960f79/2026-09-24 ai-kana 便 0b637256)】
|
|
1751
|
+
// 職人の会話の記録(実物)を読むと、どちらも利用者の発言として記録されたのは
|
|
1752
|
+
// 「 (配達役より)上の貼り付けは…」の75字だけで、貼り付け(本文と便印)は入力欄に一度も入って
|
|
1753
|
+
// いなかった。★配達役は貼り付けが入ったかを確かめず、続けて添え書きを打って Enter を押していた。
|
|
1754
|
+
// だから職人が貼り付けを受け取れなかった瞬間に、添え書きだけが単独で送信された。
|
|
1755
|
+
// 2件とも職人の tmux ができてから 11〜30 秒の、職人を起動した直後の最初の便だった
|
|
1756
|
+
// (同じ期間の会話を新しくした後の配達 12 件はすべて届いている)。
|
|
1757
|
+
// 【なぜ貼り付けが消えるか(試験用の tmux で本物の Claude Code 2.1.281 に当てて再現)】
|
|
1758
|
+
// 貼り付けと添え書きが【1回の読み込みでまとめて】職人に届くと、Claude Code は貼り付けを捨て、
|
|
1759
|
+
// 添え書きだけを入力欄に残す(入力欄は「 (配達役より)…」=実障害の記録と1字も違わない)。
|
|
1760
|
+
// 配達役は貼り付けの直後に添え書きを打つので、職人が起動直後で読み込みが遅れていると、両方が
|
|
1761
|
+
// たまって一度に読まれる(★ここは推定:実障害2件はどちらも起動直後の最初の便で、/clear から記録
|
|
1762
|
+
// までが 8.8・9.2 秒と、届いた便のほとんど(6.1〜7.9 秒)より遅かった。職人の読み込みの遅れそのものは
|
|
1763
|
+
// 記録に残っていない)。
|
|
1764
|
+
// そこへ1秒後の Enter が別に届き、添え書きだけが送信された。
|
|
1765
|
+
// 【根治】「貼る → 入力欄に入ったのを画面で確かめる → 添え書きを打つ → 入力欄に貼り付けと添え書きが
|
|
1766
|
+
// そろっているのを確かめる → Enter」。★そろっていなければ Enter を押さない。
|
|
1767
|
+
// =貼り付けが入らないかぎり、何も送信されない(添え書きだけが先に・単独で届く形が無い)。
|
|
1768
|
+
// 入らなければ、画面が入力待ちに戻るのを確かめてから貼り直す(上限あり)。時間で決め打ちしない。
|
|
1769
|
+
// ★入力欄に人が打ちかけた字があれば貼らない(人の入力に混ぜない)。
|
|
1770
|
+
// ★消すのは、配達役が打った添え書きの字だけ(貼り付けが入らず、添え書きだけが入力欄に残ったとき)。
|
|
1771
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
1772
|
+
/** 貼ってから入力欄に入るのを待つ長さ(1回ぶん・ミリ秒)。★待ちの上限であって、決め打ちの間ではない。 */
|
|
1773
|
+
export const RELAY_PASTE_LAND_MS = 5_000;
|
|
1774
|
+
/** 1回の配達の中で貼る回数の上限(入力欄に入らなかったときの貼り直しを含む)。 */
|
|
1775
|
+
export const RELAY_PASTE_MAX_TRIES = 3;
|
|
1776
|
+
/**
|
|
1777
|
+
* 画面のいちばん下の入力欄(最後の ❯ の行から次の罫線まで)の文字。入力欄が無ければ null。
|
|
1778
|
+
* ★折り返しは元に戻さない(呼び出し側で joinWrapped する)。
|
|
1779
|
+
*/
|
|
1780
|
+
export function inputBlock(cap) {
|
|
1781
|
+
const lines = cap.split('\n');
|
|
1782
|
+
let start = -1;
|
|
1783
|
+
for (let k = lines.length - 1; k >= 0; k--) {
|
|
1784
|
+
if (/^\s*❯/.test(lines[k])) {
|
|
1785
|
+
start = k;
|
|
1786
|
+
break;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
if (start < 0)
|
|
1790
|
+
return null;
|
|
1791
|
+
const block = [];
|
|
1792
|
+
for (let k = start; k < lines.length; k++) {
|
|
1793
|
+
if (k > start && /^─{10,}/.test(lines[k].trim()))
|
|
1794
|
+
break;
|
|
1795
|
+
block.push(lines[k]);
|
|
1796
|
+
}
|
|
1797
|
+
return block.join('\n');
|
|
1798
|
+
}
|
|
1799
|
+
/** 画面の折り返しを戻す(改行と、続きの行の頭の空白を取る)。 */
|
|
1800
|
+
function joinWrapped(s) {
|
|
1801
|
+
return s.replace(/\n[ \t]*/g, '');
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* 入力欄に、この便の貼り付けが入っているか。
|
|
1805
|
+
* ・長い貼り付けは Claude Code が `[Pasted text #1 +N lines]` と畳んで見せる(実測)。
|
|
1806
|
+
* ・短い貼り付けはそのまま見える=本文の末尾の便印(CONDUCTOR_JOB:<id>)が完全一致で在るか。
|
|
1807
|
+
*/
|
|
1808
|
+
export function hasPasteInInput(cap, commandId) {
|
|
1809
|
+
const block = inputBlock(cap);
|
|
1810
|
+
if (block === null)
|
|
1811
|
+
return false;
|
|
1812
|
+
if (/\[Pasted text #\d+/.test(block))
|
|
1813
|
+
return true;
|
|
1814
|
+
return hasExactJobStamp(joinWrapped(block), commandId);
|
|
1815
|
+
}
|
|
1816
|
+
/** 入力欄に、配達役の添え書きが(全部)入っているか。 */
|
|
1817
|
+
function hasNoteInInput(cap) {
|
|
1818
|
+
const block = inputBlock(cap);
|
|
1819
|
+
return block !== null && block.replace(/\s/g, '').includes(RELAY_DELIVERY_NOTE.replace(/\s/g, ''));
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* 入力欄が「配達役が貼ってよい」状態か:入力欄が在り、貼り付け・添え書き・便印のどれも無い。
|
|
1823
|
+
* ★人が打ちかけた字は画面の上では Claude Code の薄い字の例(Try "…")と見分けられないので、
|
|
1824
|
+
* ここでは「配達役の物が残っていない」ことだけを見る(人の打ちかけは受付の確かめが別に守る)。
|
|
1825
|
+
*/
|
|
1826
|
+
function inputIsClear(cap, commandId) {
|
|
1827
|
+
const block = inputBlock(cap);
|
|
1828
|
+
if (block === null)
|
|
1829
|
+
return false;
|
|
1830
|
+
return !hasPasteInInput(cap, commandId) && !block.includes('(配達役より)');
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* 貼り付けが入力欄に入ったのを確かめてから、添え書きを打って送る(Claude の職人向け)。
|
|
1834
|
+
* ★Enter を押すのは「入力欄に貼り付けと添え書きの両方がある」ときだけ。
|
|
1835
|
+
*/
|
|
1836
|
+
export async function deliverToTmux(site, text, commandId, d = {
|
|
1837
|
+
capture: () => tmuxCapture(site),
|
|
1838
|
+
paste: (t) => pasteToTmux(site, t),
|
|
1839
|
+
typeNote: () => runTmux(['send-keys', '-t', site, '-l', ` ${RELAY_DELIVERY_NOTE}`]),
|
|
1840
|
+
// 打った字の数だけ BSpace(先頭の空白1字を含む)。★配達役が打った字しか消さない。
|
|
1841
|
+
eraseNote: () => runTmux(['send-keys', '-t', site, '-N', String([...` ${RELAY_DELIVERY_NOTE}`].length), 'BSpace']),
|
|
1842
|
+
enter: () => runTmux(['send-keys', '-t', site, 'Enter']),
|
|
1843
|
+
pause: pauseSync,
|
|
1844
|
+
sleep,
|
|
1845
|
+
ready: () => isIdleStable(site, 'claude'),
|
|
1846
|
+
}, o = {
|
|
1847
|
+
landMs: RELAY_PASTE_LAND_MS,
|
|
1848
|
+
pollMs: 250,
|
|
1849
|
+
maxTries: RELAY_PASTE_MAX_TRIES,
|
|
1850
|
+
}) {
|
|
1851
|
+
let pastes = 0;
|
|
1852
|
+
const landed = async () => {
|
|
1853
|
+
for (let t = 0;; t += o.pollMs) {
|
|
1854
|
+
const cap = d.capture();
|
|
1855
|
+
if (cap !== null && hasPasteInInput(cap, commandId))
|
|
1856
|
+
return true;
|
|
1857
|
+
if (t >= o.landMs)
|
|
1858
|
+
return false;
|
|
1859
|
+
await d.sleep(o.pollMs);
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
try {
|
|
1863
|
+
for (let round = 0; round < o.maxTries; round++) {
|
|
1864
|
+
// ★入力を受け取れる状態(入力待ちで落ち着いた画面)を確かめてから触る。
|
|
1865
|
+
if (!(await d.ready())) {
|
|
1866
|
+
await d.sleep(o.pollMs);
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
const cap = d.capture();
|
|
1870
|
+
if (cap === null)
|
|
1871
|
+
return { result: 'failed', pastes };
|
|
1872
|
+
// ★まだ1度も貼っていないのに入力欄に貼り付けがあるなら、それが配達役の物だと言えるのは
|
|
1873
|
+
// この便の印(完全一致)が見えるときだけ。畳まれた貼り付けは誰の物か分からないので触らない。
|
|
1874
|
+
if (pastes === 0 && hasPasteInInput(cap, commandId) && !hasExactJobStamp(joinWrapped(inputBlock(cap) ?? ''), commandId)) {
|
|
1875
|
+
return { result: 'not-clear', pastes };
|
|
1876
|
+
}
|
|
1877
|
+
if (!hasPasteInInput(cap, commandId)) {
|
|
1878
|
+
// 前の回の貼り付けも入っていない。配達役の物ではない残りがあれば貼らない。
|
|
1879
|
+
if (!inputIsClear(cap, commandId))
|
|
1880
|
+
return { result: 'not-clear', pastes };
|
|
1881
|
+
d.paste(text);
|
|
1882
|
+
pastes++;
|
|
1883
|
+
if (!(await landed()))
|
|
1884
|
+
continue; // ★入らなかった=何も打たずに次の回へ
|
|
1885
|
+
}
|
|
1886
|
+
if (!hasNoteInInput(d.capture() ?? '')) {
|
|
1887
|
+
d.typeNote();
|
|
1888
|
+
d.pause(RELAY_SUBMIT_PAUSE_MS);
|
|
1889
|
+
}
|
|
1890
|
+
const before = d.capture();
|
|
1891
|
+
if (before !== null && hasPasteInInput(before, commandId) && hasNoteInInput(before)) {
|
|
1892
|
+
d.enter();
|
|
1893
|
+
return { result: 'submitted', pastes };
|
|
1894
|
+
}
|
|
1895
|
+
// ★そろっていない=Enter を押さない。添え書きだけが残っていれば、打った字だけ消す。
|
|
1896
|
+
if (before !== null && !hasPasteInInput(before, commandId) && hasNoteInInput(before))
|
|
1897
|
+
d.eraseNote();
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
catch (err) {
|
|
1901
|
+
log(`WARN site=${site} 本文の投入に失敗(${text.length}文字): ${describeExecError(err)}`);
|
|
1902
|
+
return { result: 'failed', pastes };
|
|
1903
|
+
}
|
|
1904
|
+
return { result: 'not-landed', pastes };
|
|
1905
|
+
}
|
|
1906
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
1735
1907
|
// 受付の確かめ(便 b69c1608)
|
|
1736
1908
|
//
|
|
1737
1909
|
// 【なぜ要るか】送信の Enter が職人に届いても、Claude Code が送信として受け付けたとは限らない
|
|
@@ -1938,7 +2110,7 @@ export async function awaitDeliveryAck(d, o = {
|
|
|
1938
2110
|
reason = '職人がアイドルでないので貼り直さなかった';
|
|
1939
2111
|
continue; // この回は貼り直さずに、もう一度だけ印を待つ(回数は数える=待ちは有限)
|
|
1940
2112
|
}
|
|
1941
|
-
if (d.repaste()) {
|
|
2113
|
+
if (await d.repaste()) {
|
|
1942
2114
|
repasted++;
|
|
1943
2115
|
reason = '';
|
|
1944
2116
|
}
|
|
@@ -1956,23 +2128,8 @@ export async function awaitDeliveryAck(d, o = {
|
|
|
1956
2128
|
* ★押し直してよいのは「こちらが入れた便が入力欄に残っている」ときだけ、を確かめるために使う。
|
|
1957
2129
|
*/
|
|
1958
2130
|
export function hasPendingDeliveryInInput(cap) {
|
|
1959
|
-
const
|
|
1960
|
-
|
|
1961
|
-
for (let k = lines.length - 1; k >= 0; k--) {
|
|
1962
|
-
if (/^\s*❯/.test(lines[k])) {
|
|
1963
|
-
start = k;
|
|
1964
|
-
break;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
if (start < 0)
|
|
1968
|
-
return false;
|
|
1969
|
-
const block = [];
|
|
1970
|
-
for (let k = start; k < lines.length; k++) {
|
|
1971
|
-
if (k > start && /^─{10,}/.test(lines[k].trim()))
|
|
1972
|
-
break;
|
|
1973
|
-
block.push(lines[k]);
|
|
1974
|
-
}
|
|
1975
|
-
return block.join('\n').includes('(配達役より)');
|
|
2131
|
+
const block = inputBlock(cap);
|
|
2132
|
+
return block !== null && block.includes('(配達役より)');
|
|
1976
2133
|
}
|
|
1977
2134
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1978
2135
|
// 1便1context:配る直前に職人の会話を新しくする(便 87a1a28a・★既定オフ)
|
|
@@ -1989,13 +2146,10 @@ export function hasPendingDeliveryInInput(cap) {
|
|
|
1989
2146
|
// ───────────────────────────────────────────────────────────────────────────
|
|
1990
2147
|
/** 会話を新しくできたかを待つ長さ(ミリ秒)。 */
|
|
1991
2148
|
export const RELAY_FRESH_CONFIRM_MS = 10_000;
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
* 届いていない」と答えて止まった)。間を置いたうえで、画面が入力待ちで落ち着いたことも確かめる。
|
|
1997
|
-
*/
|
|
1998
|
-
export const RELAY_FRESH_SETTLE_MS = 3_000;
|
|
2149
|
+
// ★会話が新しくなった後に「決め打ちの間」は置かない(便 963ddca3)。以前は 3 秒待っていたが、
|
|
2150
|
+
// 職人を起動した直後の最初の便では 3 秒待っても貼り付けが落ちた(実障害2件)。入力を受け取れる
|
|
2151
|
+
// 状態かは時間では分からないので、①画面が入力待ちで落ち着くのを待ち、②貼った後に入力欄へ入った
|
|
2152
|
+
// のを画面で確かめる(deliverToTmux)。入らなければ何も送らずに貼り直す。
|
|
1999
2153
|
/** その作業場所の会話の記録のファイル名の一覧。読めなければ空。 */
|
|
2000
2154
|
export function listTranscripts(cwd, env = process.env) {
|
|
2001
2155
|
try {
|
|
@@ -2025,9 +2179,14 @@ export async function freshenContext(site, cwd, d = {
|
|
|
2025
2179
|
}
|
|
2026
2180
|
for (let t = 0;; t += o.pollMs) {
|
|
2027
2181
|
if (d.listSessions().some((n) => !before.has(n))) {
|
|
2028
|
-
//
|
|
2029
|
-
|
|
2030
|
-
|
|
2182
|
+
// ★新しい会話ができたら、画面が入力待ちで落ち着くのを待つ(上限は waitMs・決め打ちの間は置かない)。
|
|
2183
|
+
for (let u = 0;; u += o.pollMs) {
|
|
2184
|
+
if (await d.ready())
|
|
2185
|
+
return true;
|
|
2186
|
+
if (u >= o.waitMs)
|
|
2187
|
+
return false;
|
|
2188
|
+
await d.sleep(o.pollMs);
|
|
2189
|
+
}
|
|
2031
2190
|
}
|
|
2032
2191
|
if (t >= o.waitMs)
|
|
2033
2192
|
return false;
|
|
@@ -3118,7 +3277,25 @@ export async function relayTick(cfg, state, hooks) {
|
|
|
3118
3277
|
const sendStartedAt = Date.now();
|
|
3119
3278
|
// ★Claude の職人へは受領確認を使う(便 7d810bbb)。Codex は会話の記録の形が違うので今までどおり。
|
|
3120
3279
|
const useAck = kindOf(cfg) === 'claude';
|
|
3121
|
-
|
|
3280
|
+
// ★Claude の職人へは「貼り付けが入力欄に入ったのを確かめてから添え書きと Enter」(便 963ddca3)。
|
|
3281
|
+
// 入らなければ何も送信しない=添え書きだけが届く形が無い。
|
|
3282
|
+
let landed = true;
|
|
3283
|
+
if (useAck) {
|
|
3284
|
+
const d = await deliverToTmux(cfg.site, text, cmd.id);
|
|
3285
|
+
if (d.result === 'failed' || d.result === 'not-clear') {
|
|
3286
|
+
// 職人には何も送っていない。status は queued のまま据え置く(今までの送信失敗と同じ)。
|
|
3287
|
+
log(d.result === 'failed'
|
|
3288
|
+
? `本文の投入に失敗, keep queued: ${cfg.site}/${cmd.id}`
|
|
3289
|
+
: `WARN site=${cfg.site} id=${cmd.id} 入力欄に配達役の物ではない字が残っているので貼らずに次の周で確かめ直します`);
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3292
|
+
landed = d.result === 'submitted';
|
|
3293
|
+
if (!landed) {
|
|
3294
|
+
log(`WARN site=${cfg.site} id=${cmd.id} 貼り付けが入力欄に入らない(${d.pastes}回貼った)。` +
|
|
3295
|
+
'★何も送信していません→本文未着として管制へ知らせます');
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
else if (!sendToTmux(cfg.site, text)) {
|
|
3122
3299
|
// 送信失敗(sendToTmux は内部で例外も握って false を返す)。status は queued のまま据え置く。
|
|
3123
3300
|
// 失敗の理由(tmux の stderr)は sendToTmux が直前の行に出している。
|
|
3124
3301
|
log(`本文の投入に失敗, keep queued: ${cfg.site}/${cmd.id}`);
|
|
@@ -3148,13 +3325,21 @@ export async function relayTick(cfg, state, hooks) {
|
|
|
3148
3325
|
// 新しい指示の生存ハートビートは先頭から数え直す(前指示のスロットルを引き継がない)。
|
|
3149
3326
|
state.lastHeartbeatAt = null;
|
|
3150
3327
|
noteReadiness(state, 'delivered', cmd.id);
|
|
3151
|
-
log(`site=${cfg.site} id=${cmd.id}
|
|
3328
|
+
log(`site=${cfg.site} id=${cmd.id} ${landed ? '配達しました' : '配達を試みましたが貼り付けが入らず、何も送信していません'}`);
|
|
3152
3329
|
// ★配った時点で管制へ印を書く(便 7d810bbb)。Claude の職人は「配達中(受領確認待ち)」、
|
|
3153
3330
|
// それ以外は今までどおり「配達済み(sent)」。書けなければ次tick以降で書けるまでやり直す
|
|
3154
3331
|
// (markSentAtCentral)。★書けなくても再配達はしない。
|
|
3155
3332
|
await markSentAtCentral(cfg, state);
|
|
3156
3333
|
if (!useAck || state.pending === null)
|
|
3157
3334
|
return;
|
|
3335
|
+
if (!landed) {
|
|
3336
|
+
// ★貼り付けが入らなかった=職人は何も受け取っていない。本文未着として知らせ、追跡を外す
|
|
3337
|
+
// (7d810bbb の道)。★配り直さない・ほかの便も配らない(配り直すのは人の手番)。
|
|
3338
|
+
state.pending.ack = 'missing';
|
|
3339
|
+
state.pending.ackSent = false;
|
|
3340
|
+
await sendAckAtCentral(cfg, state);
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3158
3343
|
// ── 受領確認(★えふさん確定:送った instruction_id と完全一致した印を確かめて初めて ACK) ──
|
|
3159
3344
|
// 印を待ち、入力欄に残っていれば Enter だけ押し直し(b69c1608)、現れなければ2回まで貼り直す。
|
|
3160
3345
|
// ★一致の確かめに使うのは、この便を貼った後(sendStartedAt 以降)に新しく出た記録だけ。
|
|
@@ -3177,8 +3362,9 @@ export async function relayTick(cfg, state, hooks) {
|
|
|
3177
3362
|
}
|
|
3178
3363
|
},
|
|
3179
3364
|
idle: () => isIdleStable(cfg.site, kindOf(cfg)),
|
|
3180
|
-
//
|
|
3181
|
-
|
|
3365
|
+
// ★同じ本文を貼り直す(配達回数は数えない=1回の配達の中の貼り直し)。★貼り直しも、入力欄に
|
|
3366
|
+
// 入ったのを確かめてからでないと添え書きも Enter も送らない(便 963ddca3)。
|
|
3367
|
+
repaste: async () => (await deliverToTmux(cfg.site, text, cmd.id)).result === 'submitted',
|
|
3182
3368
|
sleep,
|
|
3183
3369
|
});
|
|
3184
3370
|
log(`site=${cfg.site} id=${cmd.id} 受領の確かめ: ${r.received ? '印が一致(受領)' : `印が現れない(本文未着): ${r.reason}`}` +
|