cli-jaw 2.17.30 → 2.17.35
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/README.md +2 -0
- package/dist/src/agent/codex-app-client.js +69 -1
- package/dist/src/agent/codex-app-client.js.map +1 -1
- package/dist/src/agent/cursor-runtime.js +9 -0
- package/dist/src/agent/cursor-runtime.js.map +1 -1
- package/dist/src/agent/pi-runtime.js +1 -1
- package/dist/src/agent/pi-runtime.js.map +1 -1
- package/dist/src/agent/prompt-context.js +39 -0
- package/dist/src/agent/prompt-context.js.map +1 -1
- package/dist/src/agent/spawn.js +138 -20
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/browser/web-ai/capability-observation-presets.js +4 -0
- package/dist/src/browser/web-ai/capability-observation-presets.js.map +1 -1
- package/dist/src/browser/web-ai/chatgpt-model.js +9 -5
- package/dist/src/browser/web-ai/chatgpt-model.js.map +1 -1
- package/dist/src/browser/web-ai/grok-model.js +18 -5
- package/dist/src/browser/web-ai/grok-model.js.map +1 -1
- package/dist/src/cli/claude-models.js +6 -0
- package/dist/src/cli/claude-models.js.map +1 -1
- package/dist/src/cli/handlers-runtime.js +29 -4
- package/dist/src/cli/handlers-runtime.js.map +1 -1
- package/dist/src/cli/registry.js +56 -15
- package/dist/src/cli/registry.js.map +1 -1
- package/dist/src/code-mode/model-options.js +4 -4
- package/dist/src/code-mode/model-options.js.map +1 -1
- package/dist/src/core/config.js +1 -1
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/db.js +19 -0
- package/dist/src/core/db.js.map +1 -1
- package/dist/src/core/runtime-settings.js +11 -2
- package/dist/src/core/runtime-settings.js.map +1 -1
- package/dist/src/orchestrator/attestation.js +66 -0
- package/dist/src/orchestrator/attestation.js.map +1 -1
- package/dist/src/orchestrator/gateway.js +11 -3
- package/dist/src/orchestrator/gateway.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +5 -0
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/routes/command.js +4 -1
- package/dist/src/routes/command.js.map +1 -1
- package/dist/src/routes/orchestrate.js +14 -2
- package/dist/src/routes/orchestrate.js.map +1 -1
- package/dist/src/slack/commands.js +1 -0
- package/dist/src/slack/commands.js.map +1 -1
- package/dist/src/telegram/bot.js +3 -2
- package/dist/src/telegram/bot.js.map +1 -1
- package/package.json +5 -1
- package/public/dist/assets/{Agent-BjxJRsKG.js → Agent-kyyjcnqC.js} +1 -1
- package/public/dist/assets/BrowserPanel-0b3deLGR.js +3 -0
- package/public/dist/assets/BrowserPanel-D7SnTts8.css +1 -0
- package/public/dist/assets/{Heartbeat-Ca8DmJcw.js → Heartbeat-CD1R95Kv.js} +1 -1
- package/public/dist/assets/{Memory-BY88ur_h.js → Memory-CpZIdE1o.js} +1 -1
- package/public/dist/assets/{ModelProvider-mtAtYLmW.js → ModelProvider-tL42E5dy.js} +1 -1
- package/public/dist/assets/agent-meta-KPnVam9G.js +1 -0
- package/public/dist/assets/{app-CYayckBa.js → app-BvRV-aEt.js} +4 -4
- package/public/dist/assets/bgtask-badge-DLqpImpn.js +1 -0
- package/public/dist/assets/{bgtask-badge-CT6s6rND.js → bgtask-badge-FiEB3FJm.js} +1 -1
- package/public/dist/assets/constants-BfYYzHNr.js +1 -0
- package/public/dist/assets/{employees-DCju7V1x.js → employees-86F_D_Ce.js} +1 -1
- package/public/dist/assets/iframe-renderer-C_swzquc.js +1 -0
- package/public/dist/assets/{iframe-renderer-LZp7Nqm1.js → iframe-renderer-eozJpgjk.js} +2 -2
- package/public/dist/assets/manager-BnmWx9xL.js +13 -0
- package/public/dist/assets/manager-Cmu0aKPb.css +1 -0
- package/public/dist/assets/{memory-V5caYbpS.js → memory-DX7U3WV_.js} +1 -1
- package/public/dist/assets/memory-DfPbqPTD.js +1 -0
- package/public/dist/assets/{message-history-Bp_sUDSx.js → message-history-B_fIGorC.js} +1 -1
- package/public/dist/assets/message-history-BhSsLjis.js +1 -0
- package/public/dist/assets/{render-Cd4frZ2U.js → render-fKAuISeM.js} +1 -1
- package/public/dist/assets/settings-BNqSR9Zr.js +1 -0
- package/public/dist/assets/{settings-a0blK1fr.js → settings-CH6exiZf.js} +1 -1
- package/public/dist/assets/{settings-core-C5qo8giT.js → settings-core-DqKyyxbQ.js} +1 -1
- package/public/dist/assets/settings-core-x7CmBZq1.js +1 -0
- package/public/dist/assets/{sidebar-FbI4udzs.js → sidebar-qyLy9Og8.js} +3 -3
- package/public/dist/assets/skills-C7Yi3LIq.js +1 -0
- package/public/dist/assets/{skills-DeG9lWF2.js → skills-DAhOtbfP.js} +1 -1
- package/public/dist/assets/{slash-commands-FoQuZXga.js → slash-commands-BYH6YVS4.js} +1 -1
- package/public/dist/assets/slash-commands-DEbJk3sj.js +1 -0
- package/public/dist/assets/{trace-drawer-CWs8ydMO.js → trace-drawer-nyAJ3Bm_.js} +1 -1
- package/public/dist/assets/{ui-Cd8etFwN.js → ui-C62w6bJS.js} +1 -1
- package/public/dist/assets/ui-CBErZPBp.js +1 -0
- package/public/dist/index.html +1 -1
- package/public/dist/manager/index.html +2 -2
- package/public/js/constants.ts +22 -5
- package/public/js/features/settings-types.ts +5 -0
- package/public/manager/src/App.tsx +1 -1
- package/public/manager/src/AppChrome.tsx +18 -3
- package/public/manager/src/SidebarRailRouter.tsx +47 -1
- package/public/manager/src/browser-panel/BrowserPanel.tsx +151 -57
- package/public/manager/src/browser-panel/browser-address-bar.tsx +53 -0
- package/public/manager/src/browser-panel/browser-address-state.ts +41 -0
- package/public/manager/src/browser-panel/browser-favicon.ts +18 -0
- package/public/manager/src/browser-panel/browser-history-store.ts +80 -0
- package/public/manager/src/browser-panel/browser-panel.css +141 -24
- package/public/manager/src/components/CommandBar.tsx +17 -5
- package/public/manager/src/components/InstanceGroups.tsx +143 -23
- package/public/manager/src/components/InstanceNavigator.tsx +55 -2
- package/public/manager/src/components/InstanceRow.tsx +51 -3
- package/public/manager/src/components/SidebarRail.tsx +10 -10
- package/public/manager/src/components/SidebarResizeHandle.tsx +22 -0
- package/public/manager/src/components/WorkspaceLayout.tsx +22 -8
- package/public/manager/src/components/instance-row-status.ts +48 -0
- package/public/manager/src/components/sidebar-keyboard.ts +129 -0
- package/public/manager/src/dashboard-settings/DashboardSettingsWorkspace.tsx +42 -0
- package/public/manager/src/help/helpContent.tsx +1 -0
- package/public/manager/src/hooks/useSidebarGroupCollapse.ts +46 -0
- package/public/manager/src/hooks/useSidebarWidth.ts +88 -0
- package/public/manager/src/hooks/useWindowFullscreen.ts +23 -0
- package/public/manager/src/manager-components.css +206 -31
- package/public/manager/src/manager-layout.css +30 -19
- package/public/manager/src/manager-p0-1-1.css +72 -36
- package/public/manager/src/manager-polish.css +6 -45
- package/public/manager/src/manager-shortcut-runner.ts +37 -1
- package/public/manager/src/manager-shortcuts.ts +21 -0
- package/public/manager/src/manager-tokens.css +190 -127
- package/public/manager/src/panels/PanelResizer.tsx +18 -2
- package/public/manager/src/panels/desktop-bridge.ts +12 -1
- package/public/manager/src/panels/panels.css +32 -32
- package/public/manager/src/settings/pages/Agent.tsx +26 -0
- package/public/manager/src/settings/pages/components/agent/agent-meta.ts +6 -5
- package/public/manager/src/styles.css +15 -7
- package/public/manager/src/types.ts +5 -1
- package/public/dist/assets/BrowserPanel-CsYKexNf.js +0 -3
- package/public/dist/assets/BrowserPanel-DpntR8EJ.css +0 -1
- package/public/dist/assets/agent-meta-_Idad3qq.js +0 -1
- package/public/dist/assets/bgtask-badge-CidVvjWb.js +0 -1
- package/public/dist/assets/constants-DK-21dMG.js +0 -1
- package/public/dist/assets/iframe-renderer-aFXUtTXX.js +0 -1
- package/public/dist/assets/manager-CwqrZ2MH.js +0 -13
- package/public/dist/assets/manager-LkGs6qEJ.css +0 -1
- package/public/dist/assets/memory-BcZdf_oz.js +0 -1
- package/public/dist/assets/message-history-DrEPUdTy.js +0 -1
- package/public/dist/assets/settings-core-Cm-CmVLA.js +0 -1
- package/public/dist/assets/settings-mbGEtP5B.js +0 -1
- package/public/dist/assets/skills-G8KUeHBV.js +0 -1
- package/public/dist/assets/slash-commands-BYv3yboz.js +0 -1
- package/public/dist/assets/ui-yrn3s72A.js +0 -1
package/dist/src/agent/spawn.js
CHANGED
|
@@ -18,7 +18,7 @@ import { settleOnce } from '../orchestrator/request-registry.js';
|
|
|
18
18
|
import { settings, UPLOADS_DIR, detectCli, getProjectDirs } from '../core/config.js';
|
|
19
19
|
import { migrateLegacyClaudeValue } from '../cli/claude-models.js';
|
|
20
20
|
import { stripUndefined } from '../core/strip-undefined.js';
|
|
21
|
-
import { clearEmployeeSession, getSession, insertMessage, insertMessageWithTraceRun, getRecentMessages, listQueuedMessages, insertQueuedMessage, deleteQueuedMessage, migrateQueuedMessagesV1ToV2, getSessionBucket, clearSessionBucket, setSessionBucketSnapshot, } from '../core/db.js';
|
|
21
|
+
import { clearEmployeeSession, getSession, insertMessage, insertMessageWithTraceRun, getRecentMessages, listQueuedMessages, insertQueuedMessage, deleteQueuedMessage, migrateQueuedMessagesV1ToV2, getSessionBucket, clearSessionBucket, setSessionBucketSnapshot, getMaxMessageId, getSteerSalvageAfter, } from '../core/db.js';
|
|
22
22
|
import { sanitizeToolLogForDurableStorage } from '../shared/tool-log-sanitize.js';
|
|
23
23
|
import { buildTaskSnapshot } from '../memory/runtime.js';
|
|
24
24
|
import { getActiveChatSession, getRemoteBoundSessionId } from '../core/chat-sessions.js';
|
|
@@ -42,7 +42,7 @@ import { stripInterviewTracker } from '../orchestrator/sanitize.js';
|
|
|
42
42
|
import { beginLiveRun, appendLiveRunText, setLiveRunTraceId, clearLiveRun, replaceLiveRunTools, appendLiveRunTool, getLiveRun } from './live-run-state.js';
|
|
43
43
|
import { setSpawnRef as setMemorySpawnRef, } from './memory-flush-controller.js';
|
|
44
44
|
import { applyCliEnvDefaults, buildSessionResumeKey, ensureOpencodeAlwaysAllowPermissions, mergeEnvWindowsSafe } from './spawn-env.js';
|
|
45
|
-
import { buildPromptForArgs, shouldBuildHistoryBlock, withHistoryPrompt } from './prompt-context.js';
|
|
45
|
+
import { buildPromptForArgs, shouldBuildHistoryBlock, withHistoryPrompt, withSteerContext } from './prompt-context.js';
|
|
46
46
|
import { attachWatchdog, DEFAULT_WATCHDOG_ABSOLUTE_HARD_CAP_MS } from './watchdog.js';
|
|
47
47
|
import { buildOpencodeRuntimeSnapshot, buildOpencodeSpawnAudit, pushOpencodeRawEvent, resolveOpencodeBinary, } from './opencode-diagnostics.js';
|
|
48
48
|
import { asCliEventRecord, discriminate, fieldString } from '../types/cli-events.js';
|
|
@@ -352,6 +352,48 @@ function consumeKillReason(pid) {
|
|
|
352
352
|
killReasons.delete(pid);
|
|
353
353
|
return reason;
|
|
354
354
|
}
|
|
355
|
+
// ─── Steer exit-settle barrier ─────────────────────
|
|
356
|
+
// killActiveAgent removes the scope's activeMainProcesses entry synchronously,
|
|
357
|
+
// so waitForProcessEnd() resolves immediately on a steer kill — long before the
|
|
358
|
+
// exit handler has written the interrupted partial output to the messages table.
|
|
359
|
+
// A follow-up spawn could then read history without the salvage row. The barrier
|
|
360
|
+
// is armed at kill time (never at exit-handler entry — that is already too late)
|
|
361
|
+
// and settled by the exit handler's completion, success or failure.
|
|
362
|
+
const exitSettlers = new Map();
|
|
363
|
+
/** Arm the barrier. Idempotent: a repeated steer keeps the first arm. */
|
|
364
|
+
export function armExitSettle(scopeKey) {
|
|
365
|
+
if (exitSettlers.has(scopeKey))
|
|
366
|
+
return;
|
|
367
|
+
let resolve;
|
|
368
|
+
const promise = new Promise(r => { resolve = r; });
|
|
369
|
+
exitSettlers.set(scopeKey, { promise, resolve });
|
|
370
|
+
}
|
|
371
|
+
/** Settle the barrier; a no-op when no steer kill armed it. */
|
|
372
|
+
export function settleExit(scopeKey) {
|
|
373
|
+
const entry = exitSettlers.get(scopeKey);
|
|
374
|
+
if (!entry)
|
|
375
|
+
return;
|
|
376
|
+
exitSettlers.delete(scopeKey);
|
|
377
|
+
entry.resolve();
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Await the armed exit handler's completion, bounded. A timeout releases the
|
|
381
|
+
* waiter and drops the arm — a wedged exit handler must not hang the steer.
|
|
382
|
+
*/
|
|
383
|
+
export function waitForExitSettled(scopeKey, timeoutMs = 5000) {
|
|
384
|
+
const entry = exitSettlers.get(scopeKey);
|
|
385
|
+
if (!entry)
|
|
386
|
+
return Promise.resolve();
|
|
387
|
+
// The timer is NOT unref'd and is always cleared: an unref'd timer can vanish
|
|
388
|
+
// with a drained event loop (test runner), leaving the waiter pending forever.
|
|
389
|
+
let timer;
|
|
390
|
+
const timeout = new Promise(r => { timer = setTimeout(r, timeoutMs); });
|
|
391
|
+
return Promise.race([entry.promise, timeout]).then(() => {
|
|
392
|
+
clearTimeout(timer);
|
|
393
|
+
if (exitSettlers.get(scopeKey) === entry)
|
|
394
|
+
exitSettlers.delete(scopeKey);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
355
397
|
/**
|
|
356
398
|
* Fix A: 사용자 stop은 메모리 큐 + DB persisted_queue + frontend pending row를
|
|
357
399
|
* 모두 폐기한다. exit handler의 scoped queue 자동 드레인이 stop 직후 잔존
|
|
@@ -417,6 +459,8 @@ export function killActiveAgent(scopeKeyOrReason = 'user', scopedReason) {
|
|
|
417
459
|
if (run.process?.pid)
|
|
418
460
|
killReasons.set(run.process.pid, reason);
|
|
419
461
|
console.log(`[jaw:kill] reason=${reason} scope=${scopeKey} cli=${getActiveMainCli(scopeKey)} action=lease.cancel`);
|
|
462
|
+
if (reason === 'steer' || reason === 'interrupt')
|
|
463
|
+
armExitSettle(scopeKey);
|
|
420
464
|
run.cancelTurn(reason);
|
|
421
465
|
if (reason === 'api' || reason === 'user' || reason === 'steer' || reason === 'interrupt')
|
|
422
466
|
activeMainProcesses.delete(scopeKey);
|
|
@@ -432,6 +476,8 @@ export function killActiveAgent(scopeKeyOrReason = 'user', scopedReason) {
|
|
|
432
476
|
console.log(`[jaw:kill] reason=${reason} scope=${scopeKey} cli=${getActiveMainCli(scopeKey) || 'unknown'} signal=${policy.signal} escalationMs=${policy.escalationMs}`);
|
|
433
477
|
if (activeProcess.pid)
|
|
434
478
|
killReasons.set(activeProcess.pid, reason);
|
|
479
|
+
if (reason === 'steer' || reason === 'interrupt')
|
|
480
|
+
armExitSettle(scopeKey);
|
|
435
481
|
const proc = activeProcess;
|
|
436
482
|
// One owner runs the whole termination: tree walk with the policy signal,
|
|
437
483
|
// then escalation after policy.escalationMs that re-checks the ORIGINAL
|
|
@@ -537,7 +583,10 @@ export function waitForAllProcessesEnd(timeoutMs = 2000) {
|
|
|
537
583
|
}
|
|
538
584
|
export function canSteerAgent(scopeKey) {
|
|
539
585
|
const run = activeMainProcesses.get(scopeKey);
|
|
540
|
-
|
|
586
|
+
if (run?.meta.cli === 'jwc' && jawRuntimesByScope.get(scopeKey)?.busy === true)
|
|
587
|
+
return true;
|
|
588
|
+
// codex-app: the hook exists exactly while a steerable turn is in flight.
|
|
589
|
+
return typeof run?.steerTurnInBand === 'function';
|
|
541
590
|
}
|
|
542
591
|
export async function steerAgent(scopeKey, newPrompt, source, meta) {
|
|
543
592
|
const run = activeMainProcesses.get(scopeKey);
|
|
@@ -552,27 +601,72 @@ export async function steerAgent(scopeKey, newPrompt, source, meta) {
|
|
|
552
601
|
// event will ever carry this id. Settling here is what stops a caller
|
|
553
602
|
// from waiting for an answer that structurally cannot arrive.
|
|
554
603
|
settleOnce(meta?.requestId, 'steered');
|
|
555
|
-
return;
|
|
604
|
+
return 'steered';
|
|
605
|
+
}
|
|
606
|
+
if (typeof run?.steerTurnInBand === 'function') {
|
|
607
|
+
// codex-app same-turn steer. The user row is written only AFTER the
|
|
608
|
+
// server accepts — a fallback must not leave a duplicate insert for the
|
|
609
|
+
// queued path to write again.
|
|
610
|
+
let outcome;
|
|
611
|
+
try {
|
|
612
|
+
outcome = await run.steerTurnInBand(newPrompt);
|
|
613
|
+
}
|
|
614
|
+
catch (err) {
|
|
615
|
+
console.error('[jaw:steer] codex-app in-band steer failed:', err.message);
|
|
616
|
+
return 'fallback-queue';
|
|
617
|
+
}
|
|
618
|
+
if (outcome !== 'steered') {
|
|
619
|
+
if (outcome === 'rejected') {
|
|
620
|
+
// review/compact turns structurally reject steer — tell the user
|
|
621
|
+
// their message was queued instead, not silently swallowed.
|
|
622
|
+
broadcast('steer_rejected', stripUndefined({ prompt: newPrompt, origin: source || 'web', scope: scopeKey, sessionId: chatSessionId, reason: 'turn-not-steerable', requestId: meta?.requestId }));
|
|
623
|
+
}
|
|
624
|
+
return 'fallback-queue';
|
|
625
|
+
}
|
|
626
|
+
insertMessage.run('user', newPrompt, source, '', settings["workingDir"] || null, chatSessionId);
|
|
627
|
+
broadcast('new_message', { role: 'user', content: newPrompt, source, scope: scopeKey, sessionId: chatSessionId });
|
|
628
|
+
broadcast('steer_started', stripUndefined({ prompt: newPrompt, origin: source || 'web', scope: scopeKey, sessionId: chatSessionId, target: meta?.target, chatId: meta?.chatId, requestId: meta?.requestId, remoteKey: meta?.remoteKey, replyViaTarget: meta?.replyViaTarget }));
|
|
629
|
+
settleOnce(meta?.requestId, 'steered');
|
|
630
|
+
return 'steered';
|
|
556
631
|
}
|
|
557
632
|
const steerWaitMs = getSteerWaitMsForActiveAgent(scopeKey);
|
|
633
|
+
// Snapshot BEFORE the kill: the interrupted partial-output row is identified
|
|
634
|
+
// as the first ⏹️-tagged assistant message with id above this mark. A
|
|
635
|
+
// created_at comparison is not safe (second-resolution UTC column).
|
|
636
|
+
const maxIdBeforeKill = getMaxMessageId(chatSessionId);
|
|
558
637
|
const wasRunning = killActiveAgent(scopeKey, 'steer');
|
|
559
638
|
if (wasRunning)
|
|
560
639
|
await waitForProcessEnd(scopeKey, steerWaitMs);
|
|
640
|
+
// The kill removes the scope's map entry synchronously, so the wait above can
|
|
641
|
+
// return before the exit handler's salvage insert. Wait for the settle barrier
|
|
642
|
+
// armed by the kill so the follow-up run actually sees the partial output.
|
|
643
|
+
if (wasRunning)
|
|
644
|
+
await waitForExitSettled(scopeKey);
|
|
645
|
+
let steerContext = null;
|
|
646
|
+
if (wasRunning) {
|
|
647
|
+
const salvage = getSteerSalvageAfter(chatSessionId, maxIdBeforeKill);
|
|
648
|
+
// The ⏹️ tag is a human-facing marker; the model gets the payload only.
|
|
649
|
+
steerContext = salvage ? salvage.replace(/^⏹️ \[interrupted\]\s*/, '') : null;
|
|
650
|
+
}
|
|
561
651
|
insertMessage.run('user', newPrompt, source, '', settings["workingDir"] || null, chatSessionId);
|
|
562
652
|
broadcast('new_message', { role: 'user', content: newPrompt, source, scope: scopeKey, sessionId: chatSessionId });
|
|
563
653
|
broadcast('steer_started', stripUndefined({ prompt: newPrompt, origin: source || 'web', scope: scopeKey, requestId: meta?.requestId }));
|
|
564
654
|
const { orchestrate, orchestrateContinue, orchestrateReset, isContinueIntent, isResetIntent } = await import('../orchestrator/pipeline.js');
|
|
565
655
|
const origin = source || 'web';
|
|
656
|
+
const steerMeta = stripUndefined({ origin, scope: scopeKey, chatSessionId, requestId: meta?.requestId, _skipInsert: true, _steerContext: steerContext || undefined });
|
|
566
657
|
const task = isResetIntent(newPrompt)
|
|
567
|
-
? orchestrateReset(
|
|
658
|
+
? orchestrateReset(steerMeta)
|
|
568
659
|
: isContinueIntent(newPrompt)
|
|
569
|
-
? orchestrateContinue(
|
|
570
|
-
: orchestrate(newPrompt,
|
|
660
|
+
? orchestrateContinue(steerMeta)
|
|
661
|
+
: orchestrate(newPrompt, steerMeta);
|
|
571
662
|
task.catch(async (err) => {
|
|
572
663
|
console.error('[steer:orchestrate]', err.message);
|
|
573
664
|
broadcast('orchestrate_done', stripUndefined({ text: `[error] ${err.message}`, error: true, origin, requestId: meta?.requestId }));
|
|
574
665
|
settleOnce(meta?.requestId, 'failed', { error: err.message });
|
|
575
666
|
});
|
|
667
|
+
// The follow-up was started as a new run (kill-path or idle race). The caller
|
|
668
|
+
// must NOT also queue the message.
|
|
669
|
+
return 'new-run';
|
|
576
670
|
}
|
|
577
671
|
// ─── Helpers ─────────────────────────────────────────
|
|
578
672
|
export function makeCleanEnv(extraEnv = {}, inheritedEnv = process.env, platform = process.platform) {
|
|
@@ -712,7 +806,7 @@ export const saveUpload = (buffer, originalName, options) => _saveUpload(UPLOADS
|
|
|
712
806
|
export { buildMediaPrompt, buildMediaPromptMany };
|
|
713
807
|
// ─── Spawn Agent ─────────────────────────────────────
|
|
714
808
|
import { AcpClient } from '../cli/acp-client.js';
|
|
715
|
-
import { CodexAppClient, isRecoverableResumeError } from './codex-app-client.js';
|
|
809
|
+
import { CodexAppClient, CodexSteerError, isRecoverableResumeError } from './codex-app-client.js';
|
|
716
810
|
import { acquireCodexAppRuntime, acquirePiRuntime, } from './runtime-pool.js';
|
|
717
811
|
import { acquireCodexAppLane, CodexHostGenerationStaleError, prepareCodexAppHost, } from './codex-host-pool.js';
|
|
718
812
|
import { loadCatalogEfforts, resolveCatalogPath, validateModelEffort } from './codex-app-catalog.js';
|
|
@@ -1104,6 +1198,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1104
1198
|
sysPrompt,
|
|
1105
1199
|
isResume,
|
|
1106
1200
|
});
|
|
1201
|
+
promptForArgs = withSteerContext(promptForArgs, opts.steerContext);
|
|
1107
1202
|
const agyResumeReplayPrefix = cli === 'agy' && isResume
|
|
1108
1203
|
? getLatestAssistantContentForAgyResume(settings["workingDir"], chatSessionId)
|
|
1109
1204
|
: null;
|
|
@@ -1498,7 +1593,8 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1498
1593
|
const acpPrompt = needsHistoryFallback
|
|
1499
1594
|
? withHistoryPrompt(prompt, fallbackHistory)
|
|
1500
1595
|
: (isResume ? prompt : withHistoryPrompt(prompt, historyBlock));
|
|
1501
|
-
const
|
|
1596
|
+
const acpPromptWithSteer = withSteerContext(acpPrompt, opts.steerContext);
|
|
1597
|
+
const { promise: promptPromise } = acp.prompt(acpPromptWithSteer);
|
|
1502
1598
|
const promptResult = await promptPromise;
|
|
1503
1599
|
promptCompleted = true;
|
|
1504
1600
|
if (process.env["DEBUG"])
|
|
@@ -1575,7 +1671,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1575
1671
|
processQueue,
|
|
1576
1672
|
}).catch((err) => {
|
|
1577
1673
|
console.error('[jaw:lifecycle] handleAgentExit failed (ACP):', err.message);
|
|
1578
|
-
});
|
|
1674
|
+
}).finally(() => settleExit(scopeKey));
|
|
1579
1675
|
});
|
|
1580
1676
|
return { child, promise: resultPromise };
|
|
1581
1677
|
}
|
|
@@ -1589,7 +1685,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1589
1685
|
}
|
|
1590
1686
|
const piSessionId = isResume && bucketSessionId ? bucketSessionId : '';
|
|
1591
1687
|
console.log(`[jaw:pi] isResume=${isResume}, bucketSessionId=${bucketSessionId || 'none'}, piSessionId=${piSessionId || 'new'}`);
|
|
1592
|
-
const piPrompt = piSessionId ? prompt : withHistoryPrompt(prompt, historyBlock);
|
|
1688
|
+
const piPrompt = withSteerContext(piSessionId ? prompt : withHistoryPrompt(prompt, historyBlock), opts.steerContext);
|
|
1593
1689
|
const traceRunId = startTraceRun({ cli, model: runtimeModel, workingDir: settings["workingDir"] || null, agentLabel, audience: traceAudience });
|
|
1594
1690
|
const ctx = {
|
|
1595
1691
|
fullText: '',
|
|
@@ -1772,7 +1868,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1772
1868
|
fallbackState: queueCtrl.fallbackStateForScope(scopeKey),
|
|
1773
1869
|
fallbackMaxRetries: FALLBACK_MAX_RETRIES,
|
|
1774
1870
|
processQueue,
|
|
1775
|
-
});
|
|
1871
|
+
}).finally(() => settleExit(scopeKey));
|
|
1776
1872
|
}).catch(async (err) => {
|
|
1777
1873
|
piWatchdog.stop();
|
|
1778
1874
|
await releaseLease().catch(() => { });
|
|
@@ -1798,7 +1894,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1798
1894
|
processQueue,
|
|
1799
1895
|
}).catch((handleErr) => {
|
|
1800
1896
|
console.error('[jaw:lifecycle] handleAgentExit failed (Pi):', handleErr.message);
|
|
1801
|
-
});
|
|
1897
|
+
}).finally(() => settleExit(scopeKey));
|
|
1802
1898
|
});
|
|
1803
1899
|
};
|
|
1804
1900
|
if (opts.agentId) {
|
|
@@ -2026,6 +2122,25 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2026
2122
|
const cancelHook = (_reason) => { void requestLeaseCancel(); };
|
|
2027
2123
|
if (lease && mainRun)
|
|
2028
2124
|
mainRun.cancelTurn = cancelHook;
|
|
2125
|
+
// Same-turn steer (turn/steer) for the duration of THIS turn only:
|
|
2126
|
+
// installed and torn down with cancelHook so capability reads never
|
|
2127
|
+
// outlive the steerable window.
|
|
2128
|
+
const steerHook = async (text) => {
|
|
2129
|
+
try {
|
|
2130
|
+
await appClient.steerTurn(laneScope, text, { clientUserMessageId: crypto.randomUUID() });
|
|
2131
|
+
return 'steered';
|
|
2132
|
+
}
|
|
2133
|
+
catch (err) {
|
|
2134
|
+
if (err instanceof CodexSteerError) {
|
|
2135
|
+
// review/compact turns reject steer; a mismatched/finished
|
|
2136
|
+
// turn raced us. Both are queue-fallback territory.
|
|
2137
|
+
return err.code === 'not-steerable' ? 'rejected' : 'unavailable';
|
|
2138
|
+
}
|
|
2139
|
+
throw err;
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
2142
|
+
if (mainRun)
|
|
2143
|
+
mainRun.steerTurnInBand = steerHook;
|
|
2029
2144
|
const watchdogTimeout = (kind) => {
|
|
2030
2145
|
if (watchdogCancel)
|
|
2031
2146
|
return;
|
|
@@ -2109,7 +2224,8 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2109
2224
|
const codexAppPrompt = (shouldPrependHistory && historyBlock)
|
|
2110
2225
|
? `${historyBlock}\n\n[User Message]\n${prompt}`
|
|
2111
2226
|
: prompt;
|
|
2112
|
-
const
|
|
2227
|
+
const codexAppPromptWithSteer = withSteerContext(codexAppPrompt, opts.steerContext);
|
|
2228
|
+
const startTurn = appClient.startTurn(laneScope, codexAppPromptWithSteer);
|
|
2113
2229
|
await Promise.race([startTurn, turnDone]);
|
|
2114
2230
|
await turnDone;
|
|
2115
2231
|
turnCompleted = !turnReportedFailure;
|
|
@@ -2151,6 +2267,8 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2151
2267
|
await watchdogCancel;
|
|
2152
2268
|
if (mainRun?.cancelTurn === cancelHook)
|
|
2153
2269
|
delete mainRun.cancelTurn;
|
|
2270
|
+
if (mainRun?.steerTurnInBand === steerHook)
|
|
2271
|
+
delete mainRun.steerTurnInBand;
|
|
2154
2272
|
listener.dispose();
|
|
2155
2273
|
if (lease)
|
|
2156
2274
|
lease.release();
|
|
@@ -2195,7 +2313,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2195
2313
|
processQueue,
|
|
2196
2314
|
}).catch((err) => {
|
|
2197
2315
|
console.error('[jaw:lifecycle] handleAgentExit failed (codex-app):', err.message);
|
|
2198
|
-
});
|
|
2316
|
+
}).finally(() => settleExit(scopeKey));
|
|
2199
2317
|
};
|
|
2200
2318
|
if (opts.agentId) {
|
|
2201
2319
|
const employeeLaneScope = `employee:${opts.agentId}`;
|
|
@@ -2498,21 +2616,21 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2498
2616
|
insertMessage.run('user', prompt, cli, runtimeModel, settings["workingDir"] || null, chatSessionId);
|
|
2499
2617
|
}
|
|
2500
2618
|
if (cli === 'claude') {
|
|
2501
|
-
child.stdin.write(isResume ? prompt : withHistoryPrompt(prompt, historyBlock));
|
|
2619
|
+
child.stdin.write(withSteerContext(isResume ? prompt : withHistoryPrompt(prompt, historyBlock), opts.steerContext));
|
|
2502
2620
|
}
|
|
2503
2621
|
else if (cli === 'claude-e' || (cli === 'ai-e' && effectiveProvider === 'claude')) {
|
|
2504
|
-
child.stdin.write(isResume ? prompt : withHistoryPrompt(prompt, historyBlock));
|
|
2622
|
+
child.stdin.write(withSteerContext(isResume ? prompt : withHistoryPrompt(prompt, historyBlock), opts.steerContext));
|
|
2505
2623
|
}
|
|
2506
2624
|
else if (cli === 'codex' && !isResume) {
|
|
2507
2625
|
const codexStdin = historyBlock
|
|
2508
2626
|
? `${historyBlock}\n\n[User Message]\n${prompt}`
|
|
2509
2627
|
: `[User Message]\n${prompt}`;
|
|
2510
|
-
child.stdin.write(codexStdin);
|
|
2628
|
+
child.stdin.write(withSteerContext(codexStdin, opts.steerContext));
|
|
2511
2629
|
}
|
|
2512
2630
|
else if (cli === 'codex' && isResume) {
|
|
2513
2631
|
// Resume passes '-' in argv (see args.ts) so the prompt travels on stdin,
|
|
2514
2632
|
// matching the fresh path.
|
|
2515
|
-
child.stdin.write(prompt || '');
|
|
2633
|
+
child.stdin.write(withSteerContext(prompt || '', opts.steerContext));
|
|
2516
2634
|
}
|
|
2517
2635
|
child.stdin.end();
|
|
2518
2636
|
if (!opts.internal)
|
|
@@ -3127,7 +3245,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
3127
3245
|
...(agyTotalOutputLen > 0 ? { outputLen: agyTotalOutputLen } : {}),
|
|
3128
3246
|
}).catch((err) => {
|
|
3129
3247
|
console.error('[jaw:lifecycle] handleAgentExit failed (CLI):', err.message);
|
|
3130
|
-
});
|
|
3248
|
+
}).finally(() => settleExit(scopeKey));
|
|
3131
3249
|
});
|
|
3132
3250
|
return { child, promise: resultPromise };
|
|
3133
3251
|
}
|