@dmsdc-ai/aigentry-telepty 0.1.98 → 0.3.5
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/AGENTS.md +23 -0
- package/CHANGELOG.md +436 -0
- package/CLAUDE.md +5 -1
- package/README.md +70 -1
- package/cli.js +232 -53
- package/cross-machine.js +132 -0
- package/daemon.js +399 -39
- package/docs/reports/2026-05-05-issue-8-claude-review.md +194 -0
- package/docs/specs/2026-05-05-issue-8-telepty-init.md +477 -0
- package/docs/superpowers/specs/2026-04-26-inject-submit-enter-reliability.md +447 -0
- package/docs/superpowers/specs/2026-04-26-prompt-symbol-render-gate.md +571 -0
- package/docs/superpowers/specs/2026-04-26-submit-gate-fixes-v2.md +608 -0
- package/docs/superpowers/specs/2026-05-02-submit-force-and-retry.md +139 -0
- package/host-spec.js +60 -0
- package/mcp-server/index.mjs +24 -3
- package/package.json +6 -5
- package/scripts/regen-snippet-fixtures.js +42 -0
- package/skill-installer.js +42 -6
- package/skills/telepty/SKILL.md +1 -1
- package/skills/telepty-allow/SKILL.md +1 -1
- package/skills/telepty-attach/SKILL.md +1 -1
- package/skills/telepty-broadcast/SKILL.md +1 -1
- package/skills/telepty-daemon/SKILL.md +1 -1
- package/skills/telepty-inject/SKILL.md +76 -4
- package/skills/telepty-list/SKILL.md +1 -1
- package/skills/telepty-listen/SKILL.md +1 -1
- package/skills/telepty-rename/SKILL.md +1 -1
- package/skills/telepty-session/SKILL.md +1 -1
- package/specs/enforce-report-spec.md +237 -0
- package/src/init/print-snippet.js +114 -0
- package/src/init/snippets/agents.md +15 -0
- package/src/init/snippets/claude.md +15 -0
- package/src/init/snippets/gemini.md +15 -0
- package/src/prompt-symbol-registry.js +97 -0
- package/src/report-enforcement.js +86 -0
- package/src/submit-gate.js +269 -0
- package/tests/snippet-protocol/v1/golden-agents.json +1 -0
- package/tests/snippet-protocol/v1/golden-agents.md +17 -0
- package/tests/snippet-protocol/v1/golden-all.json +3 -0
- package/tests/snippet-protocol/v1/golden-all.md +53 -0
- package/tests/snippet-protocol/v1/golden-claude.json +1 -0
- package/tests/snippet-protocol/v1/golden-claude.md +17 -0
- package/tests/snippet-protocol/v1/golden-gemini.json +1 -0
- package/tests/snippet-protocol/v1/golden-gemini.md +17 -0
package/AGENTS.md
CHANGED
|
@@ -72,3 +72,26 @@ telepty inject --ref --from aigentry-telepty-{cli} aigentry-orchestrator-claude
|
|
|
72
72
|
- **Evidence-Based**: 추측 금지. 데이터/로그/테스트 결과 기반 판단.
|
|
73
73
|
- **Fail Fast**: 에러 즉시 보고. 숨기지 않음.
|
|
74
74
|
- **Constitution**: ~/projects/aigentry/docs/CONSTITUTION.md 준수.
|
|
75
|
+
|
|
76
|
+
## Legacy exception — `skill-installer.js`
|
|
77
|
+
|
|
78
|
+
`skill-installer.js` is a **named legacy exception** grandfathered by
|
|
79
|
+
**ADR 2026-05-05-telepty-devkit-boundary §6.2.1**. It is the single
|
|
80
|
+
exception to the telepty/devkit boundary rule and is NOT precedent for
|
|
81
|
+
new placements.
|
|
82
|
+
|
|
83
|
+
- **Scope**: bugfixes, security patches, dependency upgrades only.
|
|
84
|
+
- **No new feature expansion**: net-new functionality (new CLI detection,
|
|
85
|
+
new skill types, new install paths, new flags) MUST land in
|
|
86
|
+
`@dmsdc-ai/aigentry-devkit`. At migration time devkit will introduce
|
|
87
|
+
`aigentry scaffold install-skills`.
|
|
88
|
+
- **Reviewer enforcement**: PR reviewers cite ADR 2026-05-05 §6.2.1 when
|
|
89
|
+
rejecting feature-expansion PRs against `skill-installer.js`.
|
|
90
|
+
- **Migration triggers** (per ADR §6.2.1): ≥2 PRs in 60 days attempting
|
|
91
|
+
net-new features; devkit feature requires functionality only in
|
|
92
|
+
`skill-installer.js`; breaking change to its interface.
|
|
93
|
+
|
|
94
|
+
Per-CLI hook installation, `CLAUDE.md`/`AGENTS.md`/`GEMINI.md` scaffolding,
|
|
95
|
+
project-file generation, and cross-cutting installable skills are
|
|
96
|
+
**devkit-owned** per ADR 2026-05-05 §3.3 — not telepty's responsibility.
|
|
97
|
+
See `@dmsdc-ai/aigentry-devkit` (`aigentry scaffold …`).
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@dmsdc-ai/aigentry-telepty` are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.3.5] — 2026-05-05
|
|
6
|
+
|
|
7
|
+
### Added — `telepty init --print-snippet` (Issue #8)
|
|
8
|
+
|
|
9
|
+
New subcommand that emits the canonical telepty-baseline snippet to stdout for
|
|
10
|
+
graceful integration into per-CLI agent files. **Mechanism only** — telepty
|
|
11
|
+
emits the versioned snippet text; downstream tooling (`aigentry-devkit
|
|
12
|
+
scaffold --integrate-telepty`) owns idempotent insertion into
|
|
13
|
+
`~/CLAUDE.md` / `~/AGENTS.md` / `~/GEMINI.md`. Boundary contract per ADR
|
|
14
|
+
`2026-05-05-telepty-devkit-boundary` (commit `e4b072b`).
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
telepty init --print-snippet [--target {claude|agents|gemini|all}] [--format {markdown|json}]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **argv-only**: never consumes stdin (safe in scripted pipelines).
|
|
21
|
+
- **zero file I/O**: pure stdout emission; nothing read from or written to disk.
|
|
22
|
+
- **deterministic**: byte-identical output for a given (target, format) pair —
|
|
23
|
+
fixtures can be hashed for verification.
|
|
24
|
+
- **LF-only bodies**: no CRLF leakage on cross-platform consumers.
|
|
25
|
+
- **stderr clean**: success path emits no warnings.
|
|
26
|
+
|
|
27
|
+
Spec: `docs/specs/2026-05-05-issue-8-telepty-init.md` (commit `8d2dc94`).
|
|
28
|
+
Implementation: `f5c6bad`. Protocol SSOT: `aigentry-ssot/contracts/telepty-snippet-v1.md`
|
|
29
|
+
(commit `f4ff0cd`). 15 conformance fixtures shipped at `tests/snippet-protocol/v1/`
|
|
30
|
+
covering markdown envelopes (claude, agents, gemini, all), JSON records,
|
|
31
|
+
shell-hazard guards, deterministic LF output, default targeting, unsupported-target
|
|
32
|
+
rejection, internal-failure exit codes, stdin-pipe ignore, devkit-free invocation,
|
|
33
|
+
and the snippet golden fixtures themselves.
|
|
34
|
+
|
|
35
|
+
### Docs — G7/G8/G9 M0 audit gate closure (commit `d7b8b21`)
|
|
36
|
+
|
|
37
|
+
Per ADR `2026-05-05-telepty-devkit-boundary` §3.1.2 (devkit owns content
|
|
38
|
+
placement; telepty owns mechanism), three gates closed:
|
|
39
|
+
|
|
40
|
+
- **G7 — `README.md`**: removed reference to the rejected `telepty install
|
|
41
|
+
hooks` subcommand. Per ADR §3.1.2, that responsibility lives in devkit.
|
|
42
|
+
- **G8 — `AGENTS.md`**: added Legacy exception subsection documenting the
|
|
43
|
+
remaining devkit-shaped legacy surface.
|
|
44
|
+
- **G9 — `skill-installer.js`**: top-of-file LEGACY header per ADR §6.2.1
|
|
45
|
+
marking the module as legacy-track (devkit migration pending).
|
|
46
|
+
|
|
47
|
+
### Internal
|
|
48
|
+
|
|
49
|
+
- Cross-LLM review pattern applied: Codex implemented the `init` subcommand
|
|
50
|
+
+ fixtures; Claude reviewed and ACCEPTed (commit `d06e1e9`).
|
|
51
|
+
- `test/enforce-report.test.js` version assertion bumped to track release
|
|
52
|
+
(commit `d0f4495`).
|
|
53
|
+
|
|
54
|
+
### Tests
|
|
55
|
+
|
|
56
|
+
- `test/init.test.js` — full coverage of the new subcommand (snippet
|
|
57
|
+
emission, target/format permutations, stdin-ignore, error exits, devkit-free
|
|
58
|
+
invocation).
|
|
59
|
+
- `tests/snippet-protocol/v1/` — golden fixtures for protocol conformance;
|
|
60
|
+
`npm test` runs `git diff --exit-code` against them so any drift fails CI.
|
|
61
|
+
|
|
62
|
+
### Invariants preserved
|
|
63
|
+
|
|
64
|
+
- Daemon code unchanged. No new dependencies. No `bin` field changes.
|
|
65
|
+
- Existing CLI subcommands (`allow`, `inject`, `list`, `tui`, `daemon`, …)
|
|
66
|
+
unchanged.
|
|
67
|
+
- Cross-host inject path (0.3.4) unchanged.
|
|
68
|
+
|
|
69
|
+
## [0.3.4] — 2026-05-05
|
|
70
|
+
|
|
71
|
+
### Added — Cross-host inject (`<id>@<host>` syntax)
|
|
72
|
+
|
|
73
|
+
Enables `telepty inject <id>@<host> "msg"` to deliver to a remote daemon
|
|
74
|
+
without SSH wrapping, by resolving `<host>` against the peer registry and
|
|
75
|
+
issuing direct HTTP `POST /api/sessions/<id>/inject`. Closes the gap that
|
|
76
|
+
forced operators to either pre-shell into the host or pipe through SSH.
|
|
77
|
+
|
|
78
|
+
- **`connect-http` peer mode** (commit `a92cacc`) — new HTTP-only peer
|
|
79
|
+
registration path that does not require a reverse PTY tunnel; suitable
|
|
80
|
+
for daemons reachable via Tailscale / private DNS.
|
|
81
|
+
- **`TELEPTY_HOST` env parser fix** (commit `a92cacc`) — `<id>@<host>` now
|
|
82
|
+
parses correctly when the host segment contains a port or non-default
|
|
83
|
+
scheme; prior parser dropped the host portion silently.
|
|
84
|
+
- **Peer registry HTTP-only mode** — registry entries can be marked
|
|
85
|
+
HTTP-only so the daemon does not attempt PTY fan-out for them.
|
|
86
|
+
|
|
87
|
+
### Added — Skill installer auto-detect (`486bc1e`)
|
|
88
|
+
|
|
89
|
+
`telepty install` now auto-detects which AI CLIs are present
|
|
90
|
+
(`claude`, `codex`, `gemini`) and only installs the corresponding skill
|
|
91
|
+
files. Reduces noisy "skipped" log lines and prevents stub installs
|
|
92
|
+
on machines that don't have the target CLI yet.
|
|
93
|
+
|
|
94
|
+
### Fixed — Node 18 ESM regression (`fc7ff9a`)
|
|
95
|
+
|
|
96
|
+
Pinned `uuid@9` (was floating to v10, which is ESM-only and caused
|
|
97
|
+
`ERR_REQUIRE_ESM` under Node 18 CommonJS consumers).
|
|
98
|
+
|
|
99
|
+
### Docs
|
|
100
|
+
|
|
101
|
+
- Cross-host inject `<id>@<host>` syntax documented (commit `c8b9bbb`).
|
|
102
|
+
- `[context-ref]` inject protocol standardized across docs (commit `8986a96`).
|
|
103
|
+
- REPORT pattern + orchestrator-id runtime resolution documented in skills
|
|
104
|
+
(commit `658f712`).
|
|
105
|
+
- Korean trigger keywords added to skill `SKILL.md` descriptions for
|
|
106
|
+
cross-locale activation (commit `57f46e1`).
|
|
107
|
+
|
|
108
|
+
### Note — never published to npm
|
|
109
|
+
|
|
110
|
+
`0.3.4` was version-bumped locally but never reached the registry; this
|
|
111
|
+
entry is added retrospectively alongside the `0.3.5` publish so the
|
|
112
|
+
changelog history matches the git log. Registry consumers go directly
|
|
113
|
+
from `0.3.3` → `0.3.5`.
|
|
114
|
+
|
|
115
|
+
## [0.3.3] — 2026-05-02
|
|
116
|
+
|
|
117
|
+
### Added — `inject --submit-force` + idempotent client retry (spec: `docs/superpowers/specs/2026-05-02-submit-force-and-retry.md`)
|
|
118
|
+
|
|
119
|
+
Closes task #347. Two opt-in CLI knobs on `telepty inject` for cases where
|
|
120
|
+
the 0.3.2 prompt-symbol gate has a transient render mismatch (autocomplete
|
|
121
|
+
dropdown open, cursor moved, mid-paste race) and the 504 fall-through
|
|
122
|
+
forces the human user to press Enter manually.
|
|
123
|
+
|
|
124
|
+
- **`--submit-force`** — passes `force: true` to `POST /submit`. Skips
|
|
125
|
+
both Layer 3 (prompt-symbol) and Layer 1 (state-gate) and dispatches
|
|
126
|
+
Enter once via the existing `terminalLevelSubmit` chain (kitty → cmux
|
|
127
|
+
→ PTY). Daemon-side `force` semantics already shipped in 0.3.1 for
|
|
128
|
+
`telepty send-key`; this just plumbs the flag through inject.
|
|
129
|
+
- **`--submit-retry N`** (default 1, clamp [0, 3]) — on a 504 response
|
|
130
|
+
with a retry-safe reason, wait 300 ms and retry the same `/submit`
|
|
131
|
+
request up to N times. Retry-safe reasons (idempotent re-fire is
|
|
132
|
+
guaranteed because the body is verifiably still in the input box):
|
|
133
|
+
|
|
134
|
+
| Reason | Source |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `gated_dispatch_unconsumed` | `daemon.js:1680` (verify said body still visible after best-effort dispatch) |
|
|
137
|
+
| `gate_timeout` | reserved (Layer 1 plain timeout — falls through to dispatch in 0.3.1+, not currently a 504 source) |
|
|
138
|
+
| `no_prompt_symbol_seen` | reserved (Layer 3 timeout — currently never emits 504) |
|
|
139
|
+
|
|
140
|
+
Hard-fail reasons (`session_dead`, `session_error`, `session_restarting`,
|
|
141
|
+
`no_state`, `no_state_manager`) and any non-504 status (4xx) **never**
|
|
142
|
+
trigger client-side retry — re-firing won't recover.
|
|
143
|
+
|
|
144
|
+
- **Default behavior preserved**: a bare `telepty inject --submit ...`
|
|
145
|
+
call now retries once on a retry-safe 504. This is a strict improvement
|
|
146
|
+
over 0.3.2 (which surfaced a warning and required manual `send-key`)
|
|
147
|
+
and remains backward-compatible because retry only fires when the
|
|
148
|
+
server tells the client the dispatch demonstrably did not land.
|
|
149
|
+
|
|
150
|
+
### Tests
|
|
151
|
+
|
|
152
|
+
- `test/inject-submit-flags.test.js` (NEW, 9 tests) — mock-daemon
|
|
153
|
+
coverage:
|
|
154
|
+
- `--submit-force` adds `force:true` to `/submit` body; success line
|
|
155
|
+
renders `[forced]` tag.
|
|
156
|
+
- bare `--submit` does NOT add `force` to body.
|
|
157
|
+
- default `--submit-retry 1` retries once on `gated_dispatch_unconsumed`
|
|
158
|
+
504 then succeeds; output contains `[retry 1/1]`.
|
|
159
|
+
- `--submit-retry 2` exhausts to 3 calls then prints
|
|
160
|
+
`Submit gated-timeout … after 3 attempts`.
|
|
161
|
+
- `--submit-retry 0` makes exactly 1 call, no `[retry`.
|
|
162
|
+
- `session_dead` 504 → no retry even with `--submit-retry 3`.
|
|
163
|
+
- `no_state` 504 → no retry even with `--submit-retry 3`.
|
|
164
|
+
- `--submit-force --submit-retry 2` preserves `force:true` across retries.
|
|
165
|
+
- 500 error → no retry, prints to stderr.
|
|
166
|
+
- `test/enforce-report.test.js` — version assertion 0.2.0 → 0.3.3.
|
|
167
|
+
- All 174 existing tests pass unchanged.
|
|
168
|
+
|
|
169
|
+
### Invariants preserved
|
|
170
|
+
|
|
171
|
+
- Daemon code unchanged. `force:true` and the gate layers behave exactly
|
|
172
|
+
as in 0.3.2.
|
|
173
|
+
- `telepty send-key` unchanged.
|
|
174
|
+
- `telepty enter` unchanged.
|
|
175
|
+
- `telepty inject --ref` (no `--submit`) unchanged.
|
|
176
|
+
- Cross-machine remote inject path unchanged (the SSH branch in `cli.js`
|
|
177
|
+
bypasses the new flags by design — remote daemons handle their own
|
|
178
|
+
submit semantics).
|
|
179
|
+
- Exit code on soft failure (504) remains 0; orchestrator scripts that
|
|
180
|
+
check for non-zero exits are unaffected.
|
|
181
|
+
|
|
182
|
+
## [0.3.2] — 2026-04-26
|
|
183
|
+
|
|
184
|
+
### Added — Layer 3 prompt-symbol render gate (spec: `docs/superpowers/specs/2026-04-26-prompt-symbol-render-gate.md`)
|
|
185
|
+
|
|
186
|
+
Strictly additive layer above the 0.3.1 `sessionStateManager` gate. Closes
|
|
187
|
+
the recurring "Enter not applied on freshly-spawned `claude`/`codex`" trap
|
|
188
|
+
by directly observing the rendered terminal screen for a per-CLI prompt
|
|
189
|
+
symbol — the only deterministic ready-signal these TUIs expose to external
|
|
190
|
+
automation (no OSC 133, no exit-on-prompt, no socket signal).
|
|
191
|
+
|
|
192
|
+
- **`src/prompt-symbol-registry.js`** (NEW) — per-CLI prompt-symbol catalog:
|
|
193
|
+
|
|
194
|
+
| CLI | Symbol | Codepoint | UTF-8 | Geometry sanity |
|
|
195
|
+
|---|---|---|---|---|
|
|
196
|
+
| `claude` | `❯` | U+276F | `E2 9D AF` | sandwiched between U+2500 (`─`) horizontal-rule borders |
|
|
197
|
+
| `codex` | `›` | U+203A | `E2 80 BA` | model footer (`gpt-N…`) within 2 lines below |
|
|
198
|
+
| `gemini` | `*` | U+002A | `2A` | bracketed by U+2580 (`▀`) above / U+2584 (`▄`) below |
|
|
199
|
+
|
|
200
|
+
`lookup(command)` normalizes path + args (`/usr/local/bin/claude --resume`
|
|
201
|
+
→ claude entry; `codex resume` → codex entry). Unknown CLIs return `null`,
|
|
202
|
+
causing the gate to skip cleanly via `unknown_cli`.
|
|
203
|
+
|
|
204
|
+
- **`src/submit-gate.js` `awaitPromptSymbol(session, opts)`** (NEW) — polls
|
|
205
|
+
`cmux read-screen --workspace <id> --lines <n>` (default 30) every
|
|
206
|
+
`pollIntervalMs` (default 150 ms) and resolves only when the symbol has
|
|
207
|
+
been stably detected for ≥ `stabilityMs` (default 200 ms). Bounded by
|
|
208
|
+
`timeoutMs` (default 8000 ms; clamp [500, 30000]). Resolves cleanly with
|
|
209
|
+
one of:
|
|
210
|
+
- `{ ready: true, last_seen_at, waited_ms }`
|
|
211
|
+
- `{ ready: false, reason: 'no_screen_primitive', waited_ms: 0 }` (non-cmux backend)
|
|
212
|
+
- `{ ready: false, reason: 'unknown_cli', waited_ms: 0 }`
|
|
213
|
+
- `{ ready: false, reason: 'no_prompt_symbol_seen', waited_ms }` (timeout, fall through)
|
|
214
|
+
Pure helper: `now`/`sleep`/`readScreen`/`registry` are all injectable for
|
|
215
|
+
deterministic tests (fakeClock harness from `verifyBodyConsumed`).
|
|
216
|
+
|
|
217
|
+
- **`daemon.js` POST /submit** — Layer 3 runs immediately before Layer 1
|
|
218
|
+
on the gated path. Result threaded into success and 504 response bodies
|
|
219
|
+
as optional `prompt_symbol: { found, waited_ms, [reason], [last_seen_at] }`.
|
|
220
|
+
**Never emits its own 504** — best-effort fall-through to Layer 1, which
|
|
221
|
+
retains all existing 0.3.1 outcomes (success / `gated_dispatch_unconsumed`
|
|
222
|
+
/ hard-fail). Per-request bypass via `{ "prompt_symbol_gate": false }`
|
|
223
|
+
(Layer 3 only); `force:true` and `TELEPTY_SUBMIT_GATE=off` continue to
|
|
224
|
+
bypass BOTH layers.
|
|
225
|
+
|
|
226
|
+
### Tests
|
|
227
|
+
|
|
228
|
+
- `test/prompt-symbol-registry.test.js` (NEW) — registry coverage with
|
|
229
|
+
inline cmux read-screen fixtures: claude/codex/gemini detect on idle
|
|
230
|
+
screens, banner-stage rejection (no border geometry), history-echo
|
|
231
|
+
disambiguation (LAST occurrence anchored), `lookup()` path/args
|
|
232
|
+
normalization + case-insensitivity + unknown/null inputs, `byteSeq`
|
|
233
|
+
matches `Buffer.from(symbol, 'utf8')`.
|
|
234
|
+
- `test/submit-gate.test.js` (extended) — `awaitPromptSymbol` covers:
|
|
235
|
+
non-cmux → `no_screen_primitive`; missing workspace → same; unknown CLI
|
|
236
|
+
→ `unknown_cli`; stable claude/codex screen → ready after `stabilityMs`;
|
|
237
|
+
empty `readScreen` returns → `no_prompt_symbol_seen` after `timeoutMs`;
|
|
238
|
+
symbol-then-disappear → stability streak resets; injected registry
|
|
239
|
+
override is honored; `readScreen` receives `(workspaceId, tailLines)`.
|
|
240
|
+
|
|
241
|
+
### Invariants preserved
|
|
242
|
+
|
|
243
|
+
- All 32 existing `test/submit-gate.test.js` tests pass unchanged.
|
|
244
|
+
- `force: true` and `TELEPTY_SUBMIT_GATE=off` bypass BOTH layers.
|
|
245
|
+
- Layer 1 hard-fail short-circuits (`session_dead`/`error`/`restarting`/
|
|
246
|
+
`no_state`/`no_state_manager`) still emit 504; Layer 3 never adds a new
|
|
247
|
+
504 source.
|
|
248
|
+
- `inject --ref` (no `--submit`) path unchanged.
|
|
249
|
+
- aterm / non-cmux backends skip Layer 3 cleanly via `no_screen_primitive`.
|
|
250
|
+
- Cross-machine remote inject unchanged: Layer 3 runs only on the daemon
|
|
251
|
+
with cmux access; remote daemons fall through.
|
|
252
|
+
- Response shape additive — `prompt_symbol` is an optional field; existing
|
|
253
|
+
callers ignore unknown JSON keys.
|
|
254
|
+
|
|
255
|
+
## [0.3.1] — 2026-04-26
|
|
256
|
+
|
|
257
|
+
### Fixed — submit-gate regression cluster (spec: `docs/superpowers/specs/2026-04-26-submit-gate-fixes-v2.md`)
|
|
258
|
+
|
|
259
|
+
Three regressions surfaced post-`0.3.0` against fresh-spawned `claude`/`codex`
|
|
260
|
+
sessions where the gate's strict thresholds and timeout-abandon path made the
|
|
261
|
+
new `/submit` endpoint less reliable than the pre-`0.3.0` blind retry on cold
|
|
262
|
+
REPLs. All three fixes ship in this single patch.
|
|
263
|
+
|
|
264
|
+
- **δ-fix-2 — `send-key` bypass (P0).** `POST /api/sessions/:id/submit` now
|
|
265
|
+
accepts `{ "force": true }` to skip the render-readiness gate and verify
|
|
266
|
+
step, dispatching once via the existing kitty/cmux/PTY chain. `cli.js`
|
|
267
|
+
`send-key` always sets `force:true`, restoring the manual Enter override.
|
|
268
|
+
Response shape additive (`forced:true`); existing callers unaffected.
|
|
269
|
+
- **δ-fix-3 — gate threshold relaxed 0.85 → 0.5 (P1).** `sessionStateManager`
|
|
270
|
+
emits IDLE `confidence=0.6` when neither OSC 133 nor a shell-prompt pattern
|
|
271
|
+
matches (`session-state.js:380`) — the dominant case for AI-CLI TUIs whose
|
|
272
|
+
Unicode-box input line bypasses `PROMPT_PATTERNS`. Default `minConfidence`
|
|
273
|
+
lowered to `0.5` (below the 0.6 silence-fallback with margin); per-request
|
|
274
|
+
override `min_confidence` body field accepted (clamped `[0, 1]`).
|
|
275
|
+
- **δ-fix-4 — timeout extension + best-effort dispatch on timeout (P1).**
|
|
276
|
+
Default `gate_timeout_ms` raised `5000 → 10000` (upper clamp `15000 →
|
|
277
|
+
30000`) to cover empirical `claude` ready window (3-6 s on fresh spawn).
|
|
278
|
+
On a plain `timeout` reason, `/submit` now dispatches anyway and verifies
|
|
279
|
+
body consumption — the pre-`0.3.0` blind dispatch is restored as a fallback
|
|
280
|
+
while keeping the new honesty signal: 504 only fires when
|
|
281
|
+
`verifyBodyConsumed` confirms the body is still in the input box (new
|
|
282
|
+
`reason: 'gated_dispatch_unconsumed'`). Dispatch-on-timeout success path
|
|
283
|
+
adds `gated_dispatch_after_timeout: true` (additive).
|
|
284
|
+
Hard-fail reasons (`session_dead`/`error`/`restarting`/`no_state`) still
|
|
285
|
+
short-circuit to 504 immediately.
|
|
286
|
+
|
|
287
|
+
### Invariants preserved
|
|
288
|
+
|
|
289
|
+
- `inject --submit` warm-session reliability ≥99% target (gate short-circuits
|
|
290
|
+
at conf≥0.85 still passes after default drops to 0.5).
|
|
291
|
+
- 504 still emitted in true-fail case (after best-effort dispatch + verify
|
|
292
|
+
reports `still_visible`).
|
|
293
|
+
- `TELEPTY_SUBMIT_GATE=off` daemon-wide escape hatch preserved.
|
|
294
|
+
- `inject --ref` (no `--submit`) path unchanged.
|
|
295
|
+
- 22/23 existing `test/submit-gate.test.js` tests pass unchanged; one test
|
|
296
|
+
(line 185-193) updated to preserve the below-threshold-rejection semantic
|
|
297
|
+
with literals shifted away from the new 0.5 default.
|
|
298
|
+
|
|
299
|
+
## [0.3.0] — 2026-04-26
|
|
300
|
+
|
|
301
|
+
### Added — render-gated submit (specs: `docs/superpowers/specs/2026-04-26-inject-submit-enter-reliability.md`)
|
|
302
|
+
|
|
303
|
+
- **`src/submit-gate.js`** — pure helpers exported for unit tests:
|
|
304
|
+
- `awaitReplReady(sessionId, stateManager, opts)` — waits for the target REPL
|
|
305
|
+
to reach an input-ready state (`idle` or `waiting`) with confidence ≥ 0.85
|
|
306
|
+
before Enter is fired. Bounded by `timeoutMs` (default 5000).
|
|
307
|
+
- `verifyBodyConsumed(session, bodyText, opts)` — polls the session's
|
|
308
|
+
`outputRing` for the inject body to disappear from the input box,
|
|
309
|
+
confirming Enter was actually consumed by the REPL (default 1500 ms,
|
|
310
|
+
200 ms interval). Optimistic when body never visible (ANSI/wrap edge).
|
|
311
|
+
- `isReady`, `isFailed`, `READY_STATES`, `FAIL_STATES` — test surface.
|
|
312
|
+
- **POST `/api/sessions/:id/submit`** rewritten to use the gate by default.
|
|
313
|
+
Flow: gate on REPL readiness → dispatch via existing kitty/cmux/PTY chain →
|
|
314
|
+
verify consumption (when caller passes `injected_body`) → bounded retry.
|
|
315
|
+
Response now includes `gated`, `gate_wait_ms`, `verify` (when applicable).
|
|
316
|
+
- **HTTP `504 gate_timeout` response** on `/api/sessions/:id/submit` when the
|
|
317
|
+
REPL never readies for input within `gate_timeout_ms` (default 5000).
|
|
318
|
+
This is **why this is a minor bump** — consumers may need to handle the new
|
|
319
|
+
status code. 504 (Gateway Timeout) is the correct semantic versus 408 or
|
|
320
|
+
reused 503 — the daemon acted as a gateway to the upstream REPL and the
|
|
321
|
+
upstream did not respond in time.
|
|
322
|
+
- **CLI `inject --submit`** now passes `injected_body` to the daemon for
|
|
323
|
+
consumption verification, removed the legacy 500 ms blind sleep
|
|
324
|
+
(gate handles timing), and treats 504 as a soft failure (logs a clear
|
|
325
|
+
remediation hint, exits 0 — orchestrator scripts depend on exit 0 for
|
|
326
|
+
recoverable conditions).
|
|
327
|
+
- New body fields accepted by `/submit`: `injected_body`, `gate_timeout_ms`,
|
|
328
|
+
`verify_timeout_ms`. Existing `pre_delay_ms` / `retries` / `retry_delay_ms`
|
|
329
|
+
remain accepted for back-compat.
|
|
330
|
+
- **`TELEPTY_SUBMIT_GATE=off`** env var — escape hatch to revert to the 0.2.x
|
|
331
|
+
blind retry path for parity testing or rollback.
|
|
332
|
+
|
|
333
|
+
### Changed
|
|
334
|
+
|
|
335
|
+
- POST `/api/sessions/:id/submit` is no longer open-loop. Default behavior
|
|
336
|
+
is gated; legacy blind retry preserved only behind `TELEPTY_SUBMIT_GATE=off`.
|
|
337
|
+
- CLI `✅ Submitted via <strategy>` line now optionally appends
|
|
338
|
+
`[gate <N>ms]` when the gate had to wait. Default-on; pre-existing
|
|
339
|
+
format preserved when gate fast-paths (warm sessions).
|
|
340
|
+
- `bus` event `submit` now carries optional fields `gated`, `gate_wait_ms`,
|
|
341
|
+
`verify` (additive — consumers ignore unknown fields).
|
|
342
|
+
|
|
343
|
+
### Fixed
|
|
344
|
+
|
|
345
|
+
- Root cause: `/submit` previously fired Enter open-loop with a ~2.1 s
|
|
346
|
+
blind retry budget while a fresh `claude` REPL needed 3–6 s to render
|
|
347
|
+
(welcome banner, trust dialog, prompt setup). The legacy retry loop
|
|
348
|
+
also discarded `terminalLevelSubmit`'s return value, so the reported
|
|
349
|
+
`(N attempts)` count did not reflect verified dispatches. The new
|
|
350
|
+
gate observes the existing `sessionStateManager` (`idle` / `waiting`
|
|
351
|
+
with confidence ≥ 0.85) before dispatch, eliminating the race.
|
|
352
|
+
- Recurring orchestrator UX trap (parallel to #329 Track E27) where
|
|
353
|
+
every `inject --submit` required a manual `sleep N && telepty send-key
|
|
354
|
+
<id> enter` follow-up. Spec target: ≥ 99% on a 100× spawn-and-inject
|
|
355
|
+
E2E harness (current baseline ~0%); E2E harness execution is dispatched
|
|
356
|
+
to the builder (out of scope for this commit).
|
|
357
|
+
|
|
358
|
+
### Tests
|
|
359
|
+
|
|
360
|
+
- `test/submit-gate.test.js` — 23 new unit tests (all pass) covering
|
|
361
|
+
`awaitReplReady` fast-paths, transition resolution, timeout, fail-state
|
|
362
|
+
short-circuits; `verifyBodyConsumed` happy-path / optimistic / timeout /
|
|
363
|
+
empty / no-ring / whitespace normalization / ANSI strip / injectable
|
|
364
|
+
clock for deterministic timing.
|
|
365
|
+
- Pre-existing test suite is unmodified; integration coverage of the new
|
|
366
|
+
endpoint behavior is delegated to the builder per SAWP scope.
|
|
367
|
+
|
|
368
|
+
### Compatibility / migration
|
|
369
|
+
|
|
370
|
+
- **Default behavior changes** for callers of `/api/sessions/:id/submit`:
|
|
371
|
+
responses now succeed only when the REPL reaches readiness within
|
|
372
|
+
`gate_timeout_ms`. Most callers will see equivalent or better behavior;
|
|
373
|
+
callers that depended on "best effort fire-and-forget" can opt out via
|
|
374
|
+
`TELEPTY_SUBMIT_GATE=off`.
|
|
375
|
+
- `inject --ref` (without `--submit`), `telepty allow`, `telepty list`,
|
|
376
|
+
and `telepty send-key` semantics unchanged.
|
|
377
|
+
- Aterm sessions unaffected (gate is bypassed via existing
|
|
378
|
+
`session.type === 'aterm'` guards).
|
|
379
|
+
- No new external dependencies (Rule 17). No schema, persistence, or
|
|
380
|
+
state-machine changes (gate is read-only on `sessionStateManager`).
|
|
381
|
+
|
|
382
|
+
## [0.2.0] — 2026-04-15
|
|
383
|
+
|
|
384
|
+
### Added — REPORT enforcement (specs/enforce-report-spec.md)
|
|
385
|
+
|
|
386
|
+
- **New bus event types** for observable REPORT lifecycle:
|
|
387
|
+
- `TASK_IDLE_NO_REPORT` — fires once on idle transition for inject-driven sessions
|
|
388
|
+
- `TASK_COMPLETE_WITH_REPORT` — fires when matching REPORT inject detected via reverse-match
|
|
389
|
+
- `TASK_BLOCKED_WITH_REASON` — fires on `STATUS: blocked` reply inject
|
|
390
|
+
- `TASK_DISMISSED` — fires on `STATUS: dismissed` inject OR via DELETE endpoint
|
|
391
|
+
- `TASK_DEAD_NO_REPORT` — fires when session dies with pending report (attaches `auto_summary`)
|
|
392
|
+
- **New HTTP endpoints** on daemon:
|
|
393
|
+
- `GET /api/pendingReports/:id` — inspect pending report entry + optional auto_summary
|
|
394
|
+
- `DELETE /api/pendingReports/:id` — orchestrator-side dismissal; fires `TASK_DISMISSED`
|
|
395
|
+
- **New module** `src/report-enforcement.js` exports pure helpers:
|
|
396
|
+
- `classifyReportPrompt(prompt)` — classify inject prompt by prefix
|
|
397
|
+
- `buildAutoSummary(session, opts)` — scrape last N non-blank lines from outputRing with ANSI stripping and secret redaction
|
|
398
|
+
- **REPORT detection via reverse-match** in POST `/api/sessions/:id/inject`:
|
|
399
|
+
- An inject with `from=X` whose prompt starts with a REPORT prefix (`REPORT:`, `STATUS:`, `SPEC:`, `OWNER-DIAGNOSIS:`, `ENFORCE-SPEC:`, `ENFORCE-IMPLEMENTED:`, `LOG-FIX-SPEC:`, `LOG-FIX-IMPLEMENTED:`, `FIX-SPEC:`, `FIX-IMPLEMENTED:`, `SPEC-SYNC:`, `DIAGNOSIS:`) and whose recipient matches `pendingReports[X].source` fires the matching enforcement event.
|
|
400
|
+
- Prevents false positives: prefix alone is NOT enough; reverse-match to originating inject required.
|
|
401
|
+
- **Auto-summary with secret redaction**:
|
|
402
|
+
- Strips ANSI via shared regex
|
|
403
|
+
- Filters blank lines
|
|
404
|
+
- Caps at `DELIBERATION_REPORT_AUTO_SUMMARY_LINES` (default 40) + `DELIBERATION_REPORT_AUTO_SUMMARY_MAX_BYTES` (default 4096)
|
|
405
|
+
- Redacts `api_key`, `password`, `token`, `secret` assignment patterns → `[REDACTED]`
|
|
406
|
+
- Attached to `TASK_DEAD_NO_REPORT` events and GET query responses
|
|
407
|
+
|
|
408
|
+
### Changed
|
|
409
|
+
|
|
410
|
+
- `sessionStateManager.onTransition` handler now fires the enforcement events above. Legacy `TASK_COMPLETE:` text-inject to source session is preserved during 0.2.x grandfather period.
|
|
411
|
+
- Legacy auto-report paths (health-poll idle threshold + ready-WS signal) now coordinate via `pendingReports[id].idleNotified` flag to prevent double-fire.
|
|
412
|
+
- `pendingReports[id]` schema extended with `awaitingReport: true`, `idleNotified: bool`, `idleAt: ISO8601`. Entry is now cleared only when REPORT arrives, session dies, or orchestrator dismisses.
|
|
413
|
+
- Duplicate pendingReports overwrite now emits `[AUTO-REPORT] overwritten pending` warning.
|
|
414
|
+
|
|
415
|
+
### Configuration (new env vars)
|
|
416
|
+
|
|
417
|
+
- `DELIBERATION_REPORT_AUTO_SUMMARY_ON_QUERY` — bool, default `true`. Gates auto_summary on GET pendingReports.
|
|
418
|
+
- `DELIBERATION_REPORT_AUTO_SUMMARY_LINES` — int, default 40. Max lines in auto_summary.
|
|
419
|
+
- `DELIBERATION_REPORT_AUTO_SUMMARY_MAX_BYTES` — int, default 4096. Byte cap on auto_summary.
|
|
420
|
+
|
|
421
|
+
### Deprecated
|
|
422
|
+
|
|
423
|
+
- `reportTimeoutSecs` env var — emits deprecation warning if set. Removed in 0.3.x. Evidence (7.5s–649s task range) showed a default timer is arbitrary and prone to false timeouts; replaced with event-driven detection (idle + dead + explicit query).
|
|
424
|
+
|
|
425
|
+
### Tests
|
|
426
|
+
|
|
427
|
+
- `test/report-enforcement.test.js` — 28 new unit tests for `classifyReportPrompt`, `buildAutoSummary`, regex exports
|
|
428
|
+
- `test/enforce-report.test.js` — 11 new integration tests for bus events and endpoints
|
|
429
|
+
- Full suite: **170/170 passing** (131 pre-existing + 39 new)
|
|
430
|
+
|
|
431
|
+
### Migration notes
|
|
432
|
+
|
|
433
|
+
- **No orchestrator-side changes required** to benefit. New bus events flow passively; legacy `TASK_COMPLETE:` text-inject still fires.
|
|
434
|
+
- Consumers that subscribe to the bus now see richer event types — optional to consume.
|
|
435
|
+
- Orchestrators wanting to dismiss a pending report can use `DELETE /api/pendingReports/{id}`.
|
|
436
|
+
- Orchestrators wanting on-demand summary can use `GET /api/pendingReports/{id}` (honors `DELIBERATION_REPORT_AUTO_SUMMARY_ON_QUERY`).
|
package/CLAUDE.md
CHANGED
|
@@ -65,10 +65,14 @@ npm version patch --no-git-tag-version && npm publish --access public
|
|
|
65
65
|
- inject 시 발신자 session ID (`--from`)를 항상 포함
|
|
66
66
|
- PTY `\r` 직접 의존 금지
|
|
67
67
|
|
|
68
|
-
## 최근 주요 변경 (v0.1.58–0.
|
|
68
|
+
## 최근 주요 변경 (v0.1.58–0.3.3)
|
|
69
69
|
|
|
70
70
|
| 버전 | 변경 |
|
|
71
71
|
|------|------|
|
|
72
|
+
| 0.3.3 | inject `--submit-force` (gate bypass) + idempotent `--submit-retry` (default 1, retry-safe 504만). 클라이언트 측 변경, daemon 무수정. task #347. |
|
|
73
|
+
| 0.3.2 | Layer 3 prompt-symbol 렌더 게이트 — claude/codex/gemini 별 prompt symbol을 cmux read-screen으로 polling. |
|
|
74
|
+
| 0.3.1 | 게이트 임계값 0.85 → 0.5 완화 + dispatch-on-timeout best-effort + send-key force=true 우회 추가. |
|
|
75
|
+
| 0.3.0 | Render-gated submit (sessionStateManager 기반). Enter 송신 전 REPL ready 검증. |
|
|
72
76
|
| 0.1.62 | TUI 태스크 추적 — bus 이벤트에서 [태그] 자동 파싱, 세션별 상태 표시 |
|
|
73
77
|
| 0.1.61 | reconnect 시 resize/\x0c 제거 (멀티터미널 깜빡임 수정) |
|
|
74
78
|
| 0.1.60 | TUI P1 — s=start, k=kill, p=purge stale |
|
package/README.md
CHANGED
|
@@ -53,6 +53,9 @@ telepty broadcast "status report"
|
|
|
53
53
|
| `telepty list [--json]` | List sessions across all discovered hosts |
|
|
54
54
|
| `telepty attach [id[@host]]` | Attach to a session (interactive picker if no ID) |
|
|
55
55
|
| `telepty inject <id[@host]> "text"` | Inject text into a session |
|
|
56
|
+
| `telepty inject --submit <id> "text"` | Inject text and press Enter (render-gated, retries once on safe gate-timeout) |
|
|
57
|
+
| `telepty inject --submit --submit-force <id> "text"` | As above, but bypass the gate (skip Layer 1/3 detection — opt-in escape hatch) |
|
|
58
|
+
| `telepty inject --submit --submit-retry N <id> "text"` | Override retry count [0–3] on safe 504 (default 1) |
|
|
56
59
|
| `telepty enter <id[@host]>` | Send Enter/Return to a session |
|
|
57
60
|
| `telepty multicast <id1,id2> "text"` | Inject into multiple sessions |
|
|
58
61
|
| `telepty broadcast "text"` | Inject into ALL sessions |
|
|
@@ -69,13 +72,31 @@ telepty broadcast "status report"
|
|
|
69
72
|
|
|
70
73
|
telepty auto-discovers sessions across your Tailnet. All commands (`list`, `attach`, `inject`, `rename`, `multicast`, `broadcast`) work seamlessly across machines.
|
|
71
74
|
|
|
72
|
-
|
|
75
|
+
### `<id>@<host>` syntax
|
|
76
|
+
|
|
77
|
+
To target a specific host (when the same session ID exists on multiple hosts,
|
|
78
|
+
or when there is no Tailnet auto-discovery), append `@<host>` to the session
|
|
79
|
+
ID. `<host>` can be a hostname, LAN IP, or Tailnet name.
|
|
73
80
|
|
|
74
81
|
```bash
|
|
82
|
+
# Hostname / Tailnet name
|
|
75
83
|
telepty inject my-session@macbook "hello"
|
|
76
84
|
telepty attach worker@server-01
|
|
85
|
+
|
|
86
|
+
# LAN IP — useful when no Tailnet is configured
|
|
87
|
+
telepty inject orchestrator-claude@172.28.4.165 "ping"
|
|
88
|
+
telepty read-screen build-runner@10.0.0.42 --lines 50
|
|
77
89
|
```
|
|
78
90
|
|
|
91
|
+
**Requirements**:
|
|
92
|
+
- The remote daemon must be reachable on port **3848** from the calling host
|
|
93
|
+
(LAN routing, firewall rules, or Tailscale).
|
|
94
|
+
- No SSH or `sshd` is required on either side — the call hits the remote
|
|
95
|
+
daemon's HTTP API directly. This is the recommended path for laptop
|
|
96
|
+
daemons that don't run sshd.
|
|
97
|
+
- The `@<host>` qualifier works for `inject`, `attach`, `read-screen`,
|
|
98
|
+
`enter`, `multicast`, and `rename`.
|
|
99
|
+
|
|
79
100
|
## How It Works
|
|
80
101
|
|
|
81
102
|
```
|
|
@@ -89,6 +110,54 @@ CLI (telepty) ──> HTTP/WS ──> Daemon (:3848)
|
|
|
89
110
|
- **`inject`** delivers text via the fastest available path: kitty terminal API, WebSocket, or UDS (Unix Domain Socket for embedded integrations)
|
|
90
111
|
- **`submit`** is handled separately from text injection for reliability across all AI CLIs
|
|
91
112
|
|
|
113
|
+
## `[context-ref]` Protocol — long payloads via shared file
|
|
114
|
+
|
|
115
|
+
When a sender uses `telepty inject --ref <file> <target> "<message>"`, telepty
|
|
116
|
+
stores the payload in a shared file under `~/.telepty/shared/<sha256>.md` and
|
|
117
|
+
injects only a short pointer prompt of the form:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
[context-ref] Read ~/.telepty/shared/<sha256>.md and use it as the source of truth for this task.
|
|
121
|
+
<inline message>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This avoids prompt rot in the receiving session (and in the orchestrator's
|
|
125
|
+
window when the reply is small).
|
|
126
|
+
|
|
127
|
+
### Receiver contract
|
|
128
|
+
|
|
129
|
+
The receiving AI session is expected to:
|
|
130
|
+
1. Detect the `[context-ref]` prefix on the first line.
|
|
131
|
+
2. Read the file at the absolute path.
|
|
132
|
+
3. Treat the file contents as the **authoritative payload** for the task — the
|
|
133
|
+
inline message is supplementary (topic / hint), not the source of truth.
|
|
134
|
+
|
|
135
|
+
### Storage location
|
|
136
|
+
|
|
137
|
+
- File path: `~/.telepty/shared/<sha256>.md` (sha256 of payload body)
|
|
138
|
+
- Created with mode `0600`; readable only by the local user
|
|
139
|
+
- Persists across sessions; not garbage-collected automatically (run
|
|
140
|
+
`telepty clean --shared` to prune)
|
|
141
|
+
|
|
142
|
+
### When to use `--ref`
|
|
143
|
+
|
|
144
|
+
- Payload exceeds ~1KB or contains structured content (code, logs, tables).
|
|
145
|
+
- You want the receiver to load the payload deterministically rather than
|
|
146
|
+
paraphrase it from the inject prompt.
|
|
147
|
+
- You're orchestrating a multi-hop conversation where the orchestrator should
|
|
148
|
+
not see the full payload in its own context window.
|
|
149
|
+
|
|
150
|
+
### Integration scope
|
|
151
|
+
|
|
152
|
+
Per-agent receiver integrations (auto-loading the file via Claude Code
|
|
153
|
+
`UserPromptSubmit` hooks, Codex `AGENTS.md` directives, etc.) are **out of
|
|
154
|
+
scope for telepty core** — they live in the agent's own configuration.
|
|
155
|
+
Per-CLI hook installation lives in devkit: run `aigentry scaffold
|
|
156
|
+
install-hooks {claude|codex|gemini}` after installing
|
|
157
|
+
`@dmsdc-ai/aigentry-devkit`. (Older drafts proposed a receiver-side
|
|
158
|
+
`telepty install` subcommand for this; that direction is rejected per ADR
|
|
159
|
+
2026-05-05-telepty-devkit-boundary §3.1.2 / §3.4 row 2.)
|
|
160
|
+
|
|
92
161
|
## Inject Delivery Paths
|
|
93
162
|
|
|
94
163
|
| Priority | Method | When |
|