instar 1.3.984 → 1.3.986
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +13 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +17 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +48 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +15 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +26 -15
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/claudeReadinessProbe.d.ts +137 -0
- package/dist/core/claudeReadinessProbe.d.ts.map +1 -0
- package/dist/core/claudeReadinessProbe.js +181 -0
- package/dist/core/claudeReadinessProbe.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +21 -21
- package/src/templates/scripts/telegram-reply.sh +61 -0
- package/upgrades/1.3.985.md +109 -0
- package/upgrades/1.3.986.md +153 -0
- package/upgrades/side-effects/booting-pane-read-as-ready.md +222 -0
- package/upgrades/side-effects/misplaced-flag-sent-as-message-text.md +193 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Readiness classification for a framework TUI pane tail.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS AS ITS OWN MODULE
|
|
5
|
+
* ---------------------------------
|
|
6
|
+
* On 2026-07-26 a user's message was injected into a freshly-spawned Claude Code
|
|
7
|
+
* pane 15 seconds before that pane could accept input. The paste was swallowed by
|
|
8
|
+
* the TUI still painting itself, and the delivery was logged as a SUCCESS
|
|
9
|
+
* (`Injected initial message into "…" (915 chars, after stabilization delay)`).
|
|
10
|
+
* The message reached the agent only because an unrelated start-hook backstop
|
|
11
|
+
* re-read recent history.
|
|
12
|
+
*
|
|
13
|
+
* The cause was the readiness probe, not the injector. It accepted a bare
|
|
14
|
+
* slash-command mention anywhere in the captured tail:
|
|
15
|
+
*
|
|
16
|
+
* if (/\/(effort|model|fast)/.test(tail)) return true;
|
|
17
|
+
*
|
|
18
|
+
* Claude Code's startup banner advertises slash commands IN PROSE — the pane that
|
|
19
|
+
* fooled it carried "…Run /model and select Fable to use it." So the probe matched
|
|
20
|
+
* a promotional line and declared a still-booting pane ready. A string matcher
|
|
21
|
+
* fooled by its own subject: the banner that proves the app is NOT yet accepting
|
|
22
|
+
* input was read as proof that it is.
|
|
23
|
+
*
|
|
24
|
+
* THOSE CLAUSES ARE DELETED, NOT TUNED. The first fix attempt kept them and
|
|
25
|
+
* required "status-bar shape" (line-start, or directly after an interpunct).
|
|
26
|
+
* Second-pass review falsified that:
|
|
27
|
+
* - The banner uses the same shape. `⚠ CLAUDE.md is over the … limit · /memory
|
|
28
|
+
* to free up context` and `+1 more · /status` are BANNER lines in interpunct
|
|
29
|
+
* shape. The only thing separating them from a status bar was that `memory`
|
|
30
|
+
* and `status` fall outside the three-word set — i.e. still vocabulary, and a
|
|
31
|
+
* plausible `· /model to opt in` promo line matched outright.
|
|
32
|
+
* - The line-start branch reinstated the Anthropic-copy dependency it claimed to
|
|
33
|
+
* remove: at a fixed pane width an edit shifting the wrap by ~14 characters
|
|
34
|
+
* puts `/model` at column 0 and the original defect returns.
|
|
35
|
+
* A signal that cannot separate an ADVERT for a command from a STATUS BAR showing
|
|
36
|
+
* one carries no information about readiness. Widening the genuinely structural
|
|
37
|
+
* markers below costs nothing and covers the same panes.
|
|
38
|
+
*
|
|
39
|
+
* WHY THE ANSWER IS NOT A BOOLEAN
|
|
40
|
+
* -------------------------------
|
|
41
|
+
* There are three consumers and they want OPPOSITE responses to the two ways a
|
|
42
|
+
* pane can fail to be ready:
|
|
43
|
+
* - the spawn/inject path — must not type into a pane that is still painting,
|
|
44
|
+
* and must not type into a MENU (see below);
|
|
45
|
+
* - `waitForClaudeReadyWithRetry` — waits, then falls back;
|
|
46
|
+
* - the Slack stuck-session path (`server.ts`) — calls this on an ALREADY-LIVE
|
|
47
|
+
* session and KILLS it when the answer is not-ready.
|
|
48
|
+
* A bare `false` tells that third caller "kill this" for a pane that is merely
|
|
49
|
+
* mid-boot or politely waiting on a question. So the probe reports WHICH state it
|
|
50
|
+
* saw and each caller applies its own policy. `isReadyPromptTail` is retained as
|
|
51
|
+
* the boolean façade for callers that genuinely only want "can I type now".
|
|
52
|
+
*
|
|
53
|
+
* THE MENU CASE (found by the operator, 2026-07-26)
|
|
54
|
+
* -------------------------------------------------
|
|
55
|
+
* Claude Code paints the SAME `❯` glyph on a menu's focused option as it uses for
|
|
56
|
+
* the input box (`PermissionPromptAutoResolver.SELECTOR_GLYPH`). So a session
|
|
57
|
+
* sitting on a startup question read as READY. This is strictly worse than the
|
|
58
|
+
* banner case: text typed at a banner is lost, but text typed at a menu is not —
|
|
59
|
+
* Enter SELECTS AN OPTION, so an arriving message can answer a permission question
|
|
60
|
+
* on the operator's behalf. A menu is therefore classified as its own state, never
|
|
61
|
+
* as ready, no matter which glyphs it carries.
|
|
62
|
+
*
|
|
63
|
+
* Kept pure (text in, verdict out) so it is testable without tmux, and so the
|
|
64
|
+
* regression fixtures are the literal pane text that caused each finding.
|
|
65
|
+
*
|
|
66
|
+
* RULE 3.1 RATIONALE (state-detection; registry:
|
|
67
|
+
* `specs/provider-portability/06-state-detector-registry.md`)
|
|
68
|
+
* ------------------------------------------------------------------------------
|
|
69
|
+
* - **Criticality:** silent-corruption-if-wrong — the worst class, demonstrated
|
|
70
|
+
* twice. A false `ready` on a painting pane loses a user message AND writes a
|
|
71
|
+
* success line for it; a false `ready` on a menu is worse still, because Enter
|
|
72
|
+
* SELECTS an option, so an arriving message can answer a permission question on
|
|
73
|
+
* the operator's behalf.
|
|
74
|
+
* - **Frequency:** per-spawn and per-inject, plus per-stuck-check on the Slack
|
|
75
|
+
* path.
|
|
76
|
+
* - **Stability:** UNSTABLE. Claude Code's TUI is a private surface. This is not a
|
|
77
|
+
* theoretical rating — the 2026-07-26 incident WAS an upstream copy change (a
|
|
78
|
+
* Fable-5 promo line) turning a passing detector into a failing one, and the
|
|
79
|
+
* menu case is an upstream glyph reuse.
|
|
80
|
+
* - **Fallback:** partial and asymmetric. The spawn path has an extended wait and
|
|
81
|
+
* a blind-inject-if-alive fallback; the loss on 2026-07-26 was caught only by
|
|
82
|
+
* the start hook's unanswered-message backstop, which is an unrelated path and
|
|
83
|
+
* cannot be relied on as this detector's safety net. The Slack path's not-ready
|
|
84
|
+
* branch is DESTRUCTIVE (kill + respawn), so a wrong answer there costs a live
|
|
85
|
+
* session, not a delay.
|
|
86
|
+
* - **→ Verdict: deterministic + canary REQUIRED.** This module ships the
|
|
87
|
+
* deterministic half plus regression fixtures carrying the literal pane text of
|
|
88
|
+
* both known failures. It does NOT ship a canary, so it detects the two shapes
|
|
89
|
+
* that already bit us and not the next drift. Canary tracked as CMT-1044.
|
|
90
|
+
* <!-- tracked: CMT-1044 -->
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* What a captured pane tail shows.
|
|
94
|
+
*
|
|
95
|
+
* - `ready` — an input surface is drawn and accepting input.
|
|
96
|
+
* - `menu` — a numbered selection menu is focused. NOT ready, and NOT a
|
|
97
|
+
* stuck session: something is waiting on an answer. A caller
|
|
98
|
+
* that kills on not-ready must not kill on this.
|
|
99
|
+
* - `not-ready` — no input surface found (typically still painting).
|
|
100
|
+
*/
|
|
101
|
+
export type PaneReadiness = 'ready' | 'menu' | 'not-ready';
|
|
102
|
+
/**
|
|
103
|
+
* True when the tail shows a FOCUSED selection menu.
|
|
104
|
+
*
|
|
105
|
+
* Requires the selector glyph to sit ON a numbered option line, AND at least two
|
|
106
|
+
* numbered option lines overall. Both halves are load-bearing:
|
|
107
|
+
*
|
|
108
|
+
* - "glyph on the option line" separates a menu from the input box. The box
|
|
109
|
+
* renders `❯` alone on its own line; a menu renders `❯ 1. Yes`. Testing only
|
|
110
|
+
* "the tail contains ❯ somewhere" would classify ordinary assistant output
|
|
111
|
+
* that happens to list `1.` and `2.` above the prompt as a menu, stalling a
|
|
112
|
+
* genuinely ready session — an over-block on the inject path.
|
|
113
|
+
* - "at least two options" separates a menu from a single numbered line of
|
|
114
|
+
* ordinary output.
|
|
115
|
+
*
|
|
116
|
+
* This mirrors `PermissionPromptAutoResolver`'s requirement (a line whose lead
|
|
117
|
+
* glyphs contain ❯ whose text is `N.` + a label) rather than inventing a second,
|
|
118
|
+
* divergent notion of what a menu looks like.
|
|
119
|
+
*/
|
|
120
|
+
export declare function tailShowsMenu(tail: string): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Classify a captured pane tail.
|
|
123
|
+
*
|
|
124
|
+
* `tail` is the joined last-N non-blank lines of a `capture-pane` read. Never pass
|
|
125
|
+
* the whole scrollback: a banner that has scrolled far above the prompt is not
|
|
126
|
+
* evidence either way, and older content only adds false-positive surface.
|
|
127
|
+
*/
|
|
128
|
+
export declare function classifyPaneReadiness(tail: string): PaneReadiness;
|
|
129
|
+
/**
|
|
130
|
+
* Boolean façade: can text be typed into this pane right now?
|
|
131
|
+
*
|
|
132
|
+
* A menu answers `false` — correct for every caller that is about to type. A
|
|
133
|
+
* caller whose not-ready branch is DESTRUCTIVE (kill/respawn) must use
|
|
134
|
+
* `classifyPaneReadiness` instead and leave a `menu` pane alone.
|
|
135
|
+
*/
|
|
136
|
+
export declare function isReadyPromptTail(tail: string): boolean;
|
|
137
|
+
//# sourceMappingURL=claudeReadinessProbe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeReadinessProbe.d.ts","sourceRoot":"","sources":["../../src/core/claudeReadinessProbe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC;AA6B3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAejE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Readiness classification for a framework TUI pane tail.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS AS ITS OWN MODULE
|
|
5
|
+
* ---------------------------------
|
|
6
|
+
* On 2026-07-26 a user's message was injected into a freshly-spawned Claude Code
|
|
7
|
+
* pane 15 seconds before that pane could accept input. The paste was swallowed by
|
|
8
|
+
* the TUI still painting itself, and the delivery was logged as a SUCCESS
|
|
9
|
+
* (`Injected initial message into "…" (915 chars, after stabilization delay)`).
|
|
10
|
+
* The message reached the agent only because an unrelated start-hook backstop
|
|
11
|
+
* re-read recent history.
|
|
12
|
+
*
|
|
13
|
+
* The cause was the readiness probe, not the injector. It accepted a bare
|
|
14
|
+
* slash-command mention anywhere in the captured tail:
|
|
15
|
+
*
|
|
16
|
+
* if (/\/(effort|model|fast)/.test(tail)) return true;
|
|
17
|
+
*
|
|
18
|
+
* Claude Code's startup banner advertises slash commands IN PROSE — the pane that
|
|
19
|
+
* fooled it carried "…Run /model and select Fable to use it." So the probe matched
|
|
20
|
+
* a promotional line and declared a still-booting pane ready. A string matcher
|
|
21
|
+
* fooled by its own subject: the banner that proves the app is NOT yet accepting
|
|
22
|
+
* input was read as proof that it is.
|
|
23
|
+
*
|
|
24
|
+
* THOSE CLAUSES ARE DELETED, NOT TUNED. The first fix attempt kept them and
|
|
25
|
+
* required "status-bar shape" (line-start, or directly after an interpunct).
|
|
26
|
+
* Second-pass review falsified that:
|
|
27
|
+
* - The banner uses the same shape. `⚠ CLAUDE.md is over the … limit · /memory
|
|
28
|
+
* to free up context` and `+1 more · /status` are BANNER lines in interpunct
|
|
29
|
+
* shape. The only thing separating them from a status bar was that `memory`
|
|
30
|
+
* and `status` fall outside the three-word set — i.e. still vocabulary, and a
|
|
31
|
+
* plausible `· /model to opt in` promo line matched outright.
|
|
32
|
+
* - The line-start branch reinstated the Anthropic-copy dependency it claimed to
|
|
33
|
+
* remove: at a fixed pane width an edit shifting the wrap by ~14 characters
|
|
34
|
+
* puts `/model` at column 0 and the original defect returns.
|
|
35
|
+
* A signal that cannot separate an ADVERT for a command from a STATUS BAR showing
|
|
36
|
+
* one carries no information about readiness. Widening the genuinely structural
|
|
37
|
+
* markers below costs nothing and covers the same panes.
|
|
38
|
+
*
|
|
39
|
+
* WHY THE ANSWER IS NOT A BOOLEAN
|
|
40
|
+
* -------------------------------
|
|
41
|
+
* There are three consumers and they want OPPOSITE responses to the two ways a
|
|
42
|
+
* pane can fail to be ready:
|
|
43
|
+
* - the spawn/inject path — must not type into a pane that is still painting,
|
|
44
|
+
* and must not type into a MENU (see below);
|
|
45
|
+
* - `waitForClaudeReadyWithRetry` — waits, then falls back;
|
|
46
|
+
* - the Slack stuck-session path (`server.ts`) — calls this on an ALREADY-LIVE
|
|
47
|
+
* session and KILLS it when the answer is not-ready.
|
|
48
|
+
* A bare `false` tells that third caller "kill this" for a pane that is merely
|
|
49
|
+
* mid-boot or politely waiting on a question. So the probe reports WHICH state it
|
|
50
|
+
* saw and each caller applies its own policy. `isReadyPromptTail` is retained as
|
|
51
|
+
* the boolean façade for callers that genuinely only want "can I type now".
|
|
52
|
+
*
|
|
53
|
+
* THE MENU CASE (found by the operator, 2026-07-26)
|
|
54
|
+
* -------------------------------------------------
|
|
55
|
+
* Claude Code paints the SAME `❯` glyph on a menu's focused option as it uses for
|
|
56
|
+
* the input box (`PermissionPromptAutoResolver.SELECTOR_GLYPH`). So a session
|
|
57
|
+
* sitting on a startup question read as READY. This is strictly worse than the
|
|
58
|
+
* banner case: text typed at a banner is lost, but text typed at a menu is not —
|
|
59
|
+
* Enter SELECTS AN OPTION, so an arriving message can answer a permission question
|
|
60
|
+
* on the operator's behalf. A menu is therefore classified as its own state, never
|
|
61
|
+
* as ready, no matter which glyphs it carries.
|
|
62
|
+
*
|
|
63
|
+
* Kept pure (text in, verdict out) so it is testable without tmux, and so the
|
|
64
|
+
* regression fixtures are the literal pane text that caused each finding.
|
|
65
|
+
*
|
|
66
|
+
* RULE 3.1 RATIONALE (state-detection; registry:
|
|
67
|
+
* `specs/provider-portability/06-state-detector-registry.md`)
|
|
68
|
+
* ------------------------------------------------------------------------------
|
|
69
|
+
* - **Criticality:** silent-corruption-if-wrong — the worst class, demonstrated
|
|
70
|
+
* twice. A false `ready` on a painting pane loses a user message AND writes a
|
|
71
|
+
* success line for it; a false `ready` on a menu is worse still, because Enter
|
|
72
|
+
* SELECTS an option, so an arriving message can answer a permission question on
|
|
73
|
+
* the operator's behalf.
|
|
74
|
+
* - **Frequency:** per-spawn and per-inject, plus per-stuck-check on the Slack
|
|
75
|
+
* path.
|
|
76
|
+
* - **Stability:** UNSTABLE. Claude Code's TUI is a private surface. This is not a
|
|
77
|
+
* theoretical rating — the 2026-07-26 incident WAS an upstream copy change (a
|
|
78
|
+
* Fable-5 promo line) turning a passing detector into a failing one, and the
|
|
79
|
+
* menu case is an upstream glyph reuse.
|
|
80
|
+
* - **Fallback:** partial and asymmetric. The spawn path has an extended wait and
|
|
81
|
+
* a blind-inject-if-alive fallback; the loss on 2026-07-26 was caught only by
|
|
82
|
+
* the start hook's unanswered-message backstop, which is an unrelated path and
|
|
83
|
+
* cannot be relied on as this detector's safety net. The Slack path's not-ready
|
|
84
|
+
* branch is DESTRUCTIVE (kill + respawn), so a wrong answer there costs a live
|
|
85
|
+
* session, not a delay.
|
|
86
|
+
* - **→ Verdict: deterministic + canary REQUIRED.** This module ships the
|
|
87
|
+
* deterministic half plus regression fixtures carrying the literal pane text of
|
|
88
|
+
* both known failures. It does NOT ship a canary, so it detects the two shapes
|
|
89
|
+
* that already bit us and not the next drift. Canary tracked as CMT-1044.
|
|
90
|
+
* <!-- tracked: CMT-1044 -->
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* Status-bar / footer strings that only exist once the TUI has finished starting.
|
|
94
|
+
*
|
|
95
|
+
* Sourced from the probes already trusted for this elsewhere in the tree
|
|
96
|
+
* (`SessionReaper`, the interactive-pool adapter config) rather than invented
|
|
97
|
+
* here, so the readiness question gets one vocabulary instead of four. Verified
|
|
98
|
+
* absent from the boot banner. `bypass permissions` alone covered only ONE of
|
|
99
|
+
* Claude Code's permission-mode footers — a session in auto-accept mode shows
|
|
100
|
+
* none of it — which is why the `❯`-is-always-present assumption was load-bearing
|
|
101
|
+
* before and is not now.
|
|
102
|
+
*/
|
|
103
|
+
const AT_PROMPT_FOOTERS = [
|
|
104
|
+
'bypass permissions',
|
|
105
|
+
'? for shortcuts',
|
|
106
|
+
'shift+tab to cycle',
|
|
107
|
+
'auto-accept edits',
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* A menu option line: an optional glyph run, then `N.` or `N)` and a label.
|
|
111
|
+
* Matches the shape Claude Code uses for approval / consent / trust prompts.
|
|
112
|
+
*/
|
|
113
|
+
const MENU_OPTION_RE = /^[^\w\n]{0,4}\s*\d+[.)]\s+\S/;
|
|
114
|
+
/** The selector cursor Claude Code paints on the focused option — and on nothing else. */
|
|
115
|
+
const SELECTOR_GLYPH = '❯';
|
|
116
|
+
/**
|
|
117
|
+
* True when the tail shows a FOCUSED selection menu.
|
|
118
|
+
*
|
|
119
|
+
* Requires the selector glyph to sit ON a numbered option line, AND at least two
|
|
120
|
+
* numbered option lines overall. Both halves are load-bearing:
|
|
121
|
+
*
|
|
122
|
+
* - "glyph on the option line" separates a menu from the input box. The box
|
|
123
|
+
* renders `❯` alone on its own line; a menu renders `❯ 1. Yes`. Testing only
|
|
124
|
+
* "the tail contains ❯ somewhere" would classify ordinary assistant output
|
|
125
|
+
* that happens to list `1.` and `2.` above the prompt as a menu, stalling a
|
|
126
|
+
* genuinely ready session — an over-block on the inject path.
|
|
127
|
+
* - "at least two options" separates a menu from a single numbered line of
|
|
128
|
+
* ordinary output.
|
|
129
|
+
*
|
|
130
|
+
* This mirrors `PermissionPromptAutoResolver`'s requirement (a line whose lead
|
|
131
|
+
* glyphs contain ❯ whose text is `N.` + a label) rather than inventing a second,
|
|
132
|
+
* divergent notion of what a menu looks like.
|
|
133
|
+
*/
|
|
134
|
+
export function tailShowsMenu(tail) {
|
|
135
|
+
if (!tail || !tail.includes(SELECTOR_GLYPH))
|
|
136
|
+
return false;
|
|
137
|
+
let options = 0;
|
|
138
|
+
let glyphOnOption = false;
|
|
139
|
+
for (const line of tail.split('\n')) {
|
|
140
|
+
if (!MENU_OPTION_RE.test(line))
|
|
141
|
+
continue;
|
|
142
|
+
options++;
|
|
143
|
+
if (line.includes(SELECTOR_GLYPH))
|
|
144
|
+
glyphOnOption = true;
|
|
145
|
+
}
|
|
146
|
+
return glyphOnOption && options >= 2;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Classify a captured pane tail.
|
|
150
|
+
*
|
|
151
|
+
* `tail` is the joined last-N non-blank lines of a `capture-pane` read. Never pass
|
|
152
|
+
* the whole scrollback: a banner that has scrolled far above the prompt is not
|
|
153
|
+
* evidence either way, and older content only adds false-positive surface.
|
|
154
|
+
*/
|
|
155
|
+
export function classifyPaneReadiness(tail) {
|
|
156
|
+
if (!tail)
|
|
157
|
+
return 'not-ready';
|
|
158
|
+
// A menu is checked FIRST and wins over every positive marker below, because it
|
|
159
|
+
// carries the same `❯` the input box does. Typing here selects an option.
|
|
160
|
+
if (tailShowsMenu(tail))
|
|
161
|
+
return 'menu';
|
|
162
|
+
// The framework prompt character. Codex uses ›; keeping this probe Claude-only
|
|
163
|
+
// previously delayed continuation bootstraps to the full timeout.
|
|
164
|
+
if (tail.includes(SELECTOR_GLYPH) || tail.includes('›'))
|
|
165
|
+
return 'ready';
|
|
166
|
+
// Footer/status-bar strings that only render once the TUI is up.
|
|
167
|
+
if (AT_PROMPT_FOOTERS.some(marker => tail.includes(marker)))
|
|
168
|
+
return 'ready';
|
|
169
|
+
return 'not-ready';
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Boolean façade: can text be typed into this pane right now?
|
|
173
|
+
*
|
|
174
|
+
* A menu answers `false` — correct for every caller that is about to type. A
|
|
175
|
+
* caller whose not-ready branch is DESTRUCTIVE (kill/respawn) must use
|
|
176
|
+
* `classifyPaneReadiness` instead and leave a `menu` pane alone.
|
|
177
|
+
*/
|
|
178
|
+
export function isReadyPromptTail(tail) {
|
|
179
|
+
return classifyPaneReadiness(tail) === 'ready';
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=claudeReadinessProbe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeReadinessProbe.js","sourceRoot":"","sources":["../../src/core/claudeReadinessProbe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FG;AAaH;;;;;;;;;;GAUG;AACH,MAAM,iBAAiB,GAAG;IACxB,oBAAoB;IACpB,iBAAiB;IACjB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,cAAc,GAAG,8BAA8B,CAAC;AAEtD,0FAA0F;AAC1F,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QACzC,OAAO,EAAE,CAAC;QACV,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;YAAE,aAAa,GAAG,IAAI,CAAC;IAC1D,CAAC;IACD,OAAO,aAAa,IAAI,OAAO,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO,WAAW,CAAC;IAE9B,gFAAgF;IAChF,0EAA0E;IAC1E,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAEvC,+EAA+E;IAC/E,kEAAkE;IAClE,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAExE,iEAAiE;IACjE,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAE5E,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC;AACjD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-26T18:49:10.482Z",
|
|
5
|
+
"instarVersion": "1.3.986",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"domain": "identity",
|
|
12
12
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
13
13
|
"installedPath": ".instar/hooks/instar/session-start.sh",
|
|
14
|
-
"contentHash": "
|
|
14
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
15
15
|
"since": "2025-01-01"
|
|
16
16
|
},
|
|
17
17
|
"hook:dangerous-command-guard": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"domain": "safety",
|
|
21
21
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
22
22
|
"installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
|
|
23
|
-
"contentHash": "
|
|
23
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
24
24
|
"since": "2025-01-01"
|
|
25
25
|
},
|
|
26
26
|
"hook:grounding-before-messaging": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"domain": "safety",
|
|
30
30
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
31
31
|
"installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
|
|
32
|
-
"contentHash": "
|
|
32
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
33
33
|
"since": "2025-01-01"
|
|
34
34
|
},
|
|
35
35
|
"hook:compaction-recovery": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"domain": "identity",
|
|
39
39
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
40
40
|
"installedPath": ".instar/hooks/instar/compaction-recovery.sh",
|
|
41
|
-
"contentHash": "
|
|
41
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
42
42
|
"since": "2025-01-01"
|
|
43
43
|
},
|
|
44
44
|
"hook:external-operation-gate": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"domain": "safety",
|
|
48
48
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
49
49
|
"installedPath": ".instar/hooks/instar/external-operation-gate.js",
|
|
50
|
-
"contentHash": "
|
|
50
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
51
51
|
"since": "2025-01-01"
|
|
52
52
|
},
|
|
53
53
|
"hook:deferral-detector": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"domain": "safety",
|
|
57
57
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
58
58
|
"installedPath": ".instar/hooks/instar/deferral-detector.js",
|
|
59
|
-
"contentHash": "
|
|
59
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1242,7 +1242,7 @@
|
|
|
1242
1242
|
"type": "template",
|
|
1243
1243
|
"domain": "operations",
|
|
1244
1244
|
"sourcePath": "src/templates/scripts/telegram-reply.sh",
|
|
1245
|
-
"contentHash": "
|
|
1245
|
+
"contentHash": "4464581188f5c736a62edac5e6a2edecfcfcd365557a18e514b741731bed6e0b",
|
|
1246
1246
|
"since": "2025-01-01"
|
|
1247
1247
|
},
|
|
1248
1248
|
"template:whatsapp-reply.sh": {
|
|
@@ -1538,7 +1538,7 @@
|
|
|
1538
1538
|
"type": "subsystem",
|
|
1539
1539
|
"domain": "sessions",
|
|
1540
1540
|
"sourcePath": "src/core/SessionManager.ts",
|
|
1541
|
-
"contentHash": "
|
|
1541
|
+
"contentHash": "3f88cbed25892776b9eaa258ff68b7eb7b8b676713ea01280899ee8f5b282e96",
|
|
1542
1542
|
"since": "2025-01-01"
|
|
1543
1543
|
},
|
|
1544
1544
|
"subsystem:auto-updater": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "59f279be8e27e1d3f4a19c810f39645b9d63dacfbba3b1cc800a4ffeec67d57d",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
# Usage:
|
|
5
5
|
# ./telegram-reply.sh TOPIC_ID "message text"
|
|
6
6
|
# ./telegram-reply.sh --format markdown TOPIC_ID "**bold**"
|
|
7
|
+
#
|
|
8
|
+
# EVERY FLAG GOES BEFORE THE TOPIC ID. Flag parsing stops at the topic id, so
|
|
9
|
+
# anything after it is message text. A flag in the wrong position used to be
|
|
10
|
+
# sent to the user as literal text with its effect silently dropped; it is now
|
|
11
|
+
# refused. Correct:
|
|
12
|
+
# ./telegram-reply.sh --tone-ack B2_FILE_PATH --tone-reason "why" TOPIC_ID "msg"
|
|
7
13
|
# echo "message text" | ./telegram-reply.sh TOPIC_ID
|
|
8
14
|
# cat <<'EOF' | ./telegram-reply.sh TOPIC_ID
|
|
9
15
|
# Multi-line message here
|
|
@@ -25,6 +31,24 @@
|
|
|
25
31
|
# a blanket pre-ack that silently disables the inform
|
|
26
32
|
# layer; spec outbound-jargon-filepath-gap §2.4(4)).
|
|
27
33
|
#
|
|
34
|
+
# Tone-gate advisory reactions (answering a 422 `tone-gate-advisory`). BOTH
|
|
35
|
+
# forms are recorded as evidence that tunes the gate; neither is optional
|
|
36
|
+
# once you have been handed a decisionRef.
|
|
37
|
+
# --tone-complied <RULE> You agreed with the nudge and revised the text.
|
|
38
|
+
# Grades the check `right`.
|
|
39
|
+
# --tone-ack <RULE> You disagree and are sending unchanged. Grades
|
|
40
|
+
# the check `wrong`. REQUIRES --tone-reason: a
|
|
41
|
+
# reasonless ack is refused and nothing sends,
|
|
42
|
+
# because that reason IS the tuning evidence.
|
|
43
|
+
# --tone-reason "<why>" Why the nudge is wrong in this case.
|
|
44
|
+
# --tone-decision-ref <REF> The decisionRef from the 422, so the reaction
|
|
45
|
+
# joins the verdict it answers.
|
|
46
|
+
#
|
|
47
|
+
# Example (note the ordering — flags first, topic id last):
|
|
48
|
+
# ./telegram-reply.sh --tone-ack B2_FILE_PATH \
|
|
49
|
+
# --tone-reason "the operator asked for the path explicitly" \
|
|
50
|
+
# --tone-decision-ref DQ-1234 29723 "the message"
|
|
51
|
+
#
|
|
28
52
|
# Outbound advisory preflight (inform-only — spec outbound-jargon-filepath-gap §2.4):
|
|
29
53
|
# When this send comes from an automated LLM job session (the scheduler
|
|
30
54
|
# stamps INSTAR_MESSAGE_KIND=automated + INSTAR_SENDER_CLASS=llm-session
|
|
@@ -133,6 +157,43 @@ if [ -z "$TOPIC_ID" ]; then
|
|
|
133
157
|
exit 1
|
|
134
158
|
fi
|
|
135
159
|
|
|
160
|
+
# A flag placed AFTER the topic id was silently swallowed into the message.
|
|
161
|
+
#
|
|
162
|
+
# The parse loop above stops at the first non-flag argument (the topic id), so
|
|
163
|
+
# everything from there on becomes message text via `MSG="$*"` below. A
|
|
164
|
+
# misplaced `--tone-ack` was therefore SENT TO THE USER as visible message body
|
|
165
|
+
# while the override it was meant to carry never reached the server — and,
|
|
166
|
+
# because the flags never applied, the tone gate re-reviewed the send and its
|
|
167
|
+
# verdict was then misread as absurd. That is how a CORRECT check came to be
|
|
168
|
+
# graded `wrong` in the decision-quality data on 2026-07-26. The record is
|
|
169
|
+
# durable; the cause was two characters of argument order.
|
|
170
|
+
#
|
|
171
|
+
# The asymmetry is the defect: a flag-shaped token BEFORE the topic id is fatal
|
|
172
|
+
# ("Unknown flag", above), but after it the script was maximally permissive.
|
|
173
|
+
# Refuse flag-shaped tokens in both positions. This also catches a TYPO'd flag
|
|
174
|
+
# (`--tone-akc`), which is the realistic case and was equally silent.
|
|
175
|
+
#
|
|
176
|
+
# Deliberately strict: any `--*` argument. A message that genuinely needs such a
|
|
177
|
+
# token as literal text goes through stdin, which is the documented primary path
|
|
178
|
+
# and is unaffected by this check ($# is 0 there).
|
|
179
|
+
for _arg in "$@"; do
|
|
180
|
+
case "$_arg" in
|
|
181
|
+
--*)
|
|
182
|
+
echo "Refused: '$_arg' appears AFTER the topic id." >&2
|
|
183
|
+
echo "" >&2
|
|
184
|
+
echo "Flags are only parsed BEFORE the topic id. Placed here it would have been" >&2
|
|
185
|
+
echo "sent to the user as literal message text, and its effect silently dropped." >&2
|
|
186
|
+
echo "" >&2
|
|
187
|
+
echo " Correct: telegram-reply.sh --tone-ack RULE --tone-reason \"why\" $TOPIC_ID \"message\"" >&2
|
|
188
|
+
echo " You ran: telegram-reply.sh $TOPIC_ID ... $_arg ..." >&2
|
|
189
|
+
echo "" >&2
|
|
190
|
+
echo "If '$_arg' is genuinely part of the message text, pipe the message on stdin:" >&2
|
|
191
|
+
echo " cat <<'EOF' | telegram-reply.sh $TOPIC_ID" >&2
|
|
192
|
+
exit 1
|
|
193
|
+
;;
|
|
194
|
+
esac
|
|
195
|
+
done
|
|
196
|
+
|
|
136
197
|
# Read message from args or stdin
|
|
137
198
|
if [ $# -gt 0 ]; then
|
|
138
199
|
MSG="$*"
|