clay-server 3.4.0-beta.14 → 3.4.0-beta.16
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-connection.js +1 -0
- package/lib/public/app.js +3 -0
- package/lib/public/css/input.css +44 -0
- package/lib/public/modules/app-messages.js +4 -0
- package/lib/public/modules/background-tasks-ui.js +55 -0
- package/lib/public/modules/sidebar-sessions.js +14 -0
- package/lib/sdk-bridge.js +10 -0
- package/lib/sdk-message-processor.js +15 -0
- package/lib/sessions.js +2 -0
- package/lib/ws-schema.js +1 -0
- package/lib/yoke/adapters/claude.js +34 -0
- package/lib/yoke/interface.js +12 -0
- package/package.json +1 -1
|
@@ -282,6 +282,7 @@ function attachConnection(ctx) {
|
|
|
282
282
|
if (active.isProcessing) {
|
|
283
283
|
sendTo(ws, { type: "status", status: "processing" });
|
|
284
284
|
}
|
|
285
|
+
sendTo(ws, { type: "active_background_tasks", tasks: active.activeBackgroundTasks || [] });
|
|
285
286
|
var pendingIds = Object.keys(active.pendingPermissions);
|
|
286
287
|
for (var pi = 0; pi < pendingIds.length; pi++) {
|
|
287
288
|
var p = active.pendingPermissions[pendingIds[pi]];
|
package/lib/public/app.js
CHANGED
|
@@ -65,6 +65,7 @@ import { initMisc, flushPendingExtMessages, showImageModal as _miscShowImageModa
|
|
|
65
65
|
import { initSkillInstall, requireSkills as _siRequireSkills, requireClayMateInterview as _siRequireClayMateInterview, handleSkillInstallWs as _siHandleSkillInstallWs } from './modules/app-skills-install.js';
|
|
66
66
|
import { initDebateUi, showDebateConcludeConfirm as _debShowDebateConcludeConfirm, exitDebateConcludeMode as _debExitDebateConcludeMode, handleDebateConcludeSend as _debHandleDebateConcludeSend, showDebateEndedMode as _debShowDebateEndedMode, exitDebateEndedMode as _debExitDebateEndedMode, showDebateUserFloor as _debShowDebateUserFloor, exitDebateFloorMode as _debExitDebateFloorMode, handleDebateFloorSend as _debHandleDebateFloorSend, renderDebateUserFloorDone as _debRenderDebateUserFloorDone, showDebateSticky as _debShowDebateSticky, showDebateBottomBar as _debShowDebateBottomBar, removeDebateBottomBar as _debRemoveDebateBottomBar, sendDebateStickyComment as _debSendDebateStickyComment, updateDebateRound as _debUpdateDebateRound } from './modules/app-debate-ui.js';
|
|
67
67
|
import { initLoopUi, updateLoopInputVisibility as _loopUpdateLoopInputVisibility, updateLoopButton as _loopUpdateLoopButton, showLoopBanner as _loopShowLoopBanner, updateLoopBanner as _loopUpdateLoopBanner, updateRalphBars as _loopUpdateRalphBars, showRalphCraftingBar as _loopShowRalphCraftingBar, showRalphApprovalBar as _loopShowRalphApprovalBar, updateRalphApprovalStatus as _loopUpdateRalphApprovalStatus, openRalphPreviewModal as _loopOpenRalphPreviewModal, showExecModal as _loopShowExecModal, closeExecModal as _loopCloseExecModal, updateExecModalStatus as _loopUpdateExecModalStatus } from './modules/app-loop-ui.js';
|
|
68
|
+
import { initBackgroundTasksUi } from './modules/background-tasks-ui.js';
|
|
68
69
|
import { initLoopWizard, openRalphWizard as _loopOpenRalphWizard, closeRalphWizard as _loopCloseRalphWizard, getWizardSource as _loopGetWizardSource } from './modules/app-loop-wizard.js';
|
|
69
70
|
import { initAppNotifications, handleNotificationsState as _notifHandleState, handleNotificationCreated as _notifHandleCreated, handleNotificationDismissed as _notifHandleDismissed, handleNotificationDismissedAll as _notifHandleDismissedAll } from './modules/app-notifications.js';
|
|
70
71
|
import { initWhatsNew, handleWhatsNewState as _wnHandleState, handleWhatsNewSeenResult as _wnHandleSeenResult } from './modules/whats-new.js';
|
|
@@ -296,6 +297,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
296
297
|
slashCommands: [],
|
|
297
298
|
processing: false,
|
|
298
299
|
activeSessionId: null,
|
|
300
|
+
activeBackgroundTasks: [],
|
|
299
301
|
sessionVendorBound: false,
|
|
300
302
|
cliSessionId: null,
|
|
301
303
|
isHeadlessMode: false,
|
|
@@ -649,6 +651,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
649
651
|
initDebateUi();
|
|
650
652
|
|
|
651
653
|
initLoopUi();
|
|
654
|
+
initBackgroundTasksUi();
|
|
652
655
|
initLoopWizard();
|
|
653
656
|
|
|
654
657
|
// --- Notifications module ---
|
package/lib/public/css/input.css
CHANGED
|
@@ -667,6 +667,50 @@
|
|
|
667
667
|
position: relative;
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
+
.background-tasks-bar {
|
|
671
|
+
max-width: var(--content-width);
|
|
672
|
+
margin: 0 auto 8px;
|
|
673
|
+
border: 1px solid var(--border);
|
|
674
|
+
border-radius: 8px;
|
|
675
|
+
background: var(--input-bg);
|
|
676
|
+
overflow: hidden;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.background-tasks-toggle {
|
|
680
|
+
width: 100%;
|
|
681
|
+
display: flex;
|
|
682
|
+
align-items: center;
|
|
683
|
+
gap: 7px;
|
|
684
|
+
padding: 8px 10px;
|
|
685
|
+
border: 0;
|
|
686
|
+
background: transparent;
|
|
687
|
+
color: var(--text-secondary);
|
|
688
|
+
cursor: pointer;
|
|
689
|
+
font: inherit;
|
|
690
|
+
text-align: left;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.background-tasks-chip, .session-background-task-count {
|
|
694
|
+
display: inline-flex;
|
|
695
|
+
align-items: center;
|
|
696
|
+
justify-content: center;
|
|
697
|
+
min-width: 16px;
|
|
698
|
+
height: 16px;
|
|
699
|
+
padding: 0 4px;
|
|
700
|
+
border-radius: 8px;
|
|
701
|
+
background: var(--accent);
|
|
702
|
+
color: var(--bg);
|
|
703
|
+
font-size: 10px;
|
|
704
|
+
font-weight: 700;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.background-tasks-list { padding: 0 10px 8px; }
|
|
708
|
+
.background-task-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--border-subtle); }
|
|
709
|
+
.background-task-description { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
710
|
+
.background-task-type { padding: 2px 6px; border-radius: 5px; background: var(--code-bg); color: var(--text-muted); font-size: 11px; }
|
|
711
|
+
.background-task-stop { border: 1px solid var(--border); border-radius: 5px; padding: 3px 7px; background: transparent; color: var(--text-secondary); cursor: pointer; font: inherit; font-size: 12px; }
|
|
712
|
+
.background-task-stop:hover { color: var(--text); border-color: var(--text-muted); }
|
|
713
|
+
|
|
670
714
|
#input-row {
|
|
671
715
|
display: flex;
|
|
672
716
|
flex-direction: column;
|
|
@@ -1128,6 +1128,10 @@ export function processMessage(msg) {
|
|
|
1128
1128
|
updateSubagentTaskStatus(msg.parentToolId, msg.patch);
|
|
1129
1129
|
break;
|
|
1130
1130
|
|
|
1131
|
+
case "active_background_tasks":
|
|
1132
|
+
store.set({ activeBackgroundTasks: Array.isArray(msg.tasks) ? msg.tasks : [] });
|
|
1133
|
+
break;
|
|
1134
|
+
|
|
1131
1135
|
case "result":
|
|
1132
1136
|
// Result marks turn end. Drop pre-thinking even if no delta/tool
|
|
1133
1137
|
// event ever arrived (e.g. tool-only turn whose progress signals
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { escapeHtml } from './utils.js';
|
|
4
|
+
|
|
5
|
+
var expanded = false;
|
|
6
|
+
|
|
7
|
+
export function initBackgroundTasksUi() {
|
|
8
|
+
store.subscribe(function(state, prev) {
|
|
9
|
+
if (state.activeBackgroundTasks !== prev.activeBackgroundTasks) renderBackgroundTasks();
|
|
10
|
+
});
|
|
11
|
+
renderBackgroundTasks();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function renderBackgroundTasks() {
|
|
15
|
+
var tasks = store.get('activeBackgroundTasks') || [];
|
|
16
|
+
var existing = document.getElementById('background-tasks-bar');
|
|
17
|
+
if (tasks.length === 0) {
|
|
18
|
+
if (existing) existing.remove();
|
|
19
|
+
expanded = false;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
var inputArea = document.getElementById('input-area');
|
|
23
|
+
if (!inputArea || !inputArea.parentNode) return;
|
|
24
|
+
var bar = existing || document.createElement('div');
|
|
25
|
+
bar.id = 'background-tasks-bar';
|
|
26
|
+
bar.className = 'background-tasks-bar';
|
|
27
|
+
var taskLabel = tasks.length === 1 ? 'background task' : 'background tasks';
|
|
28
|
+
var html = '<button type="button" class="background-tasks-toggle" aria-expanded="' + expanded + '">' +
|
|
29
|
+
'<span aria-hidden="true">⏳</span><span class="background-tasks-chip">' + tasks.length + '</span>' +
|
|
30
|
+
'<span>' + tasks.length + ' ' + taskLabel + '</span></button>';
|
|
31
|
+
if (expanded) {
|
|
32
|
+
html += '<div class="background-tasks-list">';
|
|
33
|
+
for (var i = 0; i < tasks.length; i++) {
|
|
34
|
+
var task = tasks[i];
|
|
35
|
+
html += '<div class="background-task-row"><span class="background-task-description" title="' + escapeHtml(task.description || '') + '">' +
|
|
36
|
+
escapeHtml(task.description || 'Background task') + '</span><span class="background-task-type">' +
|
|
37
|
+
escapeHtml(task.task_type || 'other') + '</span><button type="button" class="background-task-stop" data-task-id="' +
|
|
38
|
+
escapeHtml(task.task_id || '') + '">Stop</button></div>';
|
|
39
|
+
}
|
|
40
|
+
html += '</div>';
|
|
41
|
+
}
|
|
42
|
+
bar.innerHTML = html;
|
|
43
|
+
if (!existing) inputArea.parentNode.insertBefore(bar, inputArea);
|
|
44
|
+
bar.querySelector('.background-tasks-toggle').addEventListener('click', function() {
|
|
45
|
+
expanded = !expanded;
|
|
46
|
+
renderBackgroundTasks();
|
|
47
|
+
});
|
|
48
|
+
var stopButtons = bar.querySelectorAll('.background-task-stop');
|
|
49
|
+
for (var j = 0; j < stopButtons.length; j++) {
|
|
50
|
+
stopButtons[j].addEventListener('click', function() {
|
|
51
|
+
var ws = getWs();
|
|
52
|
+
if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: 'stop_task', taskId: this.dataset.taskId }));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -977,6 +977,9 @@ function renderLoopChild(s) {
|
|
|
977
977
|
if (s.isProcessing) {
|
|
978
978
|
textHtml += '<span class="session-processing"></span>';
|
|
979
979
|
}
|
|
980
|
+
if (s.backgroundTaskCount) {
|
|
981
|
+
textHtml += '<span class="session-background-task-count">' + s.backgroundTaskCount + '</span>';
|
|
982
|
+
}
|
|
980
983
|
if (s.loop) {
|
|
981
984
|
var isRalphChild = s.loop.source === "ralph";
|
|
982
985
|
var roleName = s.loop.role === "crafting" ? "Crafting" : s.loop.role === "judge" ? "Judge" : (isRalphChild ? "Coder" : "Run");
|
|
@@ -1096,6 +1099,9 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
1096
1099
|
if (anyProcessing) {
|
|
1097
1100
|
textHtml += '<span class="session-processing"></span>';
|
|
1098
1101
|
}
|
|
1102
|
+
var groupBackgroundTaskCount = 0;
|
|
1103
|
+
for (var bi = 0; bi < children.length; bi++) groupBackgroundTaskCount += children[bi].backgroundTaskCount || 0;
|
|
1104
|
+
if (groupBackgroundTaskCount) textHtml += '<span class="session-background-task-count">' + groupBackgroundTaskCount + '</span>';
|
|
1099
1105
|
var groupIcon = isDebate ? "mic" : (isRalph ? "repeat" : "calendar-clock");
|
|
1100
1106
|
var iconClass = isDebate ? " debate" : (isRalph ? "" : " scheduled");
|
|
1101
1107
|
textHtml += '<span class="session-loop-icon' + iconClass + '">' + iconHtml(groupIcon) + '</span>';
|
|
@@ -1205,6 +1211,9 @@ function renderLoopRun(parentGk, startedAtKey, sessions, isRalph) {
|
|
|
1205
1211
|
if (anyProcessing) {
|
|
1206
1212
|
textHtml += '<span class="session-processing"></span>';
|
|
1207
1213
|
}
|
|
1214
|
+
var runBackgroundTaskCount = 0;
|
|
1215
|
+
for (var bi = 0; bi < sessions.length; bi++) runBackgroundTaskCount += sessions[bi].backgroundTaskCount || 0;
|
|
1216
|
+
if (runBackgroundTaskCount) textHtml += '<span class="session-background-task-count">' + runBackgroundTaskCount + '</span>';
|
|
1208
1217
|
textHtml += '<span class="session-loop-run-time">' + escapeHtml(timeLabel) + '</span>';
|
|
1209
1218
|
textHtml += '<span class="session-loop-count' + (isRalph ? "" : " scheduled") + '">' + sessions.length + '</span>';
|
|
1210
1219
|
textSpan.innerHTML = textHtml;
|
|
@@ -1254,6 +1263,9 @@ function renderSessionItem(s) {
|
|
|
1254
1263
|
if (s.isProcessing) {
|
|
1255
1264
|
textHtml += '<span class="session-processing"></span>';
|
|
1256
1265
|
}
|
|
1266
|
+
if (s.backgroundTaskCount) {
|
|
1267
|
+
textHtml += '<span class="session-background-task-count">' + s.backgroundTaskCount + '</span>';
|
|
1268
|
+
}
|
|
1257
1269
|
if (s.loop && s.loop.source === "debate") {
|
|
1258
1270
|
textHtml += '<span class="session-debate-icon" title="Debate">' + iconHtml("mic") + '</span>';
|
|
1259
1271
|
}
|
|
@@ -1322,6 +1334,8 @@ function renderSplitGroupItem(group, members) {
|
|
|
1322
1334
|
}
|
|
1323
1335
|
html += "</span>";
|
|
1324
1336
|
if (members[0].isProcessing || members[1].isProcessing) html += '<span class="session-processing"></span>';
|
|
1337
|
+
var splitBackgroundTaskCount = (members[0].backgroundTaskCount || 0) + (members[1].backgroundTaskCount || 0);
|
|
1338
|
+
if (splitBackgroundTaskCount) html += '<span class="session-background-task-count">' + splitBackgroundTaskCount + '</span>';
|
|
1325
1339
|
html += '<span class="split-group-name">' + highlightMatch(group.name || "Split", searchQuery) + "</span>";
|
|
1326
1340
|
text.innerHTML = html;
|
|
1327
1341
|
el.appendChild(text);
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -878,6 +878,8 @@ function createSDKBridge(opts) {
|
|
|
878
878
|
var stillOwnsRuntime = session.queryInstance === myQueryInstance;
|
|
879
879
|
if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
|
|
880
880
|
session.isProcessing = false;
|
|
881
|
+
session._awaitingTurnResult = false;
|
|
882
|
+
session._queuedTurnCount = 0;
|
|
881
883
|
onProcessingChanged();
|
|
882
884
|
send({ type: "status", processing: false });
|
|
883
885
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
@@ -892,6 +894,8 @@ function createSDKBridge(opts) {
|
|
|
892
894
|
// iterator without a result event. Treat any result-less stream end as
|
|
893
895
|
// a failed turn so the UI cannot remain on processing forever.
|
|
894
896
|
session.isProcessing = false;
|
|
897
|
+
session._awaitingTurnResult = false;
|
|
898
|
+
session._queuedTurnCount = 0;
|
|
895
899
|
onProcessingChanged();
|
|
896
900
|
if (!session._lastAdapterError) {
|
|
897
901
|
sendAndRecord(session, {
|
|
@@ -906,6 +910,8 @@ function createSDKBridge(opts) {
|
|
|
906
910
|
} catch (err) {
|
|
907
911
|
if (session.isProcessing && session.queryInstance === myQueryInstance) {
|
|
908
912
|
session.isProcessing = false;
|
|
913
|
+
session._awaitingTurnResult = false;
|
|
914
|
+
session._queuedTurnCount = 0;
|
|
909
915
|
onProcessingChanged();
|
|
910
916
|
if (err.name === "AbortError" || (myAbortController && myAbortController.signal.aborted) || session.taskStopRequested) {
|
|
911
917
|
if (!session.destroying) {
|
|
@@ -1584,6 +1590,8 @@ function createSDKBridge(opts) {
|
|
|
1584
1590
|
console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
|
|
1585
1591
|
console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
|
|
1586
1592
|
session.isProcessing = false;
|
|
1593
|
+
session._awaitingTurnResult = false;
|
|
1594
|
+
session._queuedTurnCount = 0;
|
|
1587
1595
|
onProcessingChanged();
|
|
1588
1596
|
session.queryInstance = null;
|
|
1589
1597
|
session.messageQueue = null;
|
|
@@ -1625,6 +1633,8 @@ function createSDKBridge(opts) {
|
|
|
1625
1633
|
session.messageQueue = null;
|
|
1626
1634
|
session.abortController = null;
|
|
1627
1635
|
session.isProcessing = false;
|
|
1636
|
+
session._awaitingTurnResult = false;
|
|
1637
|
+
session._queuedTurnCount = 0;
|
|
1628
1638
|
onProcessingChanged();
|
|
1629
1639
|
sendAndRecord(session, { type: "error", text: "The agent connection closed before it received your message. Please send it again." });
|
|
1630
1640
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
@@ -175,6 +175,10 @@ function attachMessageProcessor(ctx) {
|
|
|
175
175
|
|
|
176
176
|
// Cache slash_commands and model from CLI init message
|
|
177
177
|
if (parsed.yokeType === "init") {
|
|
178
|
+
var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
|
|
179
|
+
session.activeBackgroundTasks = [];
|
|
180
|
+
sendToSession(session, { type: "active_background_tasks", tasks: [] });
|
|
181
|
+
if (previousBackgroundTaskCount !== 0) sm.broadcastSessionList();
|
|
178
182
|
var fsSkills = discoverSkillDirs();
|
|
179
183
|
sm.skillNames = mergeSkills(parsed.skills, fsSkills);
|
|
180
184
|
if (parsed.slashCommands) {
|
|
@@ -612,6 +616,15 @@ function attachMessageProcessor(ctx) {
|
|
|
612
616
|
});
|
|
613
617
|
}
|
|
614
618
|
|
|
619
|
+
} else if (parsed.yokeType === "background_tasks_changed") {
|
|
620
|
+
var previousBackgroundTaskCount = (session.activeBackgroundTasks || []).length;
|
|
621
|
+
var activeBackgroundTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
622
|
+
session.activeBackgroundTasks = activeBackgroundTasks;
|
|
623
|
+
sendToSession(session, { type: "active_background_tasks", tasks: activeBackgroundTasks });
|
|
624
|
+
if (previousBackgroundTaskCount !== activeBackgroundTasks.length) {
|
|
625
|
+
sm.broadcastSessionList();
|
|
626
|
+
}
|
|
627
|
+
|
|
615
628
|
} else if (parsed.yokeType === "tool_progress") {
|
|
616
629
|
// Sub-agent tool_progress: forward as activity update
|
|
617
630
|
var parentId = parsed.parentToolId;
|
|
@@ -783,6 +796,8 @@ function attachMessageProcessor(ctx) {
|
|
|
783
796
|
// app-server returned an unauthorized/token-revoked error). Trigger the
|
|
784
797
|
// same login flow as the Claude login-prompt path.
|
|
785
798
|
session.isProcessing = false;
|
|
799
|
+
session._awaitingTurnResult = false;
|
|
800
|
+
session._queuedTurnCount = 0;
|
|
786
801
|
onProcessingChanged();
|
|
787
802
|
emitAuthRequired(session);
|
|
788
803
|
|
package/lib/sessions.js
CHANGED
|
@@ -495,6 +495,7 @@ function createSessionManager(opts) {
|
|
|
495
495
|
title: s.title || "New Session",
|
|
496
496
|
active: isActive,
|
|
497
497
|
isProcessing: s.isProcessing,
|
|
498
|
+
backgroundTaskCount: (s.activeBackgroundTasks || []).length,
|
|
498
499
|
lastActivity: s.lastActivity || s.createdAt || 0,
|
|
499
500
|
loop: loop,
|
|
500
501
|
spawn: s.spawn || null,
|
|
@@ -717,6 +718,7 @@ function createSessionManager(opts) {
|
|
|
717
718
|
if (session.isProcessing) {
|
|
718
719
|
_send({ type: "status", status: "processing" });
|
|
719
720
|
}
|
|
721
|
+
_send({ type: "active_background_tasks", tasks: session.activeBackgroundTasks || [] });
|
|
720
722
|
|
|
721
723
|
// Re-send any pending permission requests
|
|
722
724
|
var pendingIds = Object.keys(session.pendingPermissions);
|
package/lib/ws-schema.js
CHANGED
|
@@ -114,6 +114,7 @@ var schema = {
|
|
|
114
114
|
"subagent_done": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Subagent task completed" },
|
|
115
115
|
"task_started": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Background task started" },
|
|
116
116
|
"task_progress": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Background task progress update" },
|
|
117
|
+
"active_background_tasks": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Current background tasks for the active session (full replacement)" },
|
|
117
118
|
"markdown_edit_present": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Open a user-requested Markdown edit with a before snapshot" },
|
|
118
119
|
|
|
119
120
|
// -----------------------------------------------------------------------
|
|
@@ -197,6 +197,26 @@ function flattenEvent(raw) {
|
|
|
197
197
|
base.summary = raw.summary || null;
|
|
198
198
|
return base;
|
|
199
199
|
}
|
|
200
|
+
if (raw.subtype === "background_tasks_changed") {
|
|
201
|
+
base.yokeType = "background_tasks_changed";
|
|
202
|
+
base.tasks = normalizeBackgroundTasks(raw.tasks);
|
|
203
|
+
return base;
|
|
204
|
+
}
|
|
205
|
+
if (raw.subtype === "task_updated") {
|
|
206
|
+
base.yokeType = "task_updated";
|
|
207
|
+
base.task_id = raw.task_id;
|
|
208
|
+
base.patch = raw.patch || {};
|
|
209
|
+
return base;
|
|
210
|
+
}
|
|
211
|
+
if (raw.subtype === "task_notification") {
|
|
212
|
+
base.yokeType = "task_notification";
|
|
213
|
+
base.parentToolId = raw.parent_tool_use_id;
|
|
214
|
+
base.taskId = raw.task_id;
|
|
215
|
+
base.status = raw.status || "completed";
|
|
216
|
+
base.summary = raw.summary || "";
|
|
217
|
+
base.usage = raw.usage || null;
|
|
218
|
+
return base;
|
|
219
|
+
}
|
|
200
220
|
// Model refusal: the model declined and the CLI either fell back to
|
|
201
221
|
// another model or ended the turn. Translate to a vendor-neutral
|
|
202
222
|
// yokeType so the relay never sees SDK subtype strings.
|
|
@@ -343,6 +363,20 @@ function flattenEvent(raw) {
|
|
|
343
363
|
return base;
|
|
344
364
|
}
|
|
345
365
|
|
|
366
|
+
function normalizeBackgroundTasks(tasks) {
|
|
367
|
+
if (!Array.isArray(tasks)) return [];
|
|
368
|
+
return tasks.map(function(task) {
|
|
369
|
+
var nativeType = task && task.task_type;
|
|
370
|
+
var taskType = nativeType === "local_bash" ? "shell"
|
|
371
|
+
: nativeType === "local_agent" ? "agent" : "other";
|
|
372
|
+
return {
|
|
373
|
+
task_id: task && task.task_id,
|
|
374
|
+
task_type: taskType,
|
|
375
|
+
description: (task && task.description) || "",
|
|
376
|
+
};
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
346
380
|
// --- QueryHandle ---
|
|
347
381
|
// Wraps a raw SDK query object with the YOKE QueryHandle interface.
|
|
348
382
|
// Events are flattened via flattenEvent before yielding.
|
package/lib/yoke/interface.js
CHANGED
|
@@ -9,6 +9,17 @@
|
|
|
9
9
|
// 2. QueryHandle (returned by adapter.createQuery)
|
|
10
10
|
|
|
11
11
|
var TOOL_POLICIES = ["ask", "allow-all"];
|
|
12
|
+
var BACKGROUND_TASK_TYPES = ["shell", "agent", "monitor", "other"];
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Optional adapter event contract:
|
|
16
|
+
* { yokeType: "background_tasks_changed", tasks: [{ task_id, task_type, description }] }
|
|
17
|
+
*
|
|
18
|
+
* Each event replaces the complete live set. An adapter that emits this event
|
|
19
|
+
* must reset it to an empty set when its CLI process restarts. task_type uses
|
|
20
|
+
* BACKGROUND_TASK_TYPES; adapters without a level signal may synthesize one,
|
|
21
|
+
* and adapters that emit nothing simply have no background-task indicator.
|
|
22
|
+
*/
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* Validate that an adapter object implements all required methods.
|
|
@@ -93,6 +104,7 @@ function validateQueryHandle(handle) {
|
|
|
93
104
|
|
|
94
105
|
module.exports = {
|
|
95
106
|
TOOL_POLICIES: TOOL_POLICIES,
|
|
107
|
+
BACKGROUND_TASK_TYPES: BACKGROUND_TASK_TYPES,
|
|
96
108
|
ADAPTER_METHODS: ADAPTER_METHODS,
|
|
97
109
|
QUERY_HANDLE_METHODS: QUERY_HANDLE_METHODS,
|
|
98
110
|
validateAdapter: validateAdapter,
|
package/package.json
CHANGED