polygram 0.17.1 → 0.17.3
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.
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* `let x = null; wired in main()` pattern, made explicit.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
const ADMIN_CMD_RE = /^\/(model|effort|config|pair-code|pairings|unpair|new|reset|context|compact)(\s|$)/;
|
|
41
|
+
const ADMIN_CMD_RE = /^\/(model|effort|config|pair-code|pairings|unpair|new|reset|clear|context|compact)(\s|$)/;
|
|
42
42
|
const PAIR_CLAIM_RE = /^\/pair\s+\S+/;
|
|
43
43
|
|
|
44
44
|
function createGateInbound({
|
|
@@ -203,7 +203,7 @@ function createQuestionHandlers({
|
|
|
203
203
|
// every group member's message for the whole question lifetime).
|
|
204
204
|
if (!row.awaiting_other) return { consumed: false };
|
|
205
205
|
// /stop, /new and other commands are never consumed as a free-text answer.
|
|
206
|
-
if (/^\/(stop|new|reset|cancel|abort)\b/i.test(String(text || '').trim())) return { consumed: false };
|
|
206
|
+
if (/^\/(stop|new|reset|clear|cancel|abort)\b/i.test(String(text || '').trim())) return { consumed: false };
|
|
207
207
|
// Identity: only the claimed owner supplies the free-text answer.
|
|
208
208
|
const auth = questions.claimOrCheck(row.id, fromId);
|
|
209
209
|
if (!auth.ok) {
|
|
@@ -157,8 +157,8 @@ function createSlashCommands({
|
|
|
157
157
|
return true;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
// /new + /reset — fresh session
|
|
161
|
-
if (botAllowsCommands && (text === '/new' || text === '/reset')) {
|
|
160
|
+
// /new + /reset + /clear — fresh session (all synonyms)
|
|
161
|
+
if (botAllowsCommands && (text === '/new' || text === '/reset' || text === '/clear')) {
|
|
162
162
|
let drained = 0;
|
|
163
163
|
try {
|
|
164
164
|
const r = await pm.resetSession(sessionKey, { reason: text.slice(1) });
|
|
@@ -1839,18 +1839,43 @@ class CliProcess extends Process {
|
|
|
1839
1839
|
clearTimeout(pending._activityQuietTimer);
|
|
1840
1840
|
pending._activityQuietTimer = null;
|
|
1841
1841
|
}
|
|
1842
|
-
|
|
1842
|
+
const fire = () => {
|
|
1843
1843
|
pending._stopGraceTimer = null;
|
|
1844
|
+
// Don't finalize a turn while a sub-agent is provably still in flight — a Stop
|
|
1845
|
+
// that fired at a sub-agent boundary (or during a quiet sub-agent stretch)
|
|
1846
|
+
// would otherwise CLEAR THE REACTION and end the turn mid-work, with the result
|
|
1847
|
+
// arriving later as a detached cycle. Defer: keep the turn (and its 👾 reaction,
|
|
1848
|
+
// held by B3) alive and re-check. Single-pending only — _pendingSubagentStarts
|
|
1849
|
+
// is proc-wide, so don't cross-attribute. The idle/absolute ceilings are
|
|
1850
|
+
// untouched (we don't reset them), so a lost SubagentStop can't hang — the
|
|
1851
|
+
// ceiling backstops it. docs/progress-is-not-turn-end-spec.md
|
|
1852
|
+
if (this.pendingTurns.has(turnId)
|
|
1853
|
+
&& this.pendingTurns.size === 1
|
|
1854
|
+
&& (this._pendingSubagentStarts?.length || 0) > 0) {
|
|
1855
|
+
if (!pending._stopGraceDeferred) {
|
|
1856
|
+
pending._stopGraceDeferred = true;
|
|
1857
|
+
this._logEvent('cli-stop-grace-deferred-subagent', {
|
|
1858
|
+
turn_id: turnId, in_flight: this._pendingSubagentStarts.length,
|
|
1859
|
+
session_id: this.claudeSessionId,
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
pending._stopGracePending = true;
|
|
1863
|
+
pending._stopGraceTimer = setTimeout(fire, this.stopGraceMs);
|
|
1864
|
+
pending._stopGraceTimer.unref?.();
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1844
1867
|
pending._stopGracePending = false;
|
|
1845
1868
|
this._logEvent('cli-turn-resolved-by-stop', {
|
|
1846
1869
|
turn_id: turnId,
|
|
1847
1870
|
reply_count: pending.replies?.length || 0,
|
|
1848
1871
|
via_text_fallback: (pending.replies?.length || 0) === 0,
|
|
1849
1872
|
attributed: pending.seen === true ? 'seen' : 'reply-bound',
|
|
1873
|
+
deferred_for_subagent: pending._stopGraceDeferred === true,
|
|
1850
1874
|
session_id: this.claudeSessionId,
|
|
1851
1875
|
});
|
|
1852
1876
|
this._finalizeTurn(turnId);
|
|
1853
|
-
}
|
|
1877
|
+
};
|
|
1878
|
+
pending._stopGraceTimer = setTimeout(fire, this.stopGraceMs);
|
|
1854
1879
|
pending._stopGraceTimer.unref?.();
|
|
1855
1880
|
}
|
|
1856
1881
|
|
|
@@ -3013,6 +3038,12 @@ class CliProcess extends Process {
|
|
|
3013
3038
|
session_id: this.claudeSessionId,
|
|
3014
3039
|
});
|
|
3015
3040
|
}
|
|
3041
|
+
} else if (p._stopGraceDeferred === true) {
|
|
3042
|
+
// A Stop landed while we're deferring finalize for an in-flight
|
|
3043
|
+
// sub-agent: refresh the captured last_assistant_message so the
|
|
3044
|
+
// eventual finalize delivers the LATEST produced answer (claude's real
|
|
3045
|
+
// end-of-work text), not the boundary Stop's stale/partial text.
|
|
3046
|
+
p._stopHookData = info;
|
|
3016
3047
|
}
|
|
3017
3048
|
} else if (this.pendingTurns.size > 1) {
|
|
3018
3049
|
// Can't attribute Stop to one of several concurrent turns — surface
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "Telegram daemon for Claude Code that preserves the OpenClaw per-chat session model. Migration path for OpenClaw users moving to Claude Code.",
|
|
5
5
|
"main": "lib/ipc/client.js",
|
|
6
6
|
"bin": {
|