clay-server 4.7.0-beta.2 → 4.7.0-beta.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/lib/project-pair-result-outbox.js +1 -0
- package/lib/project-pair-result-recovery.js +15 -2
- package/lib/public/app.js +2 -0
- package/lib/public/css/input.css +27 -0
- package/lib/public/css/messages.css +15 -8
- package/lib/public/index.html +3 -0
- package/lib/public/modules/app-connection.js +23 -2
- package/lib/public/modules/app-favicon.js +1 -0
- package/lib/public/modules/app-messages.js +2 -1
- package/lib/public/modules/app-rendering.js +16 -10
- package/lib/public/modules/generation-stop.js +24 -0
- package/lib/public/modules/pair-result-status.js +27 -2
- package/lib/sdk-bridge.js +17 -3
- package/lib/sessions.js +2 -0
- package/lib/yoke/acp-process-manager.js +25 -15
- package/lib/yoke/acp-query-handle.js +52 -40
- package/lib/yoke/adapters/antigravity.js +25 -1
- package/lib/yoke/adapters/claude.js +25 -7
- package/lib/yoke/adapters/codex.js +55 -24
- package/lib/yoke/adapters/kiro.js +30 -19
- package/lib/yoke/codex-turn-events.js +32 -0
- package/lib/yoke/codex-turn-stop.js +59 -0
- package/lib/yoke/query-abort.js +40 -0
- package/lib/yoke/user-input-guidance.js +11 -0
- package/lib/yoke/user-input.js +1 -1
- package/package.json +1 -1
|
@@ -387,6 +387,7 @@ function attachPairResultOutbox(options) {
|
|
|
387
387
|
markRestartUncertain: markRestartUncertain,
|
|
388
388
|
authorizeHumanTurn: authorizeHumanTurn,
|
|
389
389
|
grantManualRetry: grantManualRetry,
|
|
390
|
+
dismissNotice: function (key, revision) { return transition(key, { dismissedNoticeRevision: revision }); },
|
|
390
391
|
status: status,
|
|
391
392
|
list: function () { return Object.keys(records).map(function (key) { return copy(records[key]); }); },
|
|
392
393
|
get: function (key) { return records[key] ? copy(records[key]) : null; },
|
|
@@ -146,6 +146,11 @@ function attachPairResultRecovery(ctx) {
|
|
|
146
146
|
confirmDuplicate: completed && state === "uncertain",
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
+
function noticeItem(record) {
|
|
150
|
+
var item = publicItem(record);
|
|
151
|
+
item.revision = opaqueId(JSON.stringify(item));
|
|
152
|
+
return item;
|
|
153
|
+
}
|
|
149
154
|
function canRead(ws, session) {
|
|
150
155
|
if (!ws || !session || ws._clayActiveSession !== session.localId) return false;
|
|
151
156
|
if (ownerId(session)) return !!(ws._clayUser && String(ws._clayUser.id) === String(ownerId(session)));
|
|
@@ -153,7 +158,9 @@ function attachPairResultRecovery(ctx) {
|
|
|
153
158
|
}
|
|
154
159
|
function sendState(session, ws, sendFn) {
|
|
155
160
|
if (!canRead(ws, session)) return false;
|
|
156
|
-
var items = recordsFor(session).
|
|
161
|
+
var items = recordsFor(session).filter(function (record) {
|
|
162
|
+
return record.dismissedNoticeRevision !== noticeItem(record).revision;
|
|
163
|
+
}).map(noticeItem);
|
|
157
164
|
(sendFn || ctx.sendTo.bind(null, ws))({ type: "pair_result_status", sessionId: session.localId, items: items });
|
|
158
165
|
return true;
|
|
159
166
|
}
|
|
@@ -204,7 +211,7 @@ function attachPairResultRecovery(ctx) {
|
|
|
204
211
|
changed(session);
|
|
205
212
|
}
|
|
206
213
|
function handleMessage(ws, msg) {
|
|
207
|
-
if (!msg || (msg.type !== "pair_result_state_request" && msg.type !== "pair_result_retry")) return false;
|
|
214
|
+
if (!msg || (msg.type !== "pair_result_state_request" && msg.type !== "pair_result_retry" && msg.type !== "pair_result_dismiss")) return false;
|
|
208
215
|
var session = ctx.sm.sessions.get(ws._clayActiveSession);
|
|
209
216
|
if (!canRead(ws, session)) return true;
|
|
210
217
|
if (msg.sessionId != null && msg.sessionId !== session.localId) return true;
|
|
@@ -213,6 +220,12 @@ function attachPairResultRecovery(ctx) {
|
|
|
213
220
|
var records = recordsFor(session), record = null;
|
|
214
221
|
for (var i = 0; i < records.length; i++) if (opaqueId(records[i].key) === msg.id) record = records[i];
|
|
215
222
|
if (!record) { sendState(session, ws); return true; }
|
|
223
|
+
if (msg.type === "pair_result_dismiss") {
|
|
224
|
+
if (msg.revision === noticeItem(record).revision) ctx.outbox.dismissNotice(record.key, msg.revision);
|
|
225
|
+
changed(session);
|
|
226
|
+
sendState(session, ws);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
216
229
|
if (record.deliveryState === "uncertain" && msg.confirmDuplicate !== true) { sendState(session, ws); return true; }
|
|
217
230
|
var granted = ctx.outbox.grantManualRetry(record.key);
|
|
218
231
|
if (granted.ok) schedule(record.key);
|
package/lib/public/app.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { initGenerationStop } from './modules/generation-stop.js';
|
|
1
2
|
import { initIssueLinks } from './modules/clay-issue-links.js';
|
|
2
3
|
import { initIssues, closeIssues } from './modules/issues.js';
|
|
3
4
|
import { initIssuesContext } from './modules/issues-context.js';
|
|
@@ -821,6 +822,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
821
822
|
|
|
822
823
|
// --- Notifications module ---
|
|
823
824
|
initAppNotifications();
|
|
825
|
+
initGenerationStop();
|
|
824
826
|
|
|
825
827
|
// --- Panels module ---
|
|
826
828
|
initPanels();
|
package/lib/public/css/input.css
CHANGED
|
@@ -1717,3 +1717,30 @@ body.tui-composer-active #config-chip-wrap {
|
|
|
1717
1717
|
font-weight: 600;
|
|
1718
1718
|
color: inherit;
|
|
1719
1719
|
}
|
|
1720
|
+
|
|
1721
|
+
/* Stop remains available when Send is occupied by a draft or Worker lock. */
|
|
1722
|
+
#generation-stop-row {
|
|
1723
|
+
max-width: var(--content-width);
|
|
1724
|
+
margin: 6px auto 0;
|
|
1725
|
+
display: flex;
|
|
1726
|
+
justify-content: flex-end;
|
|
1727
|
+
}
|
|
1728
|
+
#generation-stop-row.hidden { display: none; }
|
|
1729
|
+
body:not(.worker-pane-locked) #input-area:has(#send-btn.stop:not(:disabled)) #generation-stop-row { display: none; }
|
|
1730
|
+
#generation-stop {
|
|
1731
|
+
display: inline-flex;
|
|
1732
|
+
align-items: center;
|
|
1733
|
+
gap: 6px;
|
|
1734
|
+
min-height: 36px;
|
|
1735
|
+
padding: 6px 12px;
|
|
1736
|
+
border: 1px solid var(--border);
|
|
1737
|
+
border-radius: 18px;
|
|
1738
|
+
background: var(--sidebar-bg);
|
|
1739
|
+
color: var(--text-secondary);
|
|
1740
|
+
font: inherit;
|
|
1741
|
+
font-size: 12px;
|
|
1742
|
+
cursor: pointer;
|
|
1743
|
+
}
|
|
1744
|
+
#generation-stop:hover:not(:disabled) { color: var(--text); background: var(--code-bg); }
|
|
1745
|
+
#generation-stop:disabled { opacity: 0.5; cursor: default; }
|
|
1746
|
+
#generation-stop .lucide { width: 12px; height: 12px; }
|
|
@@ -1387,17 +1387,24 @@ pre.mermaid-error {
|
|
|
1387
1387
|
.plan-banner.plan-exit { color: var(--success); }
|
|
1388
1388
|
.plan-banner.plan-exit .plan-banner-icon { color: var(--success); }
|
|
1389
1389
|
|
|
1390
|
-
|
|
1391
|
-
.pair-result-status
|
|
1390
|
+
/* Recovery notices use the same quiet surface as session work references. */
|
|
1391
|
+
.pair-result-status { flex: 0 0 auto; margin: 8px 16px 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-alt); color: var(--text-muted); font-size: 12px; line-height: 1.5; max-height: 35vh; overflow-y: auto; }
|
|
1392
|
+
.pair-result-status-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--text-secondary); }
|
|
1393
|
+
.pair-result-status button { font: inherit; color: var(--text-secondary); background: transparent; border: 1px solid var(--border); border-radius: 5px; cursor: pointer; }
|
|
1394
|
+
.pair-result-status .pair-result-status-dismiss { display: grid; place-items: center; flex: 0 0 28px; width: 28px; height: 28px; padding: 0; border: 0; font-size: 20px; line-height: 1; }
|
|
1395
|
+
.pair-result-status button:hover { background: var(--input-bg); color: var(--text); }
|
|
1396
|
+
.pair-result-status button:focus-visible, .pair-result-status summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
1397
|
+
.pair-result-status-details > summary { cursor: pointer; width: fit-content; }
|
|
1398
|
+
.pair-result-status-card { padding: 10px 0; border-top: 1px solid var(--border); margin-top: 8px; }
|
|
1392
1399
|
.pair-result-status-card > strong, .pair-result-status-card > span { display: block; }
|
|
1393
|
-
.pair-result-status-card > strong { color: var(--text); margin-bottom: 3px; }
|
|
1400
|
+
.pair-result-status-card > strong { color: var(--text-secondary); font-weight: 500; margin-bottom: 3px; }
|
|
1394
1401
|
.pair-result-status-card details { margin-top: 7px; }
|
|
1395
|
-
.pair-result-status-card summary { cursor: pointer;
|
|
1396
|
-
.pair-result-status-card pre { max-height: 180px; overflow: auto; white-space: pre-wrap; font: inherit; }
|
|
1397
|
-
.pair-result-status-
|
|
1398
|
-
.pair-result-status-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
1402
|
+
.pair-result-status-card summary { cursor: pointer; }
|
|
1403
|
+
.pair-result-status-card pre { max-height: 180px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
|
|
1404
|
+
.pair-result-status-retry { margin-top: 8px; padding: 4px 8px; }
|
|
1399
1405
|
.pair-result-status-retry:disabled { cursor: not-allowed; opacity: 0.55; }
|
|
1400
|
-
.pair-result-status-
|
|
1406
|
+
@media (max-width: 600px) { .pair-result-status { margin: 6px 8px 0; } .pair-result-status .pair-result-status-dismiss { min-width: 36px; height: 36px; } }
|
|
1407
|
+
|
|
1401
1408
|
|
|
1402
1409
|
/* --- Plan card --- */
|
|
1403
1410
|
.plan-card {
|
package/lib/public/index.html
CHANGED
|
@@ -635,6 +635,9 @@
|
|
|
635
635
|
</div>
|
|
636
636
|
</div>
|
|
637
637
|
</div>
|
|
638
|
+
<div id="generation-stop-row" class="hidden">
|
|
639
|
+
<button id="generation-stop" type="button" aria-label="Stop generating"><i data-lucide="square"></i><span>Stop</span></button>
|
|
640
|
+
</div>
|
|
638
641
|
</div>
|
|
639
642
|
</div>
|
|
640
643
|
<!-- Mobile input-more bottom sheet: attach / image + context sources -->
|
|
@@ -151,7 +151,19 @@ function suspendSocketWatchdogs() {
|
|
|
151
151
|
watchdog.suspend();
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
function refreshBackgroundPane() {
|
|
155
|
+
if (document.hidden || !store.get('paneNeedsSnapshot')) return false;
|
|
156
|
+
store.set({ paneNeedsSnapshot: false });
|
|
157
|
+
if (!store.get('paneMode') || !store.get('paneSessionId') || lifecycle.isOffline()) return false;
|
|
158
|
+
// A fresh socket discards browser-queued deltas and re-pins the exact pane.
|
|
159
|
+
// Server replay supplies current history, status and pending interactions.
|
|
160
|
+
watchdog.resume();
|
|
161
|
+
connect();
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
154
165
|
function resumeSocketWatchdogs(reason) {
|
|
166
|
+
if (refreshBackgroundPane()) return;
|
|
155
167
|
var resumed = watchdog.resume();
|
|
156
168
|
var socket = getWs();
|
|
157
169
|
if (socket && socket.readyState === 1 && resumed) startHeartbeat(socket, lifecycle.getEpoch());
|
|
@@ -178,10 +190,13 @@ export function initConnection() {
|
|
|
178
190
|
});
|
|
179
191
|
});
|
|
180
192
|
document.addEventListener("visibilitychange", function () {
|
|
181
|
-
if (document.hidden)
|
|
182
|
-
|
|
193
|
+
if (document.hidden) {
|
|
194
|
+
if (store.get('paneMode') && store.get('paneSessionId')) store.set({ paneNeedsSnapshot: true });
|
|
195
|
+
suspendSocketWatchdogs();
|
|
196
|
+
} else resumeSocketWatchdogs("visible");
|
|
183
197
|
});
|
|
184
198
|
document.addEventListener("freeze", function () {
|
|
199
|
+
if (store.get('paneMode') && store.get('paneSessionId')) store.set({ paneNeedsSnapshot: true });
|
|
185
200
|
suspendSocketWatchdogs();
|
|
186
201
|
});
|
|
187
202
|
document.addEventListener("resume", function () {
|
|
@@ -387,6 +402,12 @@ export function connect() {
|
|
|
387
402
|
|
|
388
403
|
newWs.onmessage = function (event) {
|
|
389
404
|
if (getWs() !== newWs || !lifecycle.current(attemptEpoch)) return;
|
|
405
|
+
// Hidden pane deltas are replaced by a current server snapshot on return.
|
|
406
|
+
if (store.get('paneMode') && store.get('paneSessionId') && document.hidden) {
|
|
407
|
+
store.set({ paneNeedsSnapshot: true });
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (refreshBackgroundPane()) return;
|
|
390
411
|
// Backup: if we're receiving messages, we're connected
|
|
391
412
|
if (!store.get('connected')) {
|
|
392
413
|
setStatus("connected");
|
|
@@ -83,6 +83,7 @@ export function drawFaviconAnimFrame() {
|
|
|
83
83
|
|
|
84
84
|
export function setSendBtnMode(mode) {
|
|
85
85
|
var sendBtn = getSendBtn();
|
|
86
|
+
sendBtn.setAttribute("aria-label", mode === "stop" ? "Stop generating" : "Send");
|
|
86
87
|
if (mode === "stop") {
|
|
87
88
|
sendBtn.disabled = false;
|
|
88
89
|
sendBtn.classList.add("stop");
|
|
@@ -66,7 +66,7 @@ import { renderGeneratedImage, renderImageGenerationProgress, clearImageGenerati
|
|
|
66
66
|
import { clearPairResultStatus, handlePairResultStatus } from './pair-result-status.js';
|
|
67
67
|
|
|
68
68
|
// --- App module imports ---
|
|
69
|
-
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
69
|
+
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, resetStreamBuffer, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
70
70
|
import { setActivity, startUrgentBlink, stopUrgentBlink, blinkSessionDot, updateCrossProjectBlink } from './app-favicon.js';
|
|
71
71
|
import { setStatus } from './app-connection.js';
|
|
72
72
|
import { resolvePaneSession, resolveSwitchedVendor } from './pane-session.js';
|
|
@@ -834,6 +834,7 @@ export function processMessage(msg) {
|
|
|
834
834
|
|
|
835
835
|
case "session_switched":
|
|
836
836
|
clearPairResultStatus();
|
|
837
|
+
resetStreamBuffer();
|
|
837
838
|
syncAutonomousRunForSession(msg.autonomousRun || null);
|
|
838
839
|
// Save draft from outgoing session
|
|
839
840
|
var _prevSid = store.get('activeSessionId');
|
|
@@ -45,6 +45,11 @@ export function addCopyHandler(msgEl, rawText) {
|
|
|
45
45
|
export function appendDelta(text) {
|
|
46
46
|
ensureAssistantBlock();
|
|
47
47
|
streamBuffer += text;
|
|
48
|
+
if (store.get('replayingHistory')) {
|
|
49
|
+
store.set({ currentFullText: store.get('currentFullText') + streamBuffer });
|
|
50
|
+
streamBuffer = "";
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
48
53
|
if (!streamDrainTimer) {
|
|
49
54
|
streamDrainTimer = requestAnimationFrame(drainStreamTick);
|
|
50
55
|
}
|
|
@@ -55,16 +60,9 @@ function drainStreamTick() {
|
|
|
55
60
|
var _s = store.snap();
|
|
56
61
|
if (!_s.currentMsgEl || streamBuffer.length === 0) return;
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
else if (len > 80) { n = 8; }
|
|
62
|
-
else if (len > 30) { n = 5; }
|
|
63
|
-
else if (len > 10) { n = 2; }
|
|
64
|
-
else { n = 1; }
|
|
65
|
-
|
|
66
|
-
var chunk = streamBuffer.slice(0, n);
|
|
67
|
-
streamBuffer = streamBuffer.slice(n);
|
|
63
|
+
// Render everything received by this frame; never replay a typing backlog.
|
|
64
|
+
var chunk = streamBuffer;
|
|
65
|
+
streamBuffer = "";
|
|
68
66
|
var newText = _s.currentFullText + chunk;
|
|
69
67
|
store.set({ currentFullText: newText });
|
|
70
68
|
|
|
@@ -82,6 +80,14 @@ function drainStreamTick() {
|
|
|
82
80
|
}
|
|
83
81
|
}
|
|
84
82
|
|
|
83
|
+
export function resetStreamBuffer() {
|
|
84
|
+
if (streamDrainTimer) cancelAnimationFrame(streamDrainTimer);
|
|
85
|
+
if (highlightTimer) clearTimeout(highlightTimer);
|
|
86
|
+
streamDrainTimer = null;
|
|
87
|
+
highlightTimer = null;
|
|
88
|
+
streamBuffer = "";
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
export function flushStreamBuffer() {
|
|
86
92
|
if (streamDrainTimer) { cancelAnimationFrame(streamDrainTimer); streamDrainTimer = null; }
|
|
87
93
|
if (streamBuffer.length > 0) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
|
|
4
|
+
// This control lives outside the composer so drafts and Worker locks cannot
|
|
5
|
+
// take away the human's ability to stop the displayed session.
|
|
6
|
+
export function initGenerationStop() {
|
|
7
|
+
var row = document.getElementById('generation-stop-row');
|
|
8
|
+
var button = document.getElementById('generation-stop');
|
|
9
|
+
if (!row || !button) return;
|
|
10
|
+
function sync() {
|
|
11
|
+
row.classList.toggle('hidden', !store.get('processing'));
|
|
12
|
+
button.disabled = !store.get('connected');
|
|
13
|
+
button.title = store.get('connected') ? 'Stop generating' : 'Reconnect to stop generating';
|
|
14
|
+
}
|
|
15
|
+
button.addEventListener('click', function () {
|
|
16
|
+
var ws = getWs();
|
|
17
|
+
if (!store.get('processing') || !store.get('connected') || !ws || ws.readyState !== 1) return;
|
|
18
|
+
ws.send(JSON.stringify({ type: 'stop' }));
|
|
19
|
+
});
|
|
20
|
+
store.subscribe(function (state, previous) {
|
|
21
|
+
if (state.processing !== previous.processing || state.connected !== previous.connected) sync();
|
|
22
|
+
});
|
|
23
|
+
sync();
|
|
24
|
+
}
|
|
@@ -33,6 +33,16 @@ function retry(item) {
|
|
|
33
33
|
sendRetry(source, false);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function dismiss(items) {
|
|
37
|
+
var ws = getWs();
|
|
38
|
+
if (!ws || ws.readyState !== 1) return;
|
|
39
|
+
items.forEach(function (item) {
|
|
40
|
+
ws.send(JSON.stringify({ type: "pair_result_dismiss", id: item.id, revision: item.revision,
|
|
41
|
+
sessionId: store.get("activeSessionId"),
|
|
42
|
+
projectSlug: store.get("currentSlug") || store.get("activeProjectSlug") || null }));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
function renderPairResultStatus() {
|
|
37
47
|
var existing = document.getElementById("pair-result-status");
|
|
38
48
|
if (existing) existing.remove();
|
|
@@ -44,6 +54,21 @@ function renderPairResultStatus() {
|
|
|
44
54
|
section.id = "pair-result-status";
|
|
45
55
|
section.className = "pair-result-status";
|
|
46
56
|
section.setAttribute("role", "status");
|
|
57
|
+
var header = document.createElement("div");
|
|
58
|
+
header.className = "pair-result-status-header";
|
|
59
|
+
var heading = document.createElement("span");
|
|
60
|
+
heading.textContent = items.length === 1 ? "Worker update needs attention" : items.length + " Worker updates need attention";
|
|
61
|
+
var close = document.createElement("button");
|
|
62
|
+
close.type = "button"; close.className = "pair-result-status-dismiss";
|
|
63
|
+
close.textContent = "×";
|
|
64
|
+
close.setAttribute("aria-label", "Dismiss Worker notices");
|
|
65
|
+
close.title = "Dismiss notices. Retained tasks and results are kept.";
|
|
66
|
+
close.addEventListener("click", dismiss.bind(null, items));
|
|
67
|
+
header.appendChild(heading); header.appendChild(close); section.appendChild(header);
|
|
68
|
+
var content = document.createElement("details"), toggle = document.createElement("summary");
|
|
69
|
+
content.className = "pair-result-status-details";
|
|
70
|
+
toggle.textContent = "Review updates";
|
|
71
|
+
content.appendChild(toggle); section.appendChild(content);
|
|
47
72
|
for (var i = 0; i < items.length; i++) {
|
|
48
73
|
var item = items[i], card = document.createElement("div");
|
|
49
74
|
card.className = "pair-result-status-card pair-result-status-" + item.state;
|
|
@@ -61,11 +86,11 @@ function renderPairResultStatus() {
|
|
|
61
86
|
}
|
|
62
87
|
if (item.canRetry) {
|
|
63
88
|
var button = document.createElement("button");
|
|
64
|
-
button.type = "button"; button.className = "
|
|
89
|
+
button.type = "button"; button.className = "pair-result-status-retry"; button.textContent = "Retry delivery";
|
|
65
90
|
button.addEventListener("click", retry.bind(null, item));
|
|
66
91
|
card.appendChild(button);
|
|
67
92
|
}
|
|
68
|
-
|
|
93
|
+
content.appendChild(card);
|
|
69
94
|
}
|
|
70
95
|
messages.parentElement.insertBefore(section, messages);
|
|
71
96
|
}
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const crypto = require("crypto");
|
|
2
|
+
var codexQuestionGuidance = require("./yoke/user-input-guidance").codexQuestionGuidance;
|
|
2
3
|
var fs = require("fs");
|
|
3
4
|
var path = require("path");
|
|
4
5
|
var execSync = require("child_process").execSync;
|
|
@@ -1054,6 +1055,10 @@ function createSDKBridge(opts) {
|
|
|
1054
1055
|
console.log("[sdk-bridge] processQueryStream: starting for-await loop, vendor=" + (session.vendor || adapter.vendor));
|
|
1055
1056
|
try {
|
|
1056
1057
|
for await (var msg of myQueryInstance) {
|
|
1058
|
+
// An old iterator may drain after Stop or after a replacement query starts.
|
|
1059
|
+
if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) continue;
|
|
1060
|
+
if ((session.taskStopRequested || (myAbortController && myAbortController.signal.aborted))
|
|
1061
|
+
&& msg && msg.yokeType !== "error" && msg.yokeType !== "interrupted") continue;
|
|
1057
1062
|
if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
|
|
1058
1063
|
console.log("[sdk-bridge] processQueryStream: received event yokeType=" + msg.yokeType);
|
|
1059
1064
|
}
|
|
@@ -1111,6 +1116,7 @@ function createSDKBridge(opts) {
|
|
|
1111
1116
|
&& session.cliSessionId === msg.sessionId
|
|
1112
1117
|
&& (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
|
|
1113
1118
|
if (codexCatalogCandidate.version) session.codexDynamicToolCatalogVersion = codexCatalogCandidate.version;
|
|
1119
|
+
if (codexCatalogCandidate.userInputVersion) session.codexUserInputToolCatalogVersion = codexCatalogCandidate.userInputVersion;
|
|
1114
1120
|
if (codexCatalogCandidate.issuesVersion) session.codexIssuesToolCatalogVersion = codexCatalogCandidate.issuesVersion;
|
|
1115
1121
|
sm.saveSessionFile(session);
|
|
1116
1122
|
codexCatalogCandidate = null;
|
|
@@ -1804,8 +1810,10 @@ function createSDKBridge(opts) {
|
|
|
1804
1810
|
var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
|
|
1805
1811
|
var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
|
|
1806
1812
|
var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
|
|
1807
|
-
if (structuredInputSession) requestedUserInputMode = "fallback";
|
|
1813
|
+
if (structuredInputSession || sessionAdapter.vendor === "codex") requestedUserInputMode = "fallback";
|
|
1808
1814
|
var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
|
|
1815
|
+
var recoverCodexQuestionTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
|
|
1816
|
+
&& Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
|
|
1809
1817
|
function structuredInputContextAvailable() {
|
|
1810
1818
|
if (!queryContextAvailable()) return false;
|
|
1811
1819
|
// A schedule-triggered fallback shares the schedule feature's live
|
|
@@ -1928,7 +1936,10 @@ function createSDKBridge(opts) {
|
|
|
1928
1936
|
// would strip the driver session down to the two-sentence pair preset.
|
|
1929
1937
|
// Adapters map this to their additive mechanism (claude: preset+append,
|
|
1930
1938
|
// codex/kiro: prepend to the first message).
|
|
1931
|
-
appendSystemPrompt:
|
|
1939
|
+
appendSystemPrompt: [
|
|
1940
|
+
typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
|
|
1941
|
+
sessionAdapter.vendor === "codex" && queryContextAvailable() ? codexQuestionGuidance() : "",
|
|
1942
|
+
].filter(Boolean).join("\n\n") || undefined,
|
|
1932
1943
|
model: queryModel,
|
|
1933
1944
|
// Effort is remembered per vendor and clamped to the session vendor's
|
|
1934
1945
|
// supported levels: a global value would leak codex "minimal" into
|
|
@@ -1946,7 +1957,7 @@ function createSDKBridge(opts) {
|
|
|
1946
1957
|
dynamicTools: sessionToolDefs,
|
|
1947
1958
|
// Codex persists dynamic tools with a thread. A stale authorized Driver
|
|
1948
1959
|
// gets this exact-query bridge; its handlers still reauthorize live.
|
|
1949
|
-
sessionMcpServer: recoverCodexIssueTools ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
|
|
1960
|
+
sessionMcpServer: (recoverCodexIssueTools || recoverCodexQuestionTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
|
|
1950
1961
|
callDynamicTool: function (toolName, args) {
|
|
1951
1962
|
var toolDef = findSessionToolDef(toolName);
|
|
1952
1963
|
if (toolDef && typeof toolDef.handler === "function") {
|
|
@@ -2159,6 +2170,9 @@ function createSDKBridge(opts) {
|
|
|
2159
2170
|
if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
|
|
2160
2171
|
codexCatalogCandidate = { generation: queryGeneration, version: yoke.sessionTools.CODEX_CATALOG_VERSION };
|
|
2161
2172
|
}
|
|
2173
|
+
if (requiredCatalogTools.ask_user_questions) {
|
|
2174
|
+
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, userInputVersion: 1 });
|
|
2175
|
+
}
|
|
2162
2176
|
if (hasIssueCatalog) {
|
|
2163
2177
|
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
|
|
2164
2178
|
generation: queryGeneration,
|
package/lib/sessions.js
CHANGED
|
@@ -180,6 +180,7 @@ function createSessionManager(opts) {
|
|
|
180
180
|
if (session.ownerId) metaObj.ownerId = session.ownerId;
|
|
181
181
|
if (session.vendor) metaObj.vendor = session.vendor;
|
|
182
182
|
if (session.codexDynamicToolCatalogVersion) metaObj.codexDynamicToolCatalogVersion = session.codexDynamicToolCatalogVersion;
|
|
183
|
+
if (session.codexUserInputToolCatalogVersion) metaObj.codexUserInputToolCatalogVersion = session.codexUserInputToolCatalogVersion;
|
|
183
184
|
if (session.codexIssuesToolCatalogVersion) metaObj.codexIssuesToolCatalogVersion = session.codexIssuesToolCatalogVersion;
|
|
184
185
|
if (session.model) metaObj.model = session.model;
|
|
185
186
|
if (session.effort) metaObj.effort = session.effort;
|
|
@@ -354,6 +355,7 @@ function createSessionManager(opts) {
|
|
|
354
355
|
};
|
|
355
356
|
if (m.vendor) session.vendor = m.vendor;
|
|
356
357
|
session.codexDynamicToolCatalogVersion = Number(m.codexDynamicToolCatalogVersion || 0);
|
|
358
|
+
session.codexUserInputToolCatalogVersion = Number(m.codexUserInputToolCatalogVersion || 0);
|
|
357
359
|
session.codexIssuesToolCatalogVersion = Number(m.codexIssuesToolCatalogVersion || 0);
|
|
358
360
|
session.model = m.model || deriveSessionModel(loaded[i].history);
|
|
359
361
|
session.effort = m.effort || null;
|
|
@@ -188,33 +188,41 @@ AcpProcessManager.prototype.addRequestHandler = function(method, fn) {
|
|
|
188
188
|
this.requestHandlers[method] = fn;
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
-
AcpProcessManager.prototype.send = function(method, params, timeoutMs) {
|
|
191
|
+
AcpProcessManager.prototype.send = function(method, params, timeoutMs, signal) {
|
|
192
192
|
var self = this;
|
|
193
193
|
var id = this.nextId++;
|
|
194
194
|
timeoutMs = timeoutMs || 30000;
|
|
195
|
-
|
|
196
195
|
return new Promise(function(resolve, reject) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
var timer = setTimeout(function() {
|
|
196
|
+
var timer;
|
|
197
|
+
function cleanup() {
|
|
198
|
+
if (timer) clearTimeout(timer);
|
|
202
199
|
delete self.pendingRequests[id];
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
if (signal) signal.removeEventListener("abort", cancel);
|
|
201
|
+
}
|
|
202
|
+
function fail(error) { cleanup(); reject(error); }
|
|
203
|
+
function cancel() {
|
|
204
|
+
var error = new Error("ACP request cancelled: " + method);
|
|
205
|
+
error.name = "AbortError";
|
|
206
|
+
fail(error);
|
|
207
|
+
}
|
|
208
|
+
if (signal && signal.aborted) { cancel(); return; }
|
|
209
|
+
if (!self.proc || !self.started) { fail(new Error("ACP server not started")); return; }
|
|
210
|
+
timer = setTimeout(function() { fail(new Error("Request timeout: " + method + " (id=" + id + ")")); }, timeoutMs);
|
|
211
|
+
self.pendingRequests[id] = {
|
|
212
|
+
resolve: function (value) { cleanup(); resolve(value); }, reject: fail, timer: timer,
|
|
213
|
+
};
|
|
214
|
+
if (signal) signal.addEventListener("abort", cancel, { once: true });
|
|
207
215
|
var msg = { jsonrpc: "2.0", id: id, method: method };
|
|
208
216
|
if (params !== undefined) msg.params = params;
|
|
209
|
-
self._write(msg);
|
|
217
|
+
if (self._write(msg) === false) fail(new Error("ACP transport is not writable"));
|
|
210
218
|
});
|
|
211
219
|
};
|
|
212
220
|
|
|
213
221
|
AcpProcessManager.prototype.notify = function(method, params) {
|
|
214
|
-
if (!this.proc || !this.started) return;
|
|
222
|
+
if (!this.proc || !this.started) return false;
|
|
215
223
|
var msg = { jsonrpc: "2.0", method: method };
|
|
216
224
|
if (params !== undefined) msg.params = params;
|
|
217
|
-
this._write(msg);
|
|
225
|
+
return this._write(msg);
|
|
218
226
|
};
|
|
219
227
|
|
|
220
228
|
AcpProcessManager.prototype.respond = function(id, result) {
|
|
@@ -228,11 +236,13 @@ AcpProcessManager.prototype.respondError = function(id, code, message) {
|
|
|
228
236
|
};
|
|
229
237
|
|
|
230
238
|
AcpProcessManager.prototype._write = function(msg) {
|
|
231
|
-
if (!this.proc || !this.proc.stdin || this.proc.stdin.destroyed) return;
|
|
239
|
+
if (!this.proc || !this.proc.stdin || this.proc.stdin.destroyed) return false;
|
|
232
240
|
try {
|
|
233
241
|
this.proc.stdin.write(JSON.stringify(msg) + "\n");
|
|
242
|
+
return true;
|
|
234
243
|
} catch (e) {
|
|
235
244
|
this._error("Write error:", e.message);
|
|
245
|
+
return false;
|
|
236
246
|
}
|
|
237
247
|
};
|
|
238
248
|
|