autodev-cli 1.4.187 → 1.4.189
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/out/dispatcher.js +5 -2
- package/out/dispatcher.js.map +1 -1
- package/out/providers/claude/ProviderManager.d.ts +8 -1
- package/out/providers/claude/ProviderManager.js +16 -2
- package/out/providers/claude/ProviderManager.js.map +1 -1
- package/out/providers/claude/tui.d.ts +4 -0
- package/out/providers/claude/tui.js +50 -0
- package/out/providers/claude/tui.js.map +1 -1
- package/out/providers/copilot/ProviderManager.d.ts +8 -1
- package/out/providers/copilot/ProviderManager.js +14 -1
- package/out/providers/copilot/ProviderManager.js.map +1 -1
- package/out/providers/copilot/sdk.d.ts +4 -0
- package/out/providers/copilot/sdk.js +47 -0
- package/out/providers/copilot/sdk.js.map +1 -1
- package/out/providers/grok/GrokAcpSession.d.ts +122 -0
- package/out/providers/grok/GrokAcpSession.js +506 -0
- package/out/providers/grok/GrokAcpSession.js.map +1 -0
- package/out/providers/grok/tui.d.ts +25 -0
- package/out/providers/grok/tui.js +220 -0
- package/out/providers/grok/tui.js.map +1 -1
- package/out/providers/opencode/ProviderManager.d.ts +8 -1
- package/out/providers/opencode/ProviderManager.js +15 -1
- package/out/providers/opencode/ProviderManager.js.map +1 -1
- package/out/providers/opencode/sdk.d.ts +4 -0
- package/out/providers/opencode/sdk.js +50 -0
- package/out/providers/opencode/sdk.js.map +1 -1
- package/out/taskLoop.js +108 -48
- package/out/taskLoop.js.map +1 -1
- package/package.json +2 -2
package/out/taskLoop.js
CHANGED
|
@@ -72,6 +72,27 @@ const projectSkills_1 = require("./core/projectSkills");
|
|
|
72
72
|
const configManager_1 = require("./configManager");
|
|
73
73
|
const agentBackup_1 = require("./agentBackup");
|
|
74
74
|
const version_1 = require("./version");
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Persistent-CLI predicates (Phase 2). A CLI provider that runs through its
|
|
77
|
+
// persistent sibling's in-process client this turn must be treated by the loop
|
|
78
|
+
// exactly like that sibling (busy flag, mid-turn steer, session capture, close,
|
|
79
|
+
// compact/clear) instead of the exit-file/command path. These mirror the gate
|
|
80
|
+
// the dispatcher uses, so both agree per turn. When the gate is off (or the
|
|
81
|
+
// transport is unavailable) they return false and the legacy command handling
|
|
82
|
+
// applies — nothing regresses.
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/** claude-cli is reusing the claude-tui StructuredClient this turn. */
|
|
85
|
+
function claudeCliIsPersistent(p) { return p === 'claude-cli' && (0, claudeTuiProvider_1.claudeCliPersistentEnabled)(); }
|
|
86
|
+
/** copilot-cli is reusing the copilot-sdk LocalSession this turn. */
|
|
87
|
+
function copilotCliIsPersistent(p) { return p === 'copilot-cli' && (0, copilotSdkProvider_1.copilotCliPersistentEnabled)(); }
|
|
88
|
+
/** opencode-cli is reusing the opencode-sdk server+session this turn. */
|
|
89
|
+
function opencodeCliIsPersistent(p) { return p === 'opencode-cli' && (0, opencodeSdkProvider_1.opencodeCliPersistentEnabled)(); }
|
|
90
|
+
/** Provider runs on the persistent claude-tui client (native or claude-cli routed). */
|
|
91
|
+
function usesClaudeTuiClient(p) { return p === 'claude-tui' || claudeCliIsPersistent(p); }
|
|
92
|
+
/** Provider runs on the persistent copilot-sdk session (native or copilot-cli routed). */
|
|
93
|
+
function usesCopilotSdkSession(p) { return p === 'copilot-sdk' || copilotCliIsPersistent(p); }
|
|
94
|
+
/** Provider runs on the persistent opencode-sdk server (native or opencode-cli routed). */
|
|
95
|
+
function usesOpencodeSdkServer(p) { return p === 'opencode-sdk' || opencodeCliIsPersistent(p); }
|
|
75
96
|
/**
|
|
76
97
|
* After `_waitForTaskCompletion` resolves, decide whether the SUCCESS/completion
|
|
77
98
|
* path (`task_done` → COMPLETED, `task_progress` → WORKING, completedCount++)
|
|
@@ -373,7 +394,7 @@ class TaskLoopRunner {
|
|
|
373
394
|
this._compacting = true;
|
|
374
395
|
log(`🗜 Manual compact triggered (provider: ${provider})`);
|
|
375
396
|
try {
|
|
376
|
-
if (provider === 'claude-cli') {
|
|
397
|
+
if (provider === 'claude-cli' && !claudeCliIsPersistent(provider)) {
|
|
377
398
|
let sid = (0, sessionState_1.getSessionId)(root, 'claude-cli');
|
|
378
399
|
if (!sid) {
|
|
379
400
|
sid = (0, dispatcher_1.findLatestClaudeSession)(root);
|
|
@@ -386,8 +407,10 @@ class TaskLoopRunner {
|
|
|
386
407
|
log('⚠️ Compact: no Claude session ID found');
|
|
387
408
|
}
|
|
388
409
|
}
|
|
389
|
-
else if (provider
|
|
390
|
-
|
|
410
|
+
else if (usesClaudeTuiClient(provider)) {
|
|
411
|
+
// claude-tui OR a persistent claude-cli — compact the LIVE StructuredClient
|
|
412
|
+
// (spawning a separate `claude … /compact` would fight the held session).
|
|
413
|
+
let sid = (0, sessionState_1.getSessionId)(root, provider);
|
|
391
414
|
if (!sid) {
|
|
392
415
|
sid = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(root);
|
|
393
416
|
}
|
|
@@ -399,7 +422,7 @@ class TaskLoopRunner {
|
|
|
399
422
|
log('⚠️ Compact: no Claude TUI session ID found');
|
|
400
423
|
}
|
|
401
424
|
}
|
|
402
|
-
else if (provider === 'opencode-cli') {
|
|
425
|
+
else if (provider === 'opencode-cli' && !opencodeCliIsPersistent(provider)) {
|
|
403
426
|
let sid = (0, sessionState_1.getSessionId)(root, 'opencode-cli');
|
|
404
427
|
if (!sid) {
|
|
405
428
|
sid = await (0, opencodeCliProvider_1.getLatestOpenCodeSessionId)(root, log);
|
|
@@ -412,7 +435,7 @@ class TaskLoopRunner {
|
|
|
412
435
|
log('⚠️ Compact: no OpenCode session ID found');
|
|
413
436
|
}
|
|
414
437
|
}
|
|
415
|
-
else if (provider
|
|
438
|
+
else if (usesOpencodeSdkServer(provider)) {
|
|
416
439
|
await (0, opencodeSdkProvider_1.runOpencodeSdkCompact)(root, log);
|
|
417
440
|
log('🗜 Compact complete (opencode-sdk)');
|
|
418
441
|
}
|
|
@@ -433,7 +456,7 @@ class TaskLoopRunner {
|
|
|
433
456
|
const log = (m) => this._cb?.log(m);
|
|
434
457
|
log(`🗑 Manual /clear triggered (provider: ${provider})`);
|
|
435
458
|
try {
|
|
436
|
-
if (provider === 'claude-cli') {
|
|
459
|
+
if (provider === 'claude-cli' && !claudeCliIsPersistent(provider)) {
|
|
437
460
|
let sid = (0, sessionState_1.getSessionId)(root, 'claude-cli');
|
|
438
461
|
if (!sid) {
|
|
439
462
|
sid = (0, dispatcher_1.findLatestClaudeSession)(root);
|
|
@@ -447,14 +470,15 @@ class TaskLoopRunner {
|
|
|
447
470
|
log('⚠️ /clear: no Claude session ID found');
|
|
448
471
|
}
|
|
449
472
|
}
|
|
450
|
-
else if (provider
|
|
451
|
-
|
|
473
|
+
else if (usesClaudeTuiClient(provider)) {
|
|
474
|
+
// claude-tui OR a persistent claude-cli — /clear the LIVE StructuredClient.
|
|
475
|
+
let sid = (0, sessionState_1.getSessionId)(root, provider);
|
|
452
476
|
if (!sid) {
|
|
453
477
|
sid = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(root);
|
|
454
478
|
}
|
|
455
479
|
if (sid) {
|
|
456
480
|
await (0, claudeTuiProvider_1.runClaudeTuiClear)(root, sid, log);
|
|
457
|
-
(0, sessionState_1.clearSessionId)(root,
|
|
481
|
+
(0, sessionState_1.clearSessionId)(root, provider);
|
|
458
482
|
log('🗑 /clear (TUI) complete — session reset');
|
|
459
483
|
}
|
|
460
484
|
else {
|
|
@@ -1118,14 +1142,16 @@ class TaskLoopRunner {
|
|
|
1118
1142
|
const root = this._workspaceRoot;
|
|
1119
1143
|
const provider = this._cb?.getActiveProvider() ?? '';
|
|
1120
1144
|
if (root) {
|
|
1121
|
-
if (provider
|
|
1122
|
-
|
|
1145
|
+
if (usesClaudeTuiClient(provider)) {
|
|
1146
|
+
// claude-tui OR a persistent claude-cli — clear the LIVE StructuredClient.
|
|
1147
|
+
const claudeProvider = provider;
|
|
1148
|
+
let sessionId = (0, sessionState_1.getSessionId)(root, claudeProvider);
|
|
1123
1149
|
if (!sessionId) {
|
|
1124
1150
|
sessionId = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(root);
|
|
1125
1151
|
}
|
|
1126
1152
|
if (sessionId) {
|
|
1127
1153
|
(0, claudeTuiProvider_1.runClaudeTuiClear)(root, sessionId, msg => this._cb?.log(msg))
|
|
1128
|
-
.then(() => { (0, sessionState_1.clearSessionId)(root,
|
|
1154
|
+
.then(() => { (0, sessionState_1.clearSessionId)(root, claudeProvider); this._cb?.log('🗑 /clear (TUI) complete'); })
|
|
1129
1155
|
.catch(e => this._cb?.log(`⚠️ /clear (TUI) failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
1130
1156
|
}
|
|
1131
1157
|
else {
|
|
@@ -1133,7 +1159,7 @@ class TaskLoopRunner {
|
|
|
1133
1159
|
}
|
|
1134
1160
|
}
|
|
1135
1161
|
else {
|
|
1136
|
-
// Default to claude-cli
|
|
1162
|
+
// Default to claude-cli (legacy per-turn command path)
|
|
1137
1163
|
let sessionId = (0, sessionState_1.getSessionId)(root, 'claude-cli');
|
|
1138
1164
|
if (!sessionId) {
|
|
1139
1165
|
sessionId = (0, dispatcher_1.findLatestClaudeSession)(root);
|
|
@@ -1187,7 +1213,7 @@ class TaskLoopRunner {
|
|
|
1187
1213
|
// `_currentTask` routes an idle steer to the buffer path below, which wakes the
|
|
1188
1214
|
// idle sleep and drains it into a real turn immediately.
|
|
1189
1215
|
const turnInFlight = !!this._currentTask;
|
|
1190
|
-
if (root && turnInFlight && provider
|
|
1216
|
+
if (root && turnInFlight && usesClaudeTuiClient(provider) && (0, claudeTuiProvider_1.isClaudeTuiBusy)(root)) {
|
|
1191
1217
|
if (await (0, claudeTuiProvider_1.steerClaudeTui)(root, clean, msg => this._cb?.log(msg))) {
|
|
1192
1218
|
this._cb?.log(`⚡ Steered mid-turn: "${clean.slice(0, 80)}"`);
|
|
1193
1219
|
onDelivered?.(); // durably injected into the live turn
|
|
@@ -1198,7 +1224,7 @@ class TaskLoopRunner {
|
|
|
1198
1224
|
// injects into the running turn — true mid-turn steering, same as claude-tui.
|
|
1199
1225
|
// (copilot-CLI is a one-shot subprocess with no live turn to inject into, so
|
|
1200
1226
|
// it always falls through to the next-turn TODO queue below.)
|
|
1201
|
-
if (root && turnInFlight && provider
|
|
1227
|
+
if (root && turnInFlight && usesCopilotSdkSession(provider) && (0, copilotSdkProvider_1.isCopilotSdkBusy)(root)) {
|
|
1202
1228
|
if (await (0, copilotSdkProvider_1.steerCopilotSdk)(root, clean, msg => this._cb?.log(msg))) {
|
|
1203
1229
|
this._cb?.log(`⚡ Steered mid-turn (copilot-sdk): "${clean.slice(0, 80)}"`);
|
|
1204
1230
|
onDelivered?.();
|
|
@@ -1207,7 +1233,7 @@ class TaskLoopRunner {
|
|
|
1207
1233
|
}
|
|
1208
1234
|
// opencode-sdk keeps a persistent in-process server + session; a prompt sent to
|
|
1209
1235
|
// the live session mid-turn is picked up by the running turn (interrupt + continue).
|
|
1210
|
-
if (root && turnInFlight && provider
|
|
1236
|
+
if (root && turnInFlight && usesOpencodeSdkServer(provider) && (0, opencodeSdkProvider_1.isOpencodeSdkBusy)(root)) {
|
|
1211
1237
|
if (await (0, opencodeSdkProvider_1.steerOpencodeSdk)(root, clean, msg => this._cb?.log(msg))) {
|
|
1212
1238
|
this._cb?.log(`⚡ Steered mid-turn (opencode-sdk): "${clean.slice(0, 80)}"`);
|
|
1213
1239
|
onDelivered?.();
|
|
@@ -1225,6 +1251,16 @@ class TaskLoopRunner {
|
|
|
1225
1251
|
return;
|
|
1226
1252
|
}
|
|
1227
1253
|
}
|
|
1254
|
+
// Persistent-process grok (ACP `grok agent stdio`, grok-cli AND grok-tui):
|
|
1255
|
+
// a mid-run steer is a QUEUED session/prompt delivered to the LIVE process —
|
|
1256
|
+
// it never exits, so the steer reaches the same long-lived agent.
|
|
1257
|
+
if (root && turnInFlight && (provider === 'grok-cli' || provider === 'grok-tui') && (0, grokTuiProvider_1.isGrokAcpBusy)(root)) {
|
|
1258
|
+
if ((0, grokTuiProvider_1.steerGrokAcp)(root, clean, msg => this._cb?.log(msg))) {
|
|
1259
|
+
this._cb?.log(`⚡ Steered mid-turn (grok ACP): "${clean.slice(0, 80)}"`);
|
|
1260
|
+
onDelivered?.();
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1228
1264
|
// Fallback: the provider can't be steered mid-turn (grok-*/copilot-cli are
|
|
1229
1265
|
// one-shot subprocesses). Do NOT append to TODO here — a provider that edits
|
|
1230
1266
|
// TODO.md itself as it runs (grok manages its own checklist) would clobber a
|
|
@@ -1562,13 +1598,16 @@ class TaskLoopRunner {
|
|
|
1562
1598
|
// turn takes >30 s to finish. Instead, use the in-flight turn flag which
|
|
1563
1599
|
// stays true for the entire duration of the async, including after
|
|
1564
1600
|
// _waitForTaskCompletion resolves but before the turn emits 'result'.
|
|
1565
|
-
|
|
1601
|
+
// claude-tui OR a persistent claude-cli — both track the turn via the
|
|
1602
|
+
// shared in-memory busy flag on the StructuredClient (honest signal even
|
|
1603
|
+
// for turns >30s, unlike the exit-file sentinel).
|
|
1604
|
+
if (usesClaudeTuiClient(provider)) {
|
|
1566
1605
|
return (0, claudeTuiProvider_1.isClaudeTuiBusy)(this._workspaceRoot);
|
|
1567
1606
|
}
|
|
1568
|
-
if (provider
|
|
1607
|
+
if (usesCopilotSdkSession(provider)) {
|
|
1569
1608
|
return (0, copilotSdkProvider_1.isCopilotSdkBusy)(this._workspaceRoot);
|
|
1570
1609
|
}
|
|
1571
|
-
if (provider
|
|
1610
|
+
if (usesOpencodeSdkServer(provider)) {
|
|
1572
1611
|
return (0, opencodeSdkProvider_1.isOpencodeSdkBusy)(this._workspaceRoot);
|
|
1573
1612
|
}
|
|
1574
1613
|
// grok-tui: like claude-tui, it runs a PERSISTENT session (tmux PTY, or a
|
|
@@ -1958,7 +1997,7 @@ class TaskLoopRunner {
|
|
|
1958
1997
|
// The exit-code file is written only after the shell command completes,
|
|
1959
1998
|
// so a non-empty file guarantees the stdout file is fully flushed.
|
|
1960
1999
|
const activeProvider = this._cb?.getActiveProvider();
|
|
1961
|
-
if (this._workspaceRoot && activeProvider
|
|
2000
|
+
if (this._workspaceRoot && usesClaudeTuiClient(activeProvider)) {
|
|
1962
2001
|
// claude-tui: _waitForTaskCompletion resolves as soon as the task is
|
|
1963
2002
|
// marked [x] in TODO.md, but the persistent async turn may still be
|
|
1964
2003
|
// running (Claude executing further tool calls, marking other tasks [~],
|
|
@@ -1989,7 +2028,7 @@ class TaskLoopRunner {
|
|
|
1989
2028
|
(0, claudeTuiProvider_1.forceIdleClaudeTui)(this._workspaceRoot);
|
|
1990
2029
|
}
|
|
1991
2030
|
}
|
|
1992
|
-
else if (this._workspaceRoot && activeProvider
|
|
2031
|
+
else if (this._workspaceRoot && usesCopilotSdkSession(activeProvider)) {
|
|
1993
2032
|
const tuiDeadline = Date.now() + 10 * 60_000;
|
|
1994
2033
|
while ((0, copilotSdkProvider_1.isCopilotSdkBusy)(this._workspaceRoot) && Date.now() < tuiDeadline) {
|
|
1995
2034
|
if (this._state !== 'running') {
|
|
@@ -2001,7 +2040,7 @@ class TaskLoopRunner {
|
|
|
2001
2040
|
this._cb?.log('⚠️ Copilot TUI turn did not complete within 10 minutes — moving on');
|
|
2002
2041
|
}
|
|
2003
2042
|
}
|
|
2004
|
-
else if (this._workspaceRoot && activeProvider
|
|
2043
|
+
else if (this._workspaceRoot && usesOpencodeSdkServer(activeProvider)) {
|
|
2005
2044
|
// opencode-sdk: same pattern as claude-tui — wait for the in-flight
|
|
2006
2045
|
// fire-and-forget async to fully complete (session.idle received).
|
|
2007
2046
|
// Use activity-based deadline: reset the 30-minute window on each tool
|
|
@@ -2095,7 +2134,7 @@ class TaskLoopRunner {
|
|
|
2095
2134
|
}
|
|
2096
2135
|
// Capture and persist CLI session ID so the next task can resume it
|
|
2097
2136
|
if (this._workspaceRoot && activeProvider && providers_1.PROVIDERS[activeProvider]?.isCli) {
|
|
2098
|
-
if (activeProvider === 'opencode-cli') {
|
|
2137
|
+
if (activeProvider === 'opencode-cli' && !opencodeCliIsPersistent(activeProvider)) {
|
|
2099
2138
|
// Prefer the session ID from hooks events (fast, no subprocess);
|
|
2100
2139
|
// fall back to `opencode session list` if hooks haven't fired yet.
|
|
2101
2140
|
// Pass taskStartTime so we ignore stale/foreign hooks events from
|
|
@@ -2113,10 +2152,13 @@ class TaskLoopRunner {
|
|
|
2113
2152
|
.catch(() => { });
|
|
2114
2153
|
}
|
|
2115
2154
|
}
|
|
2116
|
-
else if (activeProvider
|
|
2155
|
+
else if (usesClaudeTuiClient(activeProvider)) {
|
|
2156
|
+
// claude-tui OR a persistent claude-cli — the StructuredClient owns the
|
|
2157
|
+
// real ~/.claude session uuid; persist it under the ACTIVE provider key
|
|
2158
|
+
// so a (re)start resumes the same session (works for command fallback too).
|
|
2117
2159
|
const sid = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(this._workspaceRoot);
|
|
2118
2160
|
if (sid) {
|
|
2119
|
-
(0, sessionState_1.saveSessionId)(this._workspaceRoot,
|
|
2161
|
+
(0, sessionState_1.saveSessionId)(this._workspaceRoot, activeProvider, sid);
|
|
2120
2162
|
}
|
|
2121
2163
|
}
|
|
2122
2164
|
else if (activeProvider === 'copilot-sdk') {
|
|
@@ -2125,10 +2167,13 @@ class TaskLoopRunner {
|
|
|
2125
2167
|
(0, sessionState_1.saveSessionId)(this._workspaceRoot, 'copilot-sdk', sid);
|
|
2126
2168
|
}
|
|
2127
2169
|
}
|
|
2128
|
-
else if (activeProvider
|
|
2170
|
+
else if (usesOpencodeSdkServer(activeProvider)) {
|
|
2171
|
+
// opencode-sdk OR a persistent opencode-cli — persist the real SDK
|
|
2172
|
+
// session id under the ACTIVE provider key so the next turn RESUMES it
|
|
2173
|
+
// (never mints a fresh session — see memory opencode-session-explosion).
|
|
2129
2174
|
const sid = (0, opencodeSdkProvider_1.getOpencodeSdkLatestSessionId)(this._workspaceRoot);
|
|
2130
2175
|
if (sid) {
|
|
2131
|
-
(0, sessionState_1.saveSessionId)(this._workspaceRoot,
|
|
2176
|
+
(0, sessionState_1.saveSessionId)(this._workspaceRoot, activeProvider, sid);
|
|
2132
2177
|
}
|
|
2133
2178
|
}
|
|
2134
2179
|
else {
|
|
@@ -2302,7 +2347,7 @@ class TaskLoopRunner {
|
|
|
2302
2347
|
this._cb?.log(`🗜 Auto-compact triggered after ${compactInterval} tasks (provider: ${acProvider})`);
|
|
2303
2348
|
this._notifyDiscord(`🗜 Auto-compact triggered after ${compactInterval} tasks`);
|
|
2304
2349
|
try {
|
|
2305
|
-
if (acProvider === 'claude-cli') {
|
|
2350
|
+
if (acProvider === 'claude-cli' && !claudeCliIsPersistent(acProvider)) {
|
|
2306
2351
|
let sid = (0, sessionState_1.getSessionId)(this._workspaceRoot, 'claude-cli');
|
|
2307
2352
|
if (!sid) {
|
|
2308
2353
|
sid = (0, dispatcher_1.findLatestClaudeSession)(this._workspaceRoot);
|
|
@@ -2315,8 +2360,9 @@ class TaskLoopRunner {
|
|
|
2315
2360
|
this._cb?.log('⚠️ Auto-compact: no Claude session ID found — skipping');
|
|
2316
2361
|
}
|
|
2317
2362
|
}
|
|
2318
|
-
else if (acProvider
|
|
2319
|
-
|
|
2363
|
+
else if (usesClaudeTuiClient(acProvider)) {
|
|
2364
|
+
// claude-tui OR a persistent claude-cli — compact the LIVE client.
|
|
2365
|
+
let sid = (0, sessionState_1.getSessionId)(this._workspaceRoot, acProvider);
|
|
2320
2366
|
if (!sid) {
|
|
2321
2367
|
sid = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(this._workspaceRoot);
|
|
2322
2368
|
}
|
|
@@ -2328,10 +2374,10 @@ class TaskLoopRunner {
|
|
|
2328
2374
|
this._cb?.log('⚠️ Auto-compact: no Claude TUI session ID found — skipping');
|
|
2329
2375
|
}
|
|
2330
2376
|
}
|
|
2331
|
-
else if (acProvider
|
|
2377
|
+
else if (usesCopilotSdkSession(acProvider)) {
|
|
2332
2378
|
this._cb?.log('ℹ️ Auto-compact not supported for copilot-sdk — skipping');
|
|
2333
2379
|
}
|
|
2334
|
-
else if (acProvider === 'opencode-cli') {
|
|
2380
|
+
else if (acProvider === 'opencode-cli' && !opencodeCliIsPersistent(acProvider)) {
|
|
2335
2381
|
let sid = (0, sessionState_1.getSessionId)(this._workspaceRoot, 'opencode-cli');
|
|
2336
2382
|
if (!sid) {
|
|
2337
2383
|
sid = await (0, opencodeCliProvider_1.getLatestOpenCodeSessionId)(this._workspaceRoot, msg => this._cb?.log(msg));
|
|
@@ -2344,7 +2390,7 @@ class TaskLoopRunner {
|
|
|
2344
2390
|
this._cb?.log('⚠️ Auto-compact: no OpenCode session ID found — skipping');
|
|
2345
2391
|
}
|
|
2346
2392
|
}
|
|
2347
|
-
else if (acProvider
|
|
2393
|
+
else if (usesOpencodeSdkServer(acProvider)) {
|
|
2348
2394
|
await (0, opencodeSdkProvider_1.runOpencodeSdkCompact)(this._workspaceRoot, msg => this._cb?.log(msg));
|
|
2349
2395
|
this._cb?.log('🗜 Auto-compact complete (opencode-sdk)');
|
|
2350
2396
|
}
|
|
@@ -2480,13 +2526,15 @@ class TaskLoopRunner {
|
|
|
2480
2526
|
if (evictRoot) {
|
|
2481
2527
|
try {
|
|
2482
2528
|
const evictLog = (m) => this._cb?.log(m);
|
|
2483
|
-
|
|
2529
|
+
// Also evict the persistent CLI variants (Phase 2) — they hold the
|
|
2530
|
+
// same live sibling process, which is likewise clutching the dead token.
|
|
2531
|
+
if (usesClaudeTuiClient(currentProvider)) {
|
|
2484
2532
|
(0, claudeTuiProvider_1.closeClaudeTuiClient)(evictRoot, evictLog);
|
|
2485
2533
|
}
|
|
2486
|
-
else if (currentProvider
|
|
2534
|
+
else if (usesOpencodeSdkServer(currentProvider)) {
|
|
2487
2535
|
(0, opencodeSdkProvider_1.closeOpencodeSdkClient)(evictRoot, evictLog);
|
|
2488
2536
|
}
|
|
2489
|
-
else if (currentProvider
|
|
2537
|
+
else if (usesCopilotSdkSession(currentProvider)) {
|
|
2490
2538
|
(0, copilotSdkProvider_1.closeCopilotSdkSession)(evictRoot, evictLog);
|
|
2491
2539
|
}
|
|
2492
2540
|
else if (currentProvider === 'grok-tui') {
|
|
@@ -2614,7 +2662,7 @@ class TaskLoopRunner {
|
|
|
2614
2662
|
const provider = this._cb?.getActiveProvider() ?? '';
|
|
2615
2663
|
this._cb?.log(`🗑 Autocompact thrashing (${provider}) — running /clear to reset session…\n${rawMsg}`);
|
|
2616
2664
|
this._notifyDiscord(`🗑 **Autocompact thrashing** (${provider}) — running \`/clear\` to reset session…\n\`\`\`\n${rawMsg}\n\`\`\``);
|
|
2617
|
-
if (provider === 'claude-cli') {
|
|
2665
|
+
if (provider === 'claude-cli' && !claudeCliIsPersistent(provider)) {
|
|
2618
2666
|
let sessionId = (0, sessionState_1.getSessionId)(this._workspaceRoot, 'claude-cli');
|
|
2619
2667
|
if (!sessionId) {
|
|
2620
2668
|
sessionId = (0, dispatcher_1.findLatestClaudeSession)(this._workspaceRoot);
|
|
@@ -2634,15 +2682,16 @@ class TaskLoopRunner {
|
|
|
2634
2682
|
this._cb?.log('⚠️ No Claude session ID found for /clear — retrying anyway');
|
|
2635
2683
|
}
|
|
2636
2684
|
}
|
|
2637
|
-
else if (provider
|
|
2638
|
-
|
|
2685
|
+
else if (usesClaudeTuiClient(provider)) {
|
|
2686
|
+
const claudeProvider = provider;
|
|
2687
|
+
let sessionId = (0, sessionState_1.getSessionId)(this._workspaceRoot, claudeProvider);
|
|
2639
2688
|
if (!sessionId) {
|
|
2640
2689
|
sessionId = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(this._workspaceRoot);
|
|
2641
2690
|
}
|
|
2642
2691
|
if (sessionId) {
|
|
2643
2692
|
try {
|
|
2644
2693
|
await (0, claudeTuiProvider_1.runClaudeTuiClear)(this._workspaceRoot, sessionId, msg => this._cb?.log(msg));
|
|
2645
|
-
(0, sessionState_1.clearSessionId)(this._workspaceRoot,
|
|
2694
|
+
(0, sessionState_1.clearSessionId)(this._workspaceRoot, claudeProvider);
|
|
2646
2695
|
this._cb?.log('🗑 /clear (TUI) complete — session reset, retrying task');
|
|
2647
2696
|
this._notifyDiscord('🗑 `/clear` (TUI) complete — session reset, retrying task');
|
|
2648
2697
|
}
|
|
@@ -2665,7 +2714,7 @@ class TaskLoopRunner {
|
|
|
2665
2714
|
const provider = this._cb?.getActiveProvider() ?? '';
|
|
2666
2715
|
this._cb?.log(`🗜 Context length exceeded (${provider}) — running /compact: ${rawMsg}`);
|
|
2667
2716
|
this._notifyDiscord(`🗜 **Context length exceeded** (${provider}) — running \`/compact\`…\n\`\`\`\n${rawMsg}\n\`\`\``);
|
|
2668
|
-
if (provider === 'claude-cli') {
|
|
2717
|
+
if (provider === 'claude-cli' && !claudeCliIsPersistent(provider)) {
|
|
2669
2718
|
// Resolve a Claude session ID — prefer the saved one, else scan
|
|
2670
2719
|
// the .claude/projects jsonl folder for the most recent.
|
|
2671
2720
|
let sessionId = (0, sessionState_1.getSessionId)(this._workspaceRoot, 'claude-cli');
|
|
@@ -2687,8 +2736,9 @@ class TaskLoopRunner {
|
|
|
2687
2736
|
this._cb?.log('⚠️ No Claude session ID found for compact — retrying task without compact');
|
|
2688
2737
|
}
|
|
2689
2738
|
}
|
|
2690
|
-
else if (provider
|
|
2691
|
-
|
|
2739
|
+
else if (usesClaudeTuiClient(provider)) {
|
|
2740
|
+
const claudeProvider = provider;
|
|
2741
|
+
let sessionId = (0, sessionState_1.getSessionId)(this._workspaceRoot, claudeProvider);
|
|
2692
2742
|
if (!sessionId) {
|
|
2693
2743
|
sessionId = (0, claudeTuiProvider_1.getClaudeTuiLatestSessionId)(this._workspaceRoot);
|
|
2694
2744
|
}
|
|
@@ -2862,6 +2912,11 @@ class TaskLoopRunner {
|
|
|
2862
2912
|
const iscopilotSdkProvider = this._cb?.getActiveProvider() === 'copilot-sdk';
|
|
2863
2913
|
const isOpenCodeCli = this._cb?.getActiveProvider() === 'opencode-cli';
|
|
2864
2914
|
const isOpencodeSdk = this._cb?.getActiveProvider() === 'opencode-sdk';
|
|
2915
|
+
// opencode-cli running PERSISTENT (Phase 2) shares the opencode-sdk code path:
|
|
2916
|
+
// its in-process server writes '0' to the exit file on session.idle, so it must
|
|
2917
|
+
// be handled like opencode-sdk (resolve on !busy) — NOT via the CLI exit-file
|
|
2918
|
+
// watcher, which would re-prompt in a loop. Command-mode opencode-cli is unaffected.
|
|
2919
|
+
const isOpencodeSdkLike = isOpencodeSdk || (isOpenCodeCli && (0, opencodeSdkProvider_1.opencodeCliPersistentEnabled)());
|
|
2865
2920
|
// grok-cli / grok-tui / copilot-cli all tee stdout too — without these flags
|
|
2866
2921
|
// the rate-limit / context-length / thrash detectors never ran for them, so
|
|
2867
2922
|
// a throttled or context-blown turn was silently walked to give_up and the
|
|
@@ -2878,7 +2933,12 @@ class TaskLoopRunner {
|
|
|
2878
2933
|
// already tails. Feed that stdout (ANSI-stripped) through a narrator so each
|
|
2879
2934
|
// chunk becomes a `Notification` hook event, giving line/chunk-granularity
|
|
2880
2935
|
// live output. (Claude/grok/SDK providers already stream and are untouched.)
|
|
2881
|
-
|
|
2936
|
+
// When copilot-cli / opencode-cli run PERSISTENT (Phase 2) their SDK sibling
|
|
2937
|
+
// already streams assistant text as live Notification events, so the teed-stdout
|
|
2938
|
+
// narrator here would DOUBLE every chunk — disable it in that mode.
|
|
2939
|
+
const cliNarrateCopilot = isCopilotCli && !(0, copilotSdkProvider_1.copilotCliPersistentEnabled)();
|
|
2940
|
+
const cliNarrateOpencode = isOpenCodeCli && !(0, opencodeSdkProvider_1.opencodeCliPersistentEnabled)();
|
|
2941
|
+
const cliNarrator = (this._workspaceRoot && (cliNarrateCopilot || cliNarrateOpencode))
|
|
2882
2942
|
? new liveNarration_1.LiveNarrationStreamer(this._cb.getActiveProvider() ?? 'cli', this._workspaceRoot, ev => (0, liveNarration_1.appendHookEventLine)(this._workspaceRoot, ev))
|
|
2883
2943
|
: null;
|
|
2884
2944
|
// Independent cursor so narration doesn't disturb the claude-cli Discord
|
|
@@ -3385,7 +3445,7 @@ class TaskLoopRunner {
|
|
|
3385
3445
|
// exit file when session.idle fires, the poller sees it, calls onCliExit(),
|
|
3386
3446
|
// which re-sends the prompt, which loops). Instead, the poller resolves this
|
|
3387
3447
|
// promise directly when isOpencodeSdkBusy() becomes false (see below).
|
|
3388
|
-
if (!
|
|
3448
|
+
if (!isOpencodeSdkLike && this._workspaceRoot) {
|
|
3389
3449
|
attachExitWatcher((0, sessionState_1.exitFilePath)(this._workspaceRoot, activeProvider));
|
|
3390
3450
|
}
|
|
3391
3451
|
// Inactivity-based check-in: track Claude JSONL byte size every 3 s.
|
|
@@ -3414,14 +3474,14 @@ class TaskLoopRunner {
|
|
|
3414
3474
|
attachStdoutWatcher(resolvedStdoutFile);
|
|
3415
3475
|
}
|
|
3416
3476
|
const latestExit = (0, sessionState_1.exitFilePath)(this._workspaceRoot, activeProvider);
|
|
3417
|
-
if (!
|
|
3477
|
+
if (!isOpencodeSdkLike && latestExit !== watchedExitFile) {
|
|
3418
3478
|
attachExitWatcher(latestExit);
|
|
3419
3479
|
}
|
|
3420
3480
|
// opencode-sdk: resolve _waitForTaskCompletion as soon as the SDK
|
|
3421
3481
|
// session goes idle (isOpencodeSdkBusy false). This avoids the
|
|
3422
3482
|
// CLI-style onCliExit() reminder re-prompt loop that the exit-file
|
|
3423
3483
|
// mechanism would otherwise trigger.
|
|
3424
|
-
if (
|
|
3484
|
+
if (isOpencodeSdkLike && this._workspaceRoot && !(0, opencodeSdkProvider_1.isOpencodeSdkBusy)(this._workspaceRoot)) {
|
|
3425
3485
|
check(); // one last todo-file check before resolving
|
|
3426
3486
|
if (!cancelled) {
|
|
3427
3487
|
cleanup();
|
|
@@ -3433,7 +3493,7 @@ class TaskLoopRunner {
|
|
|
3433
3493
|
// events (gitignored dirs, inotify limits, fast exits before re-attach).
|
|
3434
3494
|
// Read the exit file directly every tick and trigger onCliExit() if it
|
|
3435
3495
|
// became non-empty without the watcher firing.
|
|
3436
|
-
if (!
|
|
3496
|
+
if (!isOpencodeSdkLike && latestExit && latestExit !== handledExitFile) {
|
|
3437
3497
|
try {
|
|
3438
3498
|
if (fs.readFileSync(latestExit, 'utf8').trim() !== '') {
|
|
3439
3499
|
handledExitFile = latestExit;
|