@dmsdc-ai/aigentry-telepty 0.5.1 → 0.5.2

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 CHANGED
@@ -4,6 +4,20 @@ All notable changes to `@dmsdc-ai/aigentry-telepty` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.5.2] - 2026-06-06
8
+
9
+ ### Fixed — submit handshake confirmation (#507-B / #508)
10
+
11
+ - **`--submit` Enter sometimes did not register in a CLI's TUI** (the recurring
12
+ "Enter 안눌림" bug). `inject --submit` wrote the carriage return but did not
13
+ confirm the target actually consumed it, so under timing pressure the submit
14
+ could be dropped and the injected prompt left sitting unsubmitted. **Fix:** a
15
+ submit-gate handshake in `src/submit-gate.js` confirms the submit landed, with
16
+ `cli.js` / `daemon.js` wiring the gate into the inject path. Landed on `main`
17
+ at commit `2a21265`. This release ships that already-tested fix (npm 0.5.1
18
+ still served the pre-fix code; the running daemon must be restarted separately
19
+ to pick it up). (telepty#512)
20
+
7
21
  ## [0.5.1] - 2026-05-30
8
22
 
9
23
  ### Fixed — daemon never started (CRITICAL, regresses 0.5.0)
package/cli.js CHANGED
@@ -17,6 +17,13 @@ const { getRuntimeInfo } = require('./runtime-info');
17
17
  const { formatHostLabel, groupSessionsByHost, pickSessionTarget } = require('./session-routing');
18
18
  const { buildSharedContextPrompt, createSharedContextDescriptor, ensureSharedContextFile } = require('./shared-context');
19
19
  const { runInteractiveSkillInstaller } = require('./skill-installer');
20
+ const {
21
+ detectTerminalProgram,
22
+ formatSessionTerminal,
23
+ enrichSessionIdle,
24
+ formatSessionStatusWithIdle,
25
+ printSessionInfo
26
+ } = require('./src/cli/session-view');
20
27
  const { resolveWindowsExecutable } = require('./src/win-resolve-executable');
21
28
  const { decideVersionAction } = require('./src/version-handshake');
22
29
  const crossMachine = require('./cross-machine');
@@ -176,62 +183,6 @@ async function getDaemonMeta(host = REMOTE_HOST) {
176
183
  }
177
184
  }
178
185
 
179
- function detectTerminalProgram(env = process.env) {
180
- const rawTermProgram = typeof env.TERM_PROGRAM === 'string' ? env.TERM_PROGRAM.trim() : '';
181
- if (rawTermProgram) {
182
- return rawTermProgram;
183
- }
184
-
185
- if (env.TMUX) {
186
- return 'tmux';
187
- }
188
-
189
- const term = typeof env.TERM === 'string' ? env.TERM.toLowerCase() : '';
190
- if (term.includes('kitty')) return 'kitty';
191
- if (term.includes('ghostty')) return 'ghostty';
192
- if (term.includes('tmux')) return 'tmux';
193
-
194
- return null;
195
- }
196
-
197
- function formatSessionTerminal(session) {
198
- const terminal = session.terminal || session.termProgram || null;
199
- const term = session.term || null;
200
- if (terminal && term) {
201
- return `${terminal} (${term})`;
202
- }
203
- return terminal || term || 'unknown';
204
- }
205
-
206
- function formatSessionHealth(session) {
207
- const status = session.healthStatus || 'UNKNOWN';
208
- const reason = session.healthReason || null;
209
- if (reason && reason !== status) {
210
- return `${status} (${reason})`;
211
- }
212
- return status;
213
- }
214
-
215
- function enrichSessionIdle(session, nowMs = Date.now()) {
216
- const idleSeconds = typeof session.idleSeconds === 'number'
217
- ? session.idleSeconds
218
- : lifecycle.computeIdleSeconds(session.lastActivityAt, nowMs);
219
- return {
220
- ...session,
221
- idleSeconds,
222
- idle_seconds: idleSeconds
223
- };
224
- }
225
-
226
- function formatSessionStatusWithIdle(session) {
227
- const base = formatSessionHealth(session);
228
- const idleSeconds = typeof session.idleSeconds === 'number' ? session.idleSeconds : null;
229
- if (idleSeconds !== null && idleSeconds > 60) {
230
- return `${base} 💤 idle (${lifecycle.formatIdleDuration(idleSeconds)})`;
231
- }
232
- return base;
233
- }
234
-
235
186
  function formatApiError(data, fallback = 'Request failed.') {
236
187
  if (!data) {
237
188
  return fallback;
@@ -352,39 +303,6 @@ function ensureRemoteSharedReference(peerName, descriptor, message = '') {
352
303
  };
353
304
  }
354
305
 
355
- function printSessionInfo(session, options = {}) {
356
- const host = options.host || session.host || '127.0.0.1';
357
- console.log('\x1b[1mSession Info:\x1b[0m');
358
- console.log(` - ID: \x1b[36m${session.id}\x1b[0m`);
359
- console.log(` Host: ${formatHostLabel(host)}`);
360
- console.log(` Command: ${session.command}`);
361
- console.log(` Type: ${session.type || 'unknown'}`);
362
- console.log(` Status: ${formatSessionHealth(session)}`);
363
- console.log(` Terminal: ${session.terminal || session.termProgram || 'unknown'}`);
364
- console.log(` TERM: ${session.term || 'n/a'}`);
365
- console.log(` CWD: ${session.cwd}`);
366
- console.log(` Clients: ${session.active_clients ?? 0}`);
367
- if (session.createdAt) {
368
- console.log(` Started: ${new Date(session.createdAt).toLocaleString()}`);
369
- }
370
- if (session.lastActivityAt) {
371
- console.log(` Last Activity: ${new Date(session.lastActivityAt).toLocaleString()}`);
372
- }
373
- if (typeof session.idleSeconds === 'number') {
374
- console.log(` Idle: ${session.idleSeconds}s`);
375
- }
376
- if (session.semantic && session.semantic.phase) {
377
- console.log(` Phase: ${session.semantic.phase}`);
378
- }
379
- if (session.semantic && session.semantic.current_task) {
380
- console.log(` Current Task: ${session.semantic.current_task}`);
381
- }
382
- if (session.semantic && session.semantic.blocker) {
383
- console.log(` Blocker: ${session.semantic.blocker}`);
384
- }
385
- console.log('');
386
- }
387
-
388
306
  function resolveTeleptyEntryPoint() {
389
307
  // After npm upgrade, process.argv[1] still points to the OLD version's cli.js.
390
308
  // Resolve the current telepty binary from PATH, which npm updates on install.
@@ -1954,43 +1872,26 @@ async function main() {
1954
1872
  }
1955
1873
  const submitBody = {
1956
1874
  injected_body: injectPrompt || '',
1957
- retries: 1,
1875
+ retries: submitRetries,
1958
1876
  retry_delay_ms: 500,
1959
1877
  ...(submitForce ? { force: true } : {}),
1960
1878
  };
1961
- const RETRY_DELAY_MS = 300;
1962
- const RETRY_SAFE_REASONS = new Set([
1963
- 'gated_dispatch_unconsumed',
1964
- 'gate_timeout',
1965
- 'no_prompt_symbol_seen',
1966
- ]);
1967
- const maxAttempts = 1 + submitRetries;
1968
1879
  let submitRes = null;
1969
1880
  let submitData = null;
1970
1881
  let attemptsMade = 0;
1971
1882
  let lastError = null;
1972
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
1973
- if (attempt > 0) {
1974
- await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1975
- }
1976
- attemptsMade = attempt + 1;
1977
- try {
1978
- submitRes = await fetchWithAuth(`${daemonUrl(target.host)}/api/sessions/${encodeURIComponent(target.id)}/submit`, {
1979
- method: 'POST',
1980
- headers: { 'Content-Type': 'application/json' },
1981
- body: JSON.stringify(submitBody),
1982
- });
1983
- submitData = await submitRes.json();
1984
- } catch (submitErr) {
1985
- lastError = submitErr;
1986
- submitRes = null;
1987
- submitData = null;
1988
- break;
1989
- }
1990
- if (submitRes.ok) break;
1991
- if (submitRes.status !== 504) break;
1992
- const retryReason = submitData && typeof submitData.reason === 'string' ? submitData.reason : null;
1993
- if (!RETRY_SAFE_REASONS.has(retryReason)) break;
1883
+ try {
1884
+ attemptsMade = 1;
1885
+ submitRes = await fetchWithAuth(`${daemonUrl(target.host)}/api/sessions/${encodeURIComponent(target.id)}/submit`, {
1886
+ method: 'POST',
1887
+ headers: { 'Content-Type': 'application/json' },
1888
+ body: JSON.stringify(submitBody),
1889
+ });
1890
+ submitData = await submitRes.json();
1891
+ } catch (submitErr) {
1892
+ lastError = submitErr;
1893
+ submitRes = null;
1894
+ submitData = null;
1994
1895
  }
1995
1896
  if (lastError) {
1996
1897
  console.error(`⚠️ Submit failed: ${lastError.message}`);
@@ -2002,16 +1903,16 @@ async function main() {
2002
1903
  ? ' (dispatched-after-gate-timeout)'
2003
1904
  : '';
2004
1905
  const attemptsNote = submitData.attempts > 1 ? ` (${submitData.attempts} attempts)` : '';
2005
- const retryNote = attemptsMade > 1 ? ` [retry ${attemptsMade - 1}/${submitRetries}]` : '';
2006
1906
  const forcedNote = submitData.forced ? ' [forced]' : '';
2007
- console.log(`✅ Submitted via ${submitData.strategy}${attemptsNote}${gateNote}${lateNote}${retryNote}${forcedNote}.`);
1907
+ console.log(`✅ Submitted via ${submitData.strategy}${attemptsNote}${gateNote}${lateNote}${forcedNote}.`);
2008
1908
  } else if (submitRes && submitRes.status === 504) {
2009
1909
  // Soft failure: REPL never readied. Orchestrator scripts depend on
2010
1910
  // exit 0 here — surface a clear remediation hint but do not exit
2011
1911
  // non-zero.
2012
1912
  const reason = (submitData && submitData.reason) || 'gate_timeout';
2013
1913
  const lastState = (submitData && submitData.last_state) || 'unknown';
2014
- const retriesNote = attemptsMade > 1 ? ` after ${attemptsMade} attempts` : '';
1914
+ const daemonAttempts = submitData && Number.isFinite(Number(submitData.attempts)) ? Number(submitData.attempts) : attemptsMade;
1915
+ const retriesNote = daemonAttempts > 1 ? ` after ${daemonAttempts} attempts` : '';
2015
1916
  const hint = submitForce
2016
1917
  ? ''
2017
1918
  : ` Try \`telepty inject --submit --submit-force ${target.id} ...\` or manual \`telepty send-key ${target.id} enter\`.`;