jeo-code 0.7.29 → 0.7.30
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 +11 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/package.json +1 -1
- package/src/agent/engine.ts +27 -14
- package/src/ai/providers/openai-responses.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
|
|
8
8
|
|
|
9
|
+
## [0.7.30] - 2026-07-02
|
|
10
|
+
_GPT/Codex OAuth calls work again, and the 30-minute turn budget now limits no-progress stalls instead of killing genuinely progressing autonomous runs._
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **GPT/Codex OAuth no longer sends `max_output_tokens` to the ChatGPT/Codex backend.** `0.7.28` moved the computed output cap onto the shared Responses payload, but the OAuth Codex endpoint rejects that public-API-only parameter with `HTTP 400 {"detail":"Unsupported parameter: max_output_tokens"}`. The cap is now applied only on the public `/v1/responses` API-key path; OAuth requests keep using the Codex backend shape that `jeo doctor` probes.
|
|
14
|
+
- **`JEO_TURN_MAX_MS` is now a stall budget, not an absolute kill switch.** The old implementation measured 30 minutes from turn start and could terminate a long autonomous run even while it was still executing tools and making progress. The clock now resets on every executed tool step and on mid-turn steering, so it only stops no-progress loops such as refusal backoff spins, endless provider retries, or bounce loops. The existing step hard cap and repeat/cycle/failure guards still bound genuinely long runs.
|
|
15
|
+
|
|
16
|
+
### Verified
|
|
17
|
+
- `bun test` green — 2147 pass / 0 fail across 244 files; `bun run typecheck` clean.
|
|
18
|
+
- Live GPT/Codex OAuth smoke tests through the installed `jeo` symlink: `jeo chat "Say exactly: hello" --model gpt-5.5` and `jeo chat "Reply with one word: pong" --model gpt-5.5` both returned the requested text. Added/updated regression coverage in `test/codex-responses.test.ts` and `test/cycle-and-turn-budget.test.ts`.
|
|
19
|
+
|
|
9
20
|
## [0.7.29] - 2026-07-02
|
|
10
21
|
_gajae-code safety-refusal retry parity: Anthropic/OAuth content-classifier refusals no longer end the turn with a fatal "declined to answer" message after the context-reset ladder._
|
|
11
22
|
|
package/README.ja.md
CHANGED
|
@@ -205,11 +205,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
205
205
|
## 変更履歴 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.30]** (2026-07-02) — GPT/Codex OAuth calls work again, and the 30-minute turn budget now limits no-progress stalls instead of killing genuinely progressing autonomous runs.
|
|
208
209
|
- **[0.7.29]** (2026-07-02) — gajae-code safety-refusal retry parity: Anthropic/OAuth content-classifier refusals no longer end the turn with a fatal "declined to answer" message after the context-reset ladder.
|
|
209
210
|
- **[0.7.28]** (2026-07-02) — gajae-code 0.7.9–0.7.10 OAuth + model-execution parity: generic long rate-limit windows are retried (never fatal), retry waits are abort-cancellable, Codex prompt caching via session correlation, and the Kimi Code device-flow OAuth is completed end-to-end (catalog, gating, discovery, tests).
|
|
210
211
|
- **[0.7.27]** (2026-07-02) — Extended-keyboard decode layer: Esc/Ctrl+C work again on kitty-protocol terminals, Shift+Enter breaks lines on every terminal incl. Windows, row/draft-aware Cmd+←→/↑↓ + Home/End/Ctrl+Home/End, Ctrl+V text-paste fallback. Plus Anthropic 5th-gen omp-parity: catalog-derived output budgets end thinking-vs-reply starvation; refusals fail fast at the transport and the recovery ladder strips replayed thinking blocks.
|
|
211
212
|
- **[0.7.26]** (2026-07-02) — Esc/Ctrl+C reliably clears the input box (fixes a gate the earlier reorder missed), and un-bracketed multi-line pastes no longer shred into premature per-line submits.
|
|
212
|
-
- **[0.7.25]** (2026-07-02) — Shift+Enter now reliably distinguishable from Enter on kitty/WezTerm/iTerm2/xterm via modifyOtherKeys + kitty keyboard protocol; row-aware Home/End and Cmd+Left/Right in multi-row drafts.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -205,11 +205,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
205
205
|
## 변경 이력 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.30]** (2026-07-02) — GPT/Codex OAuth calls work again, and the 30-minute turn budget now limits no-progress stalls instead of killing genuinely progressing autonomous runs.
|
|
208
209
|
- **[0.7.29]** (2026-07-02) — gajae-code safety-refusal retry parity: Anthropic/OAuth content-classifier refusals no longer end the turn with a fatal "declined to answer" message after the context-reset ladder.
|
|
209
210
|
- **[0.7.28]** (2026-07-02) — gajae-code 0.7.9–0.7.10 OAuth + model-execution parity: generic long rate-limit windows are retried (never fatal), retry waits are abort-cancellable, Codex prompt caching via session correlation, and the Kimi Code device-flow OAuth is completed end-to-end (catalog, gating, discovery, tests).
|
|
210
211
|
- **[0.7.27]** (2026-07-02) — Extended-keyboard decode layer: Esc/Ctrl+C work again on kitty-protocol terminals, Shift+Enter breaks lines on every terminal incl. Windows, row/draft-aware Cmd+←→/↑↓ + Home/End/Ctrl+Home/End, Ctrl+V text-paste fallback. Plus Anthropic 5th-gen omp-parity: catalog-derived output budgets end thinking-vs-reply starvation; refusals fail fast at the transport and the recovery ladder strips replayed thinking blocks.
|
|
211
212
|
- **[0.7.26]** (2026-07-02) — Esc/Ctrl+C reliably clears the input box (fixes a gate the earlier reorder missed), and un-bracketed multi-line pastes no longer shred into premature per-line submits.
|
|
212
|
-
- **[0.7.25]** (2026-07-02) — Shift+Enter now reliably distinguishable from Enter on kitty/WezTerm/iTerm2/xterm via modifyOtherKeys + kitty keyboard protocol; row-aware Home/End and Cmd+Left/Right in multi-row drafts.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -232,11 +232,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
|
|
|
232
232
|
## Changelog
|
|
233
233
|
|
|
234
234
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
235
|
+
- **[0.7.30]** (2026-07-02) — GPT/Codex OAuth calls work again, and the 30-minute turn budget now limits no-progress stalls instead of killing genuinely progressing autonomous runs.
|
|
235
236
|
- **[0.7.29]** (2026-07-02) — gajae-code safety-refusal retry parity: Anthropic/OAuth content-classifier refusals no longer end the turn with a fatal "declined to answer" message after the context-reset ladder.
|
|
236
237
|
- **[0.7.28]** (2026-07-02) — gajae-code 0.7.9–0.7.10 OAuth + model-execution parity: generic long rate-limit windows are retried (never fatal), retry waits are abort-cancellable, Codex prompt caching via session correlation, and the Kimi Code device-flow OAuth is completed end-to-end (catalog, gating, discovery, tests).
|
|
237
238
|
- **[0.7.27]** (2026-07-02) — Extended-keyboard decode layer: Esc/Ctrl+C work again on kitty-protocol terminals, Shift+Enter breaks lines on every terminal incl. Windows, row/draft-aware Cmd+←→/↑↓ + Home/End/Ctrl+Home/End, Ctrl+V text-paste fallback. Plus Anthropic 5th-gen omp-parity: catalog-derived output budgets end thinking-vs-reply starvation; refusals fail fast at the transport and the recovery ladder strips replayed thinking blocks.
|
|
238
239
|
- **[0.7.26]** (2026-07-02) — Esc/Ctrl+C reliably clears the input box (fixes a gate the earlier reorder missed), and un-bracketed multi-line pastes no longer shred into premature per-line submits.
|
|
239
|
-
- **[0.7.25]** (2026-07-02) — Shift+Enter now reliably distinguishable from Enter on kitty/WezTerm/iTerm2/xterm via modifyOtherKeys + kitty keyboard protocol; row-aware Home/End and Cmd+Left/Right in multi-row drafts.
|
|
240
240
|
|
|
241
241
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
242
242
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -205,11 +205,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
205
205
|
## 更新日志 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.30]** (2026-07-02) — GPT/Codex OAuth calls work again, and the 30-minute turn budget now limits no-progress stalls instead of killing genuinely progressing autonomous runs.
|
|
208
209
|
- **[0.7.29]** (2026-07-02) — gajae-code safety-refusal retry parity: Anthropic/OAuth content-classifier refusals no longer end the turn with a fatal "declined to answer" message after the context-reset ladder.
|
|
209
210
|
- **[0.7.28]** (2026-07-02) — gajae-code 0.7.9–0.7.10 OAuth + model-execution parity: generic long rate-limit windows are retried (never fatal), retry waits are abort-cancellable, Codex prompt caching via session correlation, and the Kimi Code device-flow OAuth is completed end-to-end (catalog, gating, discovery, tests).
|
|
210
211
|
- **[0.7.27]** (2026-07-02) — Extended-keyboard decode layer: Esc/Ctrl+C work again on kitty-protocol terminals, Shift+Enter breaks lines on every terminal incl. Windows, row/draft-aware Cmd+←→/↑↓ + Home/End/Ctrl+Home/End, Ctrl+V text-paste fallback. Plus Anthropic 5th-gen omp-parity: catalog-derived output budgets end thinking-vs-reply starvation; refusals fail fast at the transport and the recovery ladder strips replayed thinking blocks.
|
|
211
212
|
- **[0.7.26]** (2026-07-02) — Esc/Ctrl+C reliably clears the input box (fixes a gate the earlier reorder missed), and un-bracketed multi-line pastes no longer shred into premature per-line submits.
|
|
212
|
-
- **[0.7.25]** (2026-07-02) — Shift+Enter now reliably distinguishable from Enter on kitty/WezTerm/iTerm2/xterm via modifyOtherKeys + kitty keyboard protocol; row-aware Home/End and Cmd+Left/Right in multi-row drafts.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
package/src/agent/engine.ts
CHANGED
|
@@ -286,10 +286,12 @@ export interface AgentLoopResult {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
|
|
289
|
-
/** Wall-clock budget for ONE agent turn (ms)
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* the
|
|
289
|
+
/** Wall-clock STALL budget for ONE agent turn (ms): the maximum time a turn may run
|
|
290
|
+
* WITHOUT PROGRESS (no executed tool step). JEO_TURN_MAX_MS overrides; 0 disables.
|
|
291
|
+
* Default 30 minutes: long autonomous runs that keep executing tools stay alive
|
|
292
|
+
* indefinitely (the step budget's hard cap bounds them), while a turn that spins in
|
|
293
|
+
* "thinking" (refusal backoff, endless provider retries) is guaranteed to terminate
|
|
294
|
+
* into the consolidation wrap-up instead of running for hours. */
|
|
293
295
|
export function turnMaxMs(env: Record<string, string | undefined> = process.env): number {
|
|
294
296
|
const raw = jeoEnv("TURN_MAX_MS", env);
|
|
295
297
|
if (raw !== undefined && raw !== "") {
|
|
@@ -364,12 +366,18 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
364
366
|
const ev = opts.events ?? {};
|
|
365
367
|
const maxHistoryTokens = Math.max(10_000, opts.maxHistoryTokens ?? 80_000);
|
|
366
368
|
|
|
367
|
-
// Wall-clock
|
|
368
|
-
// guarantee. Step budgets bound the COUNT of model calls; this bounds
|
|
369
|
-
//
|
|
370
|
-
//
|
|
369
|
+
// Wall-clock STALL budget — the definitive "never sits in thinking forever"
|
|
370
|
+
// guarantee. Step budgets bound the COUNT of model calls; this bounds the time a
|
|
371
|
+
// turn may spend WITHOUT PROGRESS (no executed tool step): refusal-backoff spins,
|
|
372
|
+
// endless provider retries, and bounce loops terminate into the consolidation
|
|
373
|
+
// wrap-up, while a long-running turn that keeps executing tools stays alive —
|
|
374
|
+
// its termination is owned by the step budget's hard cap and the spin guards.
|
|
375
|
+
// Field regression this replaces: an ABSOLUTE 30m budget measured from turn start
|
|
376
|
+
// killed genuinely progressing autonomous runs mid-work.
|
|
371
377
|
const turnStartedAt = Date.now();
|
|
372
378
|
const turnBudgetMs = turnMaxMs();
|
|
379
|
+
// Reset on every executed tool step and on mid-turn steering.
|
|
380
|
+
let lastProgressAt = turnStartedAt;
|
|
373
381
|
// "steps" | "time" — drives honest wording in the consolidation message.
|
|
374
382
|
let stopKind: "steps" | "time" = "steps";
|
|
375
383
|
let step = 1;
|
|
@@ -449,7 +457,8 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
449
457
|
// ladder is exhausted the loop keeps resending with capped exponential backoff
|
|
450
458
|
// instead of surfacing a terminal error (gjc parity: a refusal is retried until
|
|
451
459
|
// it clears or the user cancels — Esc aborts the backoff wait via opts.signal,
|
|
452
|
-
// and the
|
|
460
|
+
// and the stall budget bounds the spin: refusals execute no tools, so the
|
|
461
|
+
// no-progress clock keeps ticking and terminates the turn at JEO_TURN_MAX_MS).
|
|
453
462
|
const MAX_REFUSAL_RETRIES = GUARD_LIMITS.MAX_REFUSAL_RETRIES;
|
|
454
463
|
let refusalRetries = 0;
|
|
455
464
|
let lastSig = "";
|
|
@@ -475,9 +484,9 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
475
484
|
// schemas ONCE instead of rebuilding/reallocating the list on every step.
|
|
476
485
|
const turnToolSchemas = nativeToolSchemasFor(Object.keys(tools));
|
|
477
486
|
while (true) {
|
|
478
|
-
if (turnBudgetMs > 0 && Date.now() -
|
|
487
|
+
if (turnBudgetMs > 0 && Date.now() - lastProgressAt > turnBudgetMs) {
|
|
479
488
|
stopKind = "time";
|
|
480
|
-
budgetStopReason = `
|
|
489
|
+
budgetStopReason = `no tool progress for ${Math.round(turnBudgetMs / 60_000)}m — turn stall budget (JEO_TURN_MAX_MS) exceeded without done`;
|
|
481
490
|
break;
|
|
482
491
|
}
|
|
483
492
|
if (step > budget.limit()) {
|
|
@@ -515,6 +524,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
515
524
|
consecutiveFailures = 0;
|
|
516
525
|
recentStepSigs.length = 0;
|
|
517
526
|
budget.noteSteer?.();
|
|
527
|
+
lastProgressAt = Date.now(); // fresh instruction = fresh progress window
|
|
518
528
|
}
|
|
519
529
|
}
|
|
520
530
|
|
|
@@ -618,8 +628,9 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
618
628
|
// backoff instead of surfacing a terminal error (gjc parity: gjc's
|
|
619
629
|
// session auto-retry classifies a refusal as retryable and never ends
|
|
620
630
|
// the turn on it). Esc/cancel aborts the wait via opts.signal and the
|
|
621
|
-
// loop-top check turns it into "Cancelled."; JEO_TURN_MAX_MS
|
|
622
|
-
// bounds the
|
|
631
|
+
// loop-top check turns it into "Cancelled."; the JEO_TURN_MAX_MS stall
|
|
632
|
+
// budget still bounds the spin (a refusal loop executes no tools, so
|
|
633
|
+
// the no-progress clock never resets).
|
|
623
634
|
if (isRefusalError(err)) {
|
|
624
635
|
refusalRetries++;
|
|
625
636
|
if (refusalRetries === 1) {
|
|
@@ -1133,6 +1144,8 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
1133
1144
|
// repeated call's ACTUAL last outcome (A). A skipped/bounced step never reaches
|
|
1134
1145
|
// here, so this always holds the last REAL execution's results.
|
|
1135
1146
|
lastResults = results;
|
|
1147
|
+
// Executed tool step = progress: the stall budget clocks time WITHOUT this.
|
|
1148
|
+
if (results.some(r => r.executed)) lastProgressAt = Date.now();
|
|
1136
1149
|
step++;
|
|
1137
1150
|
}
|
|
1138
1151
|
|
|
@@ -1143,7 +1156,7 @@ export async function runAgentLoop(history: Message[], opts: AgentLoopOptions):
|
|
|
1143
1156
|
const extInfo = budget.extensionsUsed() > 0 ? ` after ${budget.extensionsUsed()} extension(s)` : "";
|
|
1144
1157
|
const stopInfo = budgetStopReason ? `; ${budgetStopReason}` : "";
|
|
1145
1158
|
const budgetLabel = stopKind === "time"
|
|
1146
|
-
? `turn time budget of ${Math.round(turnBudgetMs / 60_000)}m reached`
|
|
1159
|
+
? `turn time budget of ${Math.round(turnBudgetMs / 60_000)}m reached (no tool progress)`
|
|
1147
1160
|
: `step budget of ${budget.limit()} reached`;
|
|
1148
1161
|
try {
|
|
1149
1162
|
if (!opts.signal?.aborted) {
|
|
@@ -126,13 +126,14 @@ export function codexResponsesRequest(
|
|
|
126
126
|
// frame can show the model's thinking instead of a frozen "calling model (Ns)…".
|
|
127
127
|
payload.reasoning = { effort: options.reasoningEffort, summary: "auto" };
|
|
128
128
|
}
|
|
129
|
-
// Cap the response length (gjc parity)
|
|
130
|
-
//
|
|
131
|
-
|
|
129
|
+
// Cap the response length (gjc parity) on the PUBLIC Responses API only. The ChatGPT/Codex
|
|
130
|
+
// OAuth backend 400s on this parameter ({"detail":"Unsupported parameter: max_output_tokens"}),
|
|
131
|
+
// so the cap is applied inside the api_key branch below.
|
|
132
132
|
// OAuth → the undocumented ChatGPT/Codex backend (codex headers + account-id).
|
|
133
133
|
// API key → the public OpenAI Responses API (`/v1/responses`) with a plain Bearer.
|
|
134
134
|
// Both speak the same Responses schema (the body above), so only url+headers differ.
|
|
135
135
|
if (credential.kind === "api_key") {
|
|
136
|
+
if (options.maxTokens) payload.max_output_tokens = options.maxTokens;
|
|
136
137
|
const base = (options.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
137
138
|
// Stateless reasoning replay (public Responses API): ask for encrypted reasoning content
|
|
138
139
|
// so it can be captured and threaded back into a later `input` (store stays false).
|