bosun 0.36.2 → 0.36.4
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/agent-prompts.mjs +95 -0
- package/analyze-agent-work-helpers.mjs +308 -0
- package/analyze-agent-work.mjs +926 -0
- package/autofix.mjs +2 -0
- package/bosun.schema.json +101 -3
- package/codex-shell.mjs +85 -10
- package/desktop/main.mjs +871 -48
- package/desktop/preload.mjs +54 -1
- package/desktop-shortcut.mjs +90 -11
- package/git-editor-fix.mjs +273 -0
- package/mcp-registry.mjs +579 -0
- package/meeting-workflow-service.mjs +631 -0
- package/monitor.mjs +18 -103
- package/package.json +21 -2
- package/primary-agent.mjs +32 -12
- package/session-tracker.mjs +68 -0
- package/setup-web-server.mjs +20 -10
- package/setup.mjs +376 -83
- package/startup-service.mjs +51 -6
- package/stream-resilience.mjs +17 -7
- package/ui/app.js +164 -4
- package/ui/components/agent-selector.js +145 -1
- package/ui/components/chat-view.js +161 -15
- package/ui/components/session-list.js +2 -2
- package/ui/components/shared.js +188 -15
- package/ui/modules/icons.js +13 -0
- package/ui/modules/utils.js +44 -0
- package/ui/modules/voice-client-sdk.js +733 -0
- package/ui/modules/voice-overlay.js +128 -15
- package/ui/modules/voice.js +15 -6
- package/ui/setup.html +281 -81
- package/ui/styles/components.css +99 -3
- package/ui/styles/sessions.css +122 -14
- package/ui/styles.css +14 -0
- package/ui/tabs/agents.js +1 -1
- package/ui/tabs/chat.js +123 -14
- package/ui/tabs/control.js +16 -22
- package/ui/tabs/dashboard.js +85 -8
- package/ui/tabs/library.js +113 -17
- package/ui/tabs/settings.js +116 -2
- package/ui/tabs/tasks.js +388 -39
- package/ui/tabs/telemetry.js +0 -1
- package/ui/tabs/workflows.js +4 -0
- package/ui-server.mjs +400 -22
- package/update-check.mjs +41 -13
- package/voice-action-dispatcher.mjs +844 -0
- package/voice-agents-sdk.mjs +664 -0
- package/voice-auth-manager.mjs +164 -0
- package/voice-relay.mjs +1194 -0
- package/voice-tools.mjs +914 -0
- package/workflow-templates/agents.mjs +6 -2
- package/workflow-templates/github.mjs +154 -12
- package/workflow-templates.mjs +3 -0
- package/github-reconciler.mjs +0 -506
- package/merge-strategy.mjs +0 -1210
- package/pr-cleanup-daemon.mjs +0 -992
- package/workspace-reaper.mjs +0 -405
package/autofix.mjs
CHANGED
|
@@ -688,6 +688,7 @@ function detectChangedFiles(repoRoot) {
|
|
|
688
688
|
cwd: repoRoot,
|
|
689
689
|
encoding: "utf8",
|
|
690
690
|
timeout: 10_000,
|
|
691
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
691
692
|
});
|
|
692
693
|
return output
|
|
693
694
|
.split(/\r?\n/)
|
|
@@ -708,6 +709,7 @@ function getChangeSummary(repoRoot, files) {
|
|
|
708
709
|
cwd: repoRoot,
|
|
709
710
|
encoding: "utf8",
|
|
710
711
|
timeout: 10_000,
|
|
712
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
711
713
|
});
|
|
712
714
|
return diff.trim() || files.join(", ");
|
|
713
715
|
} catch {
|
package/bosun.schema.json
CHANGED
|
@@ -38,7 +38,13 @@
|
|
|
38
38
|
"codexEnabled": { "type": "boolean" },
|
|
39
39
|
"primaryAgent": {
|
|
40
40
|
"type": "string",
|
|
41
|
-
"enum": [
|
|
41
|
+
"enum": [
|
|
42
|
+
"codex-sdk",
|
|
43
|
+
"copilot-sdk",
|
|
44
|
+
"claude-sdk",
|
|
45
|
+
"gemini-sdk",
|
|
46
|
+
"opencode-sdk"
|
|
47
|
+
]
|
|
42
48
|
},
|
|
43
49
|
"telegramUiTunnel": {
|
|
44
50
|
"type": "string",
|
|
@@ -147,6 +153,48 @@
|
|
|
147
153
|
"default": "auto",
|
|
148
154
|
"description": "Voice provider: openai/azure (Tier 1 realtime), claude/gemini (Tier 2 voice + provider vision), fallback (browser STT/TTS), auto (detect from env)"
|
|
149
155
|
},
|
|
156
|
+
"providers": {
|
|
157
|
+
"type": "array",
|
|
158
|
+
"description": "Ordered provider candidates for voice routing/failover. First match with credentials is used.",
|
|
159
|
+
"items": {
|
|
160
|
+
"anyOf": [
|
|
161
|
+
{
|
|
162
|
+
"type": "string",
|
|
163
|
+
"enum": ["openai", "azure", "claude", "gemini", "fallback"]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "object",
|
|
167
|
+
"additionalProperties": false,
|
|
168
|
+
"properties": {
|
|
169
|
+
"provider": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"enum": ["openai", "azure", "claude", "gemini", "fallback"]
|
|
172
|
+
},
|
|
173
|
+
"model": { "type": "string" },
|
|
174
|
+
"visionModel": { "type": "string" },
|
|
175
|
+
"voiceId": {
|
|
176
|
+
"type": "string",
|
|
177
|
+
"enum": [
|
|
178
|
+
"alloy",
|
|
179
|
+
"ash",
|
|
180
|
+
"ballad",
|
|
181
|
+
"coral",
|
|
182
|
+
"echo",
|
|
183
|
+
"fable",
|
|
184
|
+
"onyx",
|
|
185
|
+
"nova",
|
|
186
|
+
"sage",
|
|
187
|
+
"shimmer",
|
|
188
|
+
"verse"
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
"azureDeployment": { "type": "string" }
|
|
192
|
+
},
|
|
193
|
+
"required": ["provider"]
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
},
|
|
150
198
|
"model": {
|
|
151
199
|
"type": "string",
|
|
152
200
|
"default": "gpt-4o-realtime-preview-2024-12-17",
|
|
@@ -161,10 +209,18 @@
|
|
|
161
209
|
"type": "string",
|
|
162
210
|
"description": "OpenAI API key for Realtime API (overrides OPENAI_API_KEY env)"
|
|
163
211
|
},
|
|
212
|
+
"openaiAccessToken": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "OpenAI OAuth access token for voice (OAuth preferred over API key when present)"
|
|
215
|
+
},
|
|
164
216
|
"azureApiKey": {
|
|
165
217
|
"type": "string",
|
|
166
218
|
"description": "Azure OpenAI API key for Realtime API"
|
|
167
219
|
},
|
|
220
|
+
"azureAccessToken": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "Azure OAuth/AAD access token for voice realtime"
|
|
223
|
+
},
|
|
168
224
|
"azureEndpoint": {
|
|
169
225
|
"type": "string",
|
|
170
226
|
"description": "Azure OpenAI endpoint URL"
|
|
@@ -178,13 +234,33 @@
|
|
|
178
234
|
"type": "string",
|
|
179
235
|
"description": "Anthropic API key for Claude voice/vision provider mode"
|
|
180
236
|
},
|
|
237
|
+
"claudeAccessToken": {
|
|
238
|
+
"type": "string",
|
|
239
|
+
"description": "Claude OAuth access token for voice provider mode"
|
|
240
|
+
},
|
|
181
241
|
"geminiApiKey": {
|
|
182
242
|
"type": "string",
|
|
183
243
|
"description": "Gemini API key for Gemini voice/vision provider mode"
|
|
184
244
|
},
|
|
245
|
+
"geminiAccessToken": {
|
|
246
|
+
"type": "string",
|
|
247
|
+
"description": "Gemini OAuth access token for voice provider mode"
|
|
248
|
+
},
|
|
185
249
|
"voiceId": {
|
|
186
250
|
"type": "string",
|
|
187
|
-
"enum": [
|
|
251
|
+
"enum": [
|
|
252
|
+
"alloy",
|
|
253
|
+
"ash",
|
|
254
|
+
"ballad",
|
|
255
|
+
"coral",
|
|
256
|
+
"echo",
|
|
257
|
+
"fable",
|
|
258
|
+
"onyx",
|
|
259
|
+
"nova",
|
|
260
|
+
"sage",
|
|
261
|
+
"shimmer",
|
|
262
|
+
"verse"
|
|
263
|
+
],
|
|
188
264
|
"default": "alloy",
|
|
189
265
|
"description": "Voice ID for TTS output"
|
|
190
266
|
},
|
|
@@ -204,9 +280,31 @@
|
|
|
204
280
|
"default": "browser",
|
|
205
281
|
"description": "Fallback when Realtime API unavailable: browser (Web Speech API) or disabled"
|
|
206
282
|
},
|
|
283
|
+
"failover": {
|
|
284
|
+
"type": "object",
|
|
285
|
+
"additionalProperties": false,
|
|
286
|
+
"properties": {
|
|
287
|
+
"enabled": {
|
|
288
|
+
"type": "boolean",
|
|
289
|
+
"default": true,
|
|
290
|
+
"description": "Enable automatic realtime failover across configured voice providers"
|
|
291
|
+
},
|
|
292
|
+
"maxAttempts": {
|
|
293
|
+
"type": "number",
|
|
294
|
+
"default": 2,
|
|
295
|
+
"description": "Maximum realtime provider attempts per voice session token request"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
207
299
|
"delegateExecutor": {
|
|
208
300
|
"type": "string",
|
|
209
|
-
"enum": [
|
|
301
|
+
"enum": [
|
|
302
|
+
"codex-sdk",
|
|
303
|
+
"copilot-sdk",
|
|
304
|
+
"claude-sdk",
|
|
305
|
+
"gemini-sdk",
|
|
306
|
+
"opencode-sdk"
|
|
307
|
+
],
|
|
210
308
|
"description": "Which executor to use for delegate_to_agent calls. Defaults to primaryAgent."
|
|
211
309
|
}
|
|
212
310
|
}
|
package/codex-shell.mjs
CHANGED
|
@@ -177,6 +177,7 @@ function sanitizeAndTruncatePrompt(text) {
|
|
|
177
177
|
return truncated + `\n\n[...prompt truncated — ${removedBytes} bytes removed to stay within API limits]`;
|
|
178
178
|
}
|
|
179
179
|
const REPO_ROOT = resolveRepoRoot();
|
|
180
|
+
const DEFAULT_WORKING_DIRECTORY = REPO_ROOT;
|
|
180
181
|
|
|
181
182
|
// ── State ────────────────────────────────────────────────────────────────────
|
|
182
183
|
|
|
@@ -187,6 +188,7 @@ let activeThreadId = null; // Thread ID for resume
|
|
|
187
188
|
let activeTurn = null; // Whether a turn is in-flight
|
|
188
189
|
let turnCount = 0; // Number of turns in this thread
|
|
189
190
|
let currentSessionId = null; // Active session identifier
|
|
191
|
+
let activeWorkingDirectory = DEFAULT_WORKING_DIRECTORY; // Session/thread cwd
|
|
190
192
|
let threadNeedsPriming = false; // True when a fresh thread needs the system prompt on next turn
|
|
191
193
|
let codexRuntimeCaps = {
|
|
192
194
|
hasSteeringApi: false,
|
|
@@ -200,6 +202,20 @@ function timestamp() {
|
|
|
200
202
|
return new Date().toISOString();
|
|
201
203
|
}
|
|
202
204
|
|
|
205
|
+
function normalizeWorkingDirectory(input) {
|
|
206
|
+
const raw = String(input || "").trim();
|
|
207
|
+
if (!raw) return null;
|
|
208
|
+
try {
|
|
209
|
+
return resolve(raw);
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function getWorkingDirectory() {
|
|
216
|
+
return normalizeWorkingDirectory(activeWorkingDirectory) || DEFAULT_WORKING_DIRECTORY;
|
|
217
|
+
}
|
|
218
|
+
|
|
203
219
|
function resolveCodexTransport() {
|
|
204
220
|
const raw = String(process.env.CODEX_TRANSPORT || "auto")
|
|
205
221
|
.trim()
|
|
@@ -250,13 +266,17 @@ async function loadState() {
|
|
|
250
266
|
activeThreadId = data.threadId || null;
|
|
251
267
|
turnCount = data.turnCount || 0;
|
|
252
268
|
currentSessionId = data.currentSessionId || null;
|
|
269
|
+
activeWorkingDirectory =
|
|
270
|
+
normalizeWorkingDirectory(data.workingDirectory) ||
|
|
271
|
+
DEFAULT_WORKING_DIRECTORY;
|
|
253
272
|
console.log(
|
|
254
|
-
`[codex-shell] loaded state: threadId=${activeThreadId}, turns=${turnCount}, session=${currentSessionId}`,
|
|
273
|
+
`[codex-shell] loaded state: threadId=${activeThreadId}, turns=${turnCount}, session=${currentSessionId}, cwd=${getWorkingDirectory()}`,
|
|
255
274
|
);
|
|
256
275
|
} catch {
|
|
257
276
|
activeThreadId = null;
|
|
258
277
|
turnCount = 0;
|
|
259
278
|
currentSessionId = null;
|
|
279
|
+
activeWorkingDirectory = DEFAULT_WORKING_DIRECTORY;
|
|
260
280
|
}
|
|
261
281
|
}
|
|
262
282
|
|
|
@@ -270,6 +290,7 @@ async function saveState() {
|
|
|
270
290
|
threadId: activeThreadId,
|
|
271
291
|
turnCount,
|
|
272
292
|
currentSessionId,
|
|
293
|
+
workingDirectory: getWorkingDirectory(),
|
|
273
294
|
updatedAt: timestamp(),
|
|
274
295
|
},
|
|
275
296
|
null,
|
|
@@ -311,6 +332,7 @@ async function saveCurrentSession() {
|
|
|
311
332
|
await saveSessionData(currentSessionId, {
|
|
312
333
|
threadId: activeThreadId,
|
|
313
334
|
turnCount,
|
|
335
|
+
workingDirectory: getWorkingDirectory(),
|
|
314
336
|
createdAt: (await loadSessionData(currentSessionId))?.createdAt || timestamp(),
|
|
315
337
|
lastActiveAt: timestamp(),
|
|
316
338
|
});
|
|
@@ -325,12 +347,16 @@ async function loadSession(sessionId) {
|
|
|
325
347
|
turnCount = data.turnCount || 0;
|
|
326
348
|
activeThread = null; // will be re-created/resumed via getThread()
|
|
327
349
|
currentSessionId = sessionId;
|
|
328
|
-
|
|
350
|
+
activeWorkingDirectory =
|
|
351
|
+
normalizeWorkingDirectory(data.workingDirectory) ||
|
|
352
|
+
DEFAULT_WORKING_DIRECTORY;
|
|
353
|
+
console.log(`[codex-shell] loaded session ${sessionId}: threadId=${activeThreadId}, turns=${turnCount}, cwd=${getWorkingDirectory()}`);
|
|
329
354
|
} else {
|
|
330
355
|
activeThread = null;
|
|
331
356
|
activeThreadId = null;
|
|
332
357
|
turnCount = 0;
|
|
333
358
|
currentSessionId = sessionId;
|
|
359
|
+
activeWorkingDirectory = DEFAULT_WORKING_DIRECTORY;
|
|
334
360
|
console.log(`[codex-shell] created new session ${sessionId}`);
|
|
335
361
|
}
|
|
336
362
|
await saveState();
|
|
@@ -366,9 +392,8 @@ Key files:
|
|
|
366
392
|
AGENTS.md — Repo guide for agents
|
|
367
393
|
`;
|
|
368
394
|
|
|
369
|
-
const
|
|
395
|
+
const THREAD_BASE_OPTIONS = {
|
|
370
396
|
sandboxMode: process.env.CODEX_SANDBOX || "workspace-write",
|
|
371
|
-
workingDirectory: REPO_ROOT,
|
|
372
397
|
skipGitRepoCheck: true,
|
|
373
398
|
webSearchMode: "live",
|
|
374
399
|
approvalPolicy: "never",
|
|
@@ -377,6 +402,13 @@ const THREAD_OPTIONS = {
|
|
|
377
402
|
// codex-config.mjs ensureFeatureFlags() handles this during setup.
|
|
378
403
|
};
|
|
379
404
|
|
|
405
|
+
function buildThreadOptions() {
|
|
406
|
+
return {
|
|
407
|
+
...THREAD_BASE_OPTIONS,
|
|
408
|
+
workingDirectory: getWorkingDirectory(),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
380
412
|
/**
|
|
381
413
|
* Get or create a thread.
|
|
382
414
|
* Uses fresh-thread mode by default to avoid context bloat.
|
|
@@ -384,6 +416,7 @@ const THREAD_OPTIONS = {
|
|
|
384
416
|
*/
|
|
385
417
|
async function getThread() {
|
|
386
418
|
if (activeThread) return activeThread;
|
|
419
|
+
const threadOptions = buildThreadOptions();
|
|
387
420
|
|
|
388
421
|
const { env: resolvedEnv } = resolveCodexProfileRuntime(process.env);
|
|
389
422
|
Object.assign(process.env, resolvedEnv);
|
|
@@ -391,8 +424,21 @@ async function getThread() {
|
|
|
391
424
|
if (!codexInstance) {
|
|
392
425
|
const Cls = await loadCodexSdk();
|
|
393
426
|
if (!Cls) throw new Error("Codex SDK not available");
|
|
394
|
-
|
|
395
|
-
//
|
|
427
|
+
|
|
428
|
+
// Inject stream resilience settings via --config overrides so they apply
|
|
429
|
+
// even if config.toml hasn't been patched by codex-config.mjs yet.
|
|
430
|
+
// This is the most reliable path for Azure/Foundry deployments where
|
|
431
|
+
// dropped SSE streams ("response.failed") are the dominant failure mode.
|
|
432
|
+
const providerName = resolvedEnv.OPENAI_BASE_URL?.toLowerCase().includes(".openai.azure.com")
|
|
433
|
+
? "azure"
|
|
434
|
+
: "openai";
|
|
435
|
+
const STREAM_IDLE_TIMEOUT_MS = 3_600_000; // 60 min — matches Azure max stream lifetime
|
|
436
|
+
const streamProviderOverrides = {
|
|
437
|
+
stream_idle_timeout_ms: STREAM_IDLE_TIMEOUT_MS,
|
|
438
|
+
stream_max_retries: 15,
|
|
439
|
+
request_max_retries: 6,
|
|
440
|
+
};
|
|
441
|
+
|
|
396
442
|
codexInstance = new Cls({
|
|
397
443
|
config: {
|
|
398
444
|
features: {
|
|
@@ -402,8 +448,13 @@ async function getThread() {
|
|
|
402
448
|
undo: true,
|
|
403
449
|
steer: true,
|
|
404
450
|
},
|
|
451
|
+
model_providers: {
|
|
452
|
+
[providerName]: streamProviderOverrides,
|
|
453
|
+
},
|
|
405
454
|
},
|
|
406
455
|
});
|
|
456
|
+
|
|
457
|
+
console.log(`[codex-shell] created Codex instance (provider=${providerName}, stream_idle_timeout=${STREAM_IDLE_TIMEOUT_MS}ms, stream_max_retries=${streamProviderOverrides.stream_max_retries})`);
|
|
407
458
|
}
|
|
408
459
|
|
|
409
460
|
const transport = resolveCodexTransport();
|
|
@@ -414,7 +465,7 @@ async function getThread() {
|
|
|
414
465
|
try {
|
|
415
466
|
activeThread = codexInstance.resumeThread(
|
|
416
467
|
activeThreadId,
|
|
417
|
-
|
|
468
|
+
threadOptions,
|
|
418
469
|
);
|
|
419
470
|
if (activeThread) {
|
|
420
471
|
detectThreadCapabilities(activeThread);
|
|
@@ -446,16 +497,16 @@ async function getThread() {
|
|
|
446
497
|
// the priming turn is STREAMED (runStreamed) instead of blocking (run).
|
|
447
498
|
// This eliminates the 2-5 minute silent delay the chat UI suffered because
|
|
448
499
|
// the old `thread.run(SYSTEM_PROMPT)` call produced zero streaming events.
|
|
449
|
-
activeThread = codexInstance.startThread(
|
|
500
|
+
activeThread = codexInstance.startThread(threadOptions);
|
|
450
501
|
detectThreadCapabilities(activeThread);
|
|
451
502
|
threadNeedsPriming = true;
|
|
452
503
|
|
|
453
504
|
if (activeThread.id) {
|
|
454
505
|
activeThreadId = activeThread.id;
|
|
455
506
|
await saveState();
|
|
456
|
-
console.log(`[codex-shell] new thread started: ${activeThreadId} (priming deferred to first user turn)`);
|
|
507
|
+
console.log(`[codex-shell] new thread started: ${activeThreadId} (priming deferred to first user turn, cwd=${threadOptions.workingDirectory})`);
|
|
457
508
|
} else {
|
|
458
|
-
console.log(
|
|
509
|
+
console.log(`[codex-shell] new thread started (priming deferred to first user turn, cwd=${threadOptions.workingDirectory})`);
|
|
459
510
|
}
|
|
460
511
|
|
|
461
512
|
return activeThread;
|
|
@@ -627,6 +678,7 @@ export async function execCodexPrompt(userMessage, options = {}) {
|
|
|
627
678
|
persistent = false,
|
|
628
679
|
sessionId = null,
|
|
629
680
|
mode = null,
|
|
681
|
+
cwd = null,
|
|
630
682
|
} = options;
|
|
631
683
|
|
|
632
684
|
agentSdk = resolveAgentSdkConfig({ reload: true });
|
|
@@ -651,9 +703,13 @@ export async function execCodexPrompt(userMessage, options = {}) {
|
|
|
651
703
|
|
|
652
704
|
try {
|
|
653
705
|
const streamSafety = resolveCodexStreamSafety(timeoutMs);
|
|
706
|
+
const requestedWorkingDirectory = normalizeWorkingDirectory(cwd);
|
|
707
|
+
|
|
654
708
|
if (!persistent) {
|
|
655
709
|
// Task executor path — keep existing fresh-thread behavior
|
|
656
710
|
activeThread = null;
|
|
711
|
+
activeWorkingDirectory =
|
|
712
|
+
requestedWorkingDirectory || DEFAULT_WORKING_DIRECTORY;
|
|
657
713
|
} else if (sessionId && sessionId !== currentSessionId) {
|
|
658
714
|
// Switching to a different persistent session
|
|
659
715
|
await loadSession(sessionId);
|
|
@@ -669,6 +725,24 @@ export async function execCodexPrompt(userMessage, options = {}) {
|
|
|
669
725
|
}
|
|
670
726
|
// else: persistent && same session && under limit → reuse activeThread
|
|
671
727
|
|
|
728
|
+
if (
|
|
729
|
+
requestedWorkingDirectory &&
|
|
730
|
+
requestedWorkingDirectory !== getWorkingDirectory()
|
|
731
|
+
) {
|
|
732
|
+
activeWorkingDirectory = requestedWorkingDirectory;
|
|
733
|
+
activeThread = null;
|
|
734
|
+
activeThreadId = null;
|
|
735
|
+
turnCount = 0;
|
|
736
|
+
threadNeedsPriming = false;
|
|
737
|
+
await saveState();
|
|
738
|
+
if (persistent && currentSessionId) {
|
|
739
|
+
await saveCurrentSession();
|
|
740
|
+
}
|
|
741
|
+
console.log(
|
|
742
|
+
`[codex-shell] switched working directory to ${requestedWorkingDirectory} for session ${currentSessionId || "(ephemeral)"}`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
672
746
|
// ── Mode detection ───────────────────────────────────────────────────
|
|
673
747
|
// "ask" mode should be lightweight — no heavy executor framing that
|
|
674
748
|
// instructs the agent to run commands and read files. The mode is
|
|
@@ -960,6 +1034,7 @@ export async function resetThread() {
|
|
|
960
1034
|
turnCount = 0;
|
|
961
1035
|
activeTurn = null;
|
|
962
1036
|
currentSessionId = null;
|
|
1037
|
+
activeWorkingDirectory = DEFAULT_WORKING_DIRECTORY;
|
|
963
1038
|
threadNeedsPriming = false;
|
|
964
1039
|
await saveState();
|
|
965
1040
|
console.log("[codex-shell] thread reset");
|