jeopi-cli 16.4.0 → 16.4.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 +12 -0
- package/dist/cli.js +1956 -1956
- package/package.json +12 -12
- package/src/session/agent-session.ts +77 -26
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-cli",
|
|
4
|
-
"version": "16.4.
|
|
4
|
+
"version": "16.4.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
58
58
|
"@babel/parser": "^7.29.7",
|
|
59
59
|
"@mozilla/readability": "^0.6.0",
|
|
60
|
-
"jeopi-hashline": "16.4.
|
|
61
|
-
"jeopi-stats": "16.4.
|
|
62
|
-
"jeopi-agent-core": "16.4.
|
|
63
|
-
"jeopi-ai": "16.4.
|
|
64
|
-
"jeopi-catalog": "16.4.
|
|
65
|
-
"jeopi-mnemopi": "16.4.
|
|
66
|
-
"jeopi-natives": "16.4.
|
|
67
|
-
"jeopi-tui": "16.4.
|
|
68
|
-
"jeopi-utils": "16.4.
|
|
69
|
-
"jeopi-wire": "16.4.
|
|
70
|
-
"jeopi-snapcompact": "16.4.
|
|
60
|
+
"jeopi-hashline": "16.4.2",
|
|
61
|
+
"jeopi-stats": "16.4.2",
|
|
62
|
+
"jeopi-agent-core": "16.4.2",
|
|
63
|
+
"jeopi-ai": "16.4.2",
|
|
64
|
+
"jeopi-catalog": "16.4.2",
|
|
65
|
+
"jeopi-mnemopi": "16.4.2",
|
|
66
|
+
"jeopi-natives": "16.4.2",
|
|
67
|
+
"jeopi-tui": "16.4.2",
|
|
68
|
+
"jeopi-utils": "16.4.2",
|
|
69
|
+
"jeopi-wire": "16.4.2",
|
|
70
|
+
"jeopi-snapcompact": "16.4.2",
|
|
71
71
|
"@opentelemetry/api": "^1.9.1",
|
|
72
72
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
73
73
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
serviceTierFamily,
|
|
110
110
|
streamSimple,
|
|
111
111
|
} from "jeopi-ai";
|
|
112
|
+
import { renderDemotedThinking } from "jeopi-ai/dialect";
|
|
112
113
|
import * as AIError from "jeopi-ai/error";
|
|
113
114
|
import { toolWireSchema } from "jeopi-ai/utils/schema";
|
|
114
115
|
import { GeminiHeaderRunDetector, isGeminiThinkingModel } from "jeopi-ai/utils/thinking-loop";
|
|
@@ -521,6 +522,17 @@ const RETRY_BACKOFF_MAX_DELAY_MS = 8_000;
|
|
|
521
522
|
const REFUSAL_BACKOFF_BASE_MS = 2_000;
|
|
522
523
|
const REFUSAL_BACKOFF_MAX_MS = 30_000;
|
|
523
524
|
const REFUSAL_BACKOFF_BUDGET_MS = 30 * 60_000;
|
|
525
|
+
/**
|
|
526
|
+
* Consecutive-use cap on the `reasoning_extraction` fast-degrade path (strip
|
|
527
|
+
* unsigned thinking, resend same-model, zero delay — see
|
|
528
|
+
* `#reasoningExtractionDegradeStreak` below). Bounds the worst case where a
|
|
529
|
+
* pathological classifier state (documented: 1,164 refusals over ~4 hours in
|
|
530
|
+
* one incident) refuses every turn's fresh thinking in a row — after this
|
|
531
|
+
* many back-to-back fast-degrades with no intervening success, fall through
|
|
532
|
+
* to the Rung-4 ladder above, which already wall-clock-bounds via
|
|
533
|
+
* {@link REFUSAL_BACKOFF_BUDGET_MS}.
|
|
534
|
+
*/
|
|
535
|
+
const REASONING_EXTRACTION_DEGRADE_STREAK_CAP = 3;
|
|
524
536
|
|
|
525
537
|
function positiveEnvMs(name: string): number | undefined {
|
|
526
538
|
const raw = process.env[name];
|
|
@@ -1610,13 +1622,31 @@ export class AgentSession {
|
|
|
1610
1622
|
/** Rung-4 classifier-refusal resend streak (post fallback-ladder). */
|
|
1611
1623
|
#refusalBackoff: { startedAt: number; attempt: number } | undefined = undefined;
|
|
1612
1624
|
/**
|
|
1613
|
-
*
|
|
1614
|
-
*
|
|
1615
|
-
* predictably re-refuses
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
|
|
1619
|
-
|
|
1625
|
+
* `reasoning_extraction` classifier refusals are payload-shape-bound (see
|
|
1626
|
+
* advisor/runtime.ts `#includeThinking` degrade): resending the IDENTICAL
|
|
1627
|
+
* batch predictably re-refuses, so a bare retry can't help. But this is a
|
|
1628
|
+
* point-in-time strip of the CURRENT active context, not a persistent
|
|
1629
|
+
* setting — unlike the advisor's permanent-for-the-session
|
|
1630
|
+
* `includeThinking` flag, it does not prevent NEW unsigned thinking from a
|
|
1631
|
+
* LATER turn (a fresh advisor exchange, another interrupt, plain
|
|
1632
|
+
* continued conversation) from independently triggering its own,
|
|
1633
|
+
* genuinely-different refusal. A one-shot latch here previously left every
|
|
1634
|
+
* occurrence after the first to fall through to the generic Rung-4
|
|
1635
|
+
* backoff ladder unstripped — resending the same unstripped payload until
|
|
1636
|
+
* the wall-clock budget expired and the turn failed visibly.
|
|
1637
|
+
*
|
|
1638
|
+
* Fixed by gating on whether {@link #stripUnsignedThinkingForRefusalDegrade}
|
|
1639
|
+
* actually found something to strip THIS time (it already returns
|
|
1640
|
+
* `undefined` when there is nothing new to remove, which is itself proof
|
|
1641
|
+
* the resend would be payload-identical and degrading again cannot help)
|
|
1642
|
+
* rather than a permanent flag — each distinct occurrence gets its own
|
|
1643
|
+
* degrade attempt. Bounded by {@link REASONING_EXTRACTION_DEGRADE_STREAK_CAP}
|
|
1644
|
+
* consecutive uses (reset on any turn that actually succeeds) so a
|
|
1645
|
+
* pathological classifier state that refuses every turn's fresh thinking
|
|
1646
|
+
* in a row still falls through to the wall-clock-bounded ladder instead
|
|
1647
|
+
* of resending at zero delay indefinitely.
|
|
1648
|
+
*/
|
|
1649
|
+
#reasoningExtractionDegradeStreak = 0;
|
|
1620
1650
|
#retryPromise: Promise<void> | undefined = undefined;
|
|
1621
1651
|
#retryResolve: (() => void) | undefined = undefined;
|
|
1622
1652
|
#activeRetryFallback: ActiveRetryFallbackState | undefined = undefined;
|
|
@@ -3327,6 +3357,15 @@ export class AgentSession {
|
|
|
3327
3357
|
* so live render, reload, and Ctrl+L rebuilds keep showing it; `convertToLlm`
|
|
3328
3358
|
* strips the run from the provider request (incomplete/unsigned thinking is
|
|
3329
3359
|
* rejected on resend) when this continuity message follows the assistant turn.
|
|
3360
|
+
*
|
|
3361
|
+
* The reasoning text is rendered through {@link renderDemotedThinking} (keyed
|
|
3362
|
+
* off `message.model`, the model resuming the turn in the overwhelming
|
|
3363
|
+
* majority of cases) before it reaches the template — same disguise every
|
|
3364
|
+
* other thinking-replay site in this codebase already applies before
|
|
3365
|
+
* sending a model's own prior reasoning back to it as plaintext. Raw,
|
|
3366
|
+
* undisguised reasoning wrapped in a "your unfinished internal work state"
|
|
3367
|
+
* frame is exactly the shape Anthropic's `reasoning_extraction` classifier
|
|
3368
|
+
* flags; this was the one remaining site that still shipped it undisguised.
|
|
3330
3369
|
*/
|
|
3331
3370
|
#demoteInterruptedThinkingOnUserInterrupt(
|
|
3332
3371
|
message: AssistantMessage,
|
|
@@ -3335,10 +3374,11 @@ export class AgentSession {
|
|
|
3335
3374
|
const demoted = demoteInterruptedThinking(message);
|
|
3336
3375
|
if (!demoted) return undefined;
|
|
3337
3376
|
const interruptedAt = Date.now();
|
|
3377
|
+
const disguisedReasoning = renderDemotedThinking(message.model, demoted.reasoning).trimEnd();
|
|
3338
3378
|
return {
|
|
3339
3379
|
role: "custom",
|
|
3340
3380
|
customType: INTERRUPTED_THINKING_MESSAGE_TYPE,
|
|
3341
|
-
content: prompt.render(interruptedThinkingTemplate, { reasoning:
|
|
3381
|
+
content: prompt.render(interruptedThinkingTemplate, { reasoning: disguisedReasoning }),
|
|
3342
3382
|
display: false,
|
|
3343
3383
|
details: {
|
|
3344
3384
|
interruptedAt,
|
|
@@ -3663,6 +3703,7 @@ export class AgentSession {
|
|
|
3663
3703
|
});
|
|
3664
3704
|
this.#retryAttempt = 0;
|
|
3665
3705
|
this.#refusalBackoff = undefined;
|
|
3706
|
+
this.#reasoningExtractionDegradeStreak = 0;
|
|
3666
3707
|
}
|
|
3667
3708
|
if (assistantMsg.provider === "opencode-go") {
|
|
3668
3709
|
this.#modelRegistry.authStorage.recordUsageCost(assistantMsg.provider, assistantMsg.usage.cost.total, {
|
|
@@ -13213,29 +13254,39 @@ export class AgentSession {
|
|
|
13213
13254
|
const errorMessage = message.errorMessage || "Unknown error";
|
|
13214
13255
|
// `reasoning_extraction` is payload-shape-bound (see advisor/runtime.ts): resending
|
|
13215
13256
|
// the identical batch predictably re-refuses, unlike probabilistic classifier flags
|
|
13216
|
-
// where backoff/fallback genuinely help.
|
|
13217
|
-
//
|
|
13218
|
-
//
|
|
13219
|
-
//
|
|
13257
|
+
// where backoff/fallback genuinely help. Strip unsigned thinking/redactedThinking
|
|
13258
|
+
// from active context and resend immediately on the same model — before falling
|
|
13259
|
+
// through to the generic fallback-pin / backoff ladder below, which would otherwise
|
|
13260
|
+
// retry the unchanged, re-refusing payload. Gated on the strip itself finding
|
|
13261
|
+
// something to remove THIS time (not a one-shot flag): a later turn can
|
|
13262
|
+
// independently accumulate fresh unsigned thinking and trigger its own genuinely
|
|
13263
|
+
// different refusal, and `#stripUnsignedThinkingForRefusalDegrade` already reports
|
|
13264
|
+
// "nothing to strip" via `undefined` when the payload truly is unchanged — which is
|
|
13265
|
+
// itself the correct signal to fall through instead of degrading again. Bounded by
|
|
13266
|
+
// `REASONING_EXTRACTION_DEGRADE_STREAK_CAP` consecutive uses so a pathological
|
|
13267
|
+
// classifier state refusing every turn's fresh thinking in a row still reaches the
|
|
13268
|
+
// wall-clock-bounded ladder instead of resending at zero delay indefinitely.
|
|
13220
13269
|
if (
|
|
13221
13270
|
classifierRefusal &&
|
|
13222
13271
|
message.stopDetails?.category === "reasoning_extraction" &&
|
|
13223
|
-
|
|
13272
|
+
this.#reasoningExtractionDegradeStreak < REASONING_EXTRACTION_DEGRADE_STREAK_CAP
|
|
13224
13273
|
) {
|
|
13225
|
-
this.#reasoningExtractionDegraded = true;
|
|
13226
13274
|
const stripped = this.#stripUnsignedThinkingForRefusalDegrade(this.agent.state.messages);
|
|
13227
|
-
if (stripped)
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13275
|
+
if (stripped) {
|
|
13276
|
+
this.#reasoningExtractionDegradeStreak++;
|
|
13277
|
+
this.agent.replaceMessages(stripped);
|
|
13278
|
+
this.#removeAssistantMessageFromActiveContext(message, "reasoning-extraction-degrade");
|
|
13279
|
+
await this.#emitSessionEvent({
|
|
13280
|
+
type: "auto_retry_start",
|
|
13281
|
+
attempt: this.#retryAttempt,
|
|
13282
|
+
maxAttempts: retrySettings.maxRetries,
|
|
13283
|
+
delayMs: 0,
|
|
13284
|
+
errorMessage,
|
|
13285
|
+
errorId: message.errorId,
|
|
13286
|
+
});
|
|
13287
|
+
this.#scheduleAgentContinue({ delayMs: 1, generation });
|
|
13288
|
+
return true;
|
|
13289
|
+
}
|
|
13239
13290
|
}
|
|
13240
13291
|
|
|
13241
13292
|
const id = this.#classifyRetryMessage(message);
|