clay-server 4.1.0-beta.8 → 4.1.0
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/context-usage.js +47 -0
- package/lib/issues-mcp-server.js +1 -1
- package/lib/issues-service.js +3 -1
- package/lib/loop-registry-occurrence.js +1 -0
- package/lib/notes.js +20 -3
- package/lib/os-users.js +6 -0
- package/lib/project-connection.js +14 -3
- package/lib/project-file-path.js +13 -0
- package/lib/project-file-watch.js +19 -8
- package/lib/project-filesystem.js +10 -16
- package/lib/project-http.js +53 -48
- package/lib/project-logs-mcp-server.js +8 -8
- package/lib/project-logs-schema.js +1 -1
- package/lib/project-logs-service.js +46 -13
- package/lib/project-logs-store.js +17 -15
- package/lib/project-logs.js +70 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-pending-message-queue.js +71 -7
- package/lib/project-scheduled-messages.js +235 -52
- package/lib/project-scheduled-result-store.js +131 -0
- package/lib/project-scheduled-task-execution.js +35 -2
- package/lib/project-session-notes.js +11 -4
- package/lib/project-sessions.js +7 -0
- package/lib/project-user-message.js +72 -5
- package/lib/project.js +60 -5
- package/lib/public/app.js +27 -12
- package/lib/public/css/avatar-imprints.css +1 -1
- package/lib/public/css/command-palette.css +20 -0
- package/lib/public/css/git-panel.css +13 -12
- package/lib/public/css/git-placard.css +4 -4
- package/lib/public/css/input.css +0 -109
- package/lib/public/css/issues.css +78 -31
- package/lib/public/css/mobile-nav.css +7 -0
- package/lib/public/css/notifications-center.css +3 -3
- package/lib/public/css/pending-message-queue.css +3 -0
- package/lib/public/css/project-logs.css +15 -0
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +29 -0
- package/lib/public/modules/app-connection.js +9 -1
- package/lib/public/modules/app-favicon.js +10 -0
- package/lib/public/modules/app-messages.js +49 -37
- package/lib/public/modules/app-notifications.js +2 -0
- package/lib/public/modules/app-panels.js +77 -23
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/app-rate-limit.js +148 -243
- package/lib/public/modules/avatar-imprint.js +25 -70
- package/lib/public/modules/avatar.js +12 -4
- package/lib/public/modules/chat-render-runtime.js +20 -2
- package/lib/public/modules/clay-issue-links.js +23 -2
- package/lib/public/modules/clay-log-links.js +11 -1
- package/lib/public/modules/context-view-preference.js +143 -0
- package/lib/public/modules/default-vendor.js +116 -0
- package/lib/public/modules/filebrowser-tabs.js +1 -0
- package/lib/public/modules/filebrowser.js +45 -18
- package/lib/public/modules/input.js +81 -20
- package/lib/public/modules/issues-render.js +36 -10
- package/lib/public/modules/issues.js +95 -13
- package/lib/public/modules/markdown.js +3 -1
- package/lib/public/modules/pane-bridge.js +14 -8
- package/lib/public/modules/pane-issue-bridge.js +27 -0
- package/lib/public/modules/pending-message-queue.js +43 -1
- package/lib/public/modules/permission-hints.js +13 -0
- package/lib/public/modules/profile.js +9 -9
- package/lib/public/modules/project-activation.js +49 -0
- package/lib/public/modules/project-logs-render.js +13 -3
- package/lib/public/modules/project-logs.js +45 -15
- package/lib/public/modules/right-workbench.js +44 -0
- package/lib/public/modules/scheduled-message-state.js +12 -36
- package/lib/public/modules/scheduled-result-client.js +33 -0
- package/lib/public/modules/scheduled-result-display.js +50 -0
- package/lib/public/modules/scheduled-result-notification.js +31 -0
- package/lib/public/modules/scheduled-task-presentation.js +15 -0
- package/lib/public/modules/scheduled-tasks.js +9 -21
- package/lib/public/modules/sidebar-mobile.js +51 -3
- package/lib/public/modules/sidebar-sessions.js +65 -5
- package/lib/public/modules/split-session-boundary.js +28 -0
- package/lib/public/modules/split-view.js +42 -7
- package/lib/public/modules/sticky-notes-browser.js +130 -18
- package/lib/public/modules/sticky-notes.js +2 -3
- package/lib/public/modules/terminal.js +4 -4
- package/lib/public/modules/thinking-lifecycle.js +4 -1
- package/lib/public/modules/tools.js +5 -1
- package/lib/scheduled-run-state.js +10 -2
- package/lib/scheduler.js +1 -0
- package/lib/sdk-bridge.js +97 -61
- package/lib/sdk-message-processor.js +44 -13
- package/lib/sdk-skill-discovery.js +6 -28
- package/lib/sdk-turn-queue.js +14 -0
- package/lib/server-context-view.js +53 -0
- package/lib/server-default-vendor.js +60 -0
- package/lib/server.js +23 -6
- package/lib/session-notes-mcp-server.js +3 -5
- package/lib/session-pair-factory.js +2 -2
- package/lib/session-pair-mcp-server.js +5 -4
- package/lib/session-title-generator.js +89 -0
- package/lib/session-title-policy.js +8 -2
- package/lib/sessions.js +26 -1
- package/lib/user-presence.js +6 -0
- package/lib/users-context-view-preferences.js +65 -0
- package/lib/users-default-vendor-preferences.js +80 -0
- package/lib/users.js +12 -0
- package/lib/ws-schema.js +11 -0
- package/lib/yoke/acp-agent-profiles.js +15 -2
- package/lib/yoke/adapters/acp.js +3 -2
- package/lib/yoke/adapters/antigravity.js +2 -2
- package/lib/yoke/adapters/claude-worker.js +11 -12
- package/lib/yoke/adapters/claude.js +75 -22
- package/lib/yoke/adapters/codex.js +216 -29
- package/lib/yoke/adapters/kiro.js +6 -5
- package/lib/yoke/claude-context-usage.js +57 -0
- package/lib/yoke/claude-message-correlation.js +31 -0
- package/lib/yoke/claude-model-switch.js +15 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/mcp-bridge-server.js +18 -5
- package/lib/yoke/session-skill-catalog.js +45 -0
- package/lib/yoke/skill-discovery.js +60 -20
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +5 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { escapeHtml } from './utils.js';
|
|
2
2
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
3
|
import { store } from './store.js';
|
|
4
|
-
import { addToMessages, scrollToBottom } from './chat-render-runtime.js';
|
|
4
|
+
import { addToMessages, keepActiveThinkingAtTail, scrollToBottom } from './chat-render-runtime.js';
|
|
5
5
|
import { prepareThinkingView, bindThinkingView, updateThinkingView, beginThinkingView, setThinkingViewLive, finishThinkingView } from './thinking-view.js';
|
|
6
6
|
|
|
7
7
|
function getThinkingState() {
|
|
@@ -92,6 +92,7 @@ export function startThinkingSegment() {
|
|
|
92
92
|
segmentText: "",
|
|
93
93
|
startTime: Date.now(),
|
|
94
94
|
});
|
|
95
|
+
if (!store.get('replayingHistory')) keepActiveThinkingAtTail();
|
|
95
96
|
beginThinkingView(thinkingState.el, !store.get('replayingHistory') && !isMateThinking());
|
|
96
97
|
showMateActivity(thinkingState.el, true, !!thinkingState.detailsText.trim());
|
|
97
98
|
refreshIcons();
|
|
@@ -109,6 +110,7 @@ export function appendThinkingText(text) {
|
|
|
109
110
|
detailsText: thinkingState.detailsText + separator + text,
|
|
110
111
|
latestText: segmentText.trim() ? segmentText : thinkingState.latestText,
|
|
111
112
|
});
|
|
113
|
+
if (!store.get('replayingHistory')) keepActiveThinkingAtTail();
|
|
112
114
|
updateThinkingView(thinkingState.el, thinkingState.segmentText || thinkingState.latestText, thinkingState.detailsText);
|
|
113
115
|
maybeScrollToBottom();
|
|
114
116
|
}
|
|
@@ -156,6 +158,7 @@ export function restoreThinkingState(saved) {
|
|
|
156
158
|
export function resumeThinkingAfterReplay(processing) {
|
|
157
159
|
var thinkingState = getThinkingState();
|
|
158
160
|
if (!thinkingState || !thinkingState.active || !processing) return;
|
|
161
|
+
keepActiveThinkingAtTail();
|
|
159
162
|
setThinkingViewLive(thinkingState.el, !isMateThinking());
|
|
160
163
|
showMateActivity(thinkingState.el, true, !!thinkingState.detailsText.trim());
|
|
161
164
|
}
|
|
@@ -6,6 +6,7 @@ import { openFile } from './filebrowser.js';
|
|
|
6
6
|
import { mateAvatarUrl } from './avatar.js';
|
|
7
7
|
import { getChatLayout } from './theme.js';
|
|
8
8
|
import { store } from './store.js';
|
|
9
|
+
import { applyPermissionHints } from './permission-hints.js';
|
|
9
10
|
import { VENDOR_NAMES } from './app-rendering.js';
|
|
10
11
|
import { startThinkingSegment, appendThinkingText, stopThinkingSegment, resetThinkingTurn, clearThinkingState, saveThinkingState, restoreThinkingState, updateThinkingTokenEstimate } from './thinking-lifecycle.js';
|
|
11
12
|
import { findAskUserCard, getAskUserRequestKey, hasAskUserRequest, rememberAskUserRequest, markAskUserRequestAnswered, saveAskUserState, restoreAskUserState, resetAskUserState } from './ask-user-state.js';
|
|
@@ -542,7 +543,7 @@ function permissionInputSummary(toolName, input) {
|
|
|
542
543
|
}
|
|
543
544
|
}
|
|
544
545
|
|
|
545
|
-
export function renderPermissionRequest(requestId, toolName, toolInput, decisionReason, mateId, vendor) {
|
|
546
|
+
export function renderPermissionRequest(requestId, toolName, toolInput, decisionReason, mateId, vendor, hints) {
|
|
546
547
|
if (pendingPermissions[requestId]) return;
|
|
547
548
|
ctx.finalizeAssistantBlock();
|
|
548
549
|
stopThinking();
|
|
@@ -551,17 +552,20 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
|
|
|
551
552
|
// ExitPlanMode: render as plan confirmation instead of generic permission
|
|
552
553
|
if (toolName === "ExitPlanMode") {
|
|
553
554
|
renderPlanPermission(requestId);
|
|
555
|
+
applyPermissionHints(pendingPermissions[requestId], { defaultToNo: hints && hints.defaultToNo });
|
|
554
556
|
return;
|
|
555
557
|
}
|
|
556
558
|
|
|
557
559
|
// Channel layout or Mate DM: conversational "Can I ...?" style
|
|
558
560
|
if ((ctx.isMateDm && ctx.isMateDm()) || getChatLayout() === "channel") {
|
|
559
561
|
renderConversationalPermission(requestId, toolName, toolInput, mateId, vendor);
|
|
562
|
+
applyPermissionHints(pendingPermissions[requestId], hints);
|
|
560
563
|
return;
|
|
561
564
|
}
|
|
562
565
|
|
|
563
566
|
// Bubble layout: formal "Permission Required" dialog
|
|
564
567
|
renderFormalPermission(requestId, toolName, toolInput, decisionReason);
|
|
568
|
+
applyPermissionHints(pendingPermissions[requestId], hints);
|
|
565
569
|
}
|
|
566
570
|
|
|
567
571
|
function renderFormalPermission(requestId, toolName, toolInput, decisionReason) {
|
|
@@ -42,9 +42,11 @@ function createScheduledRunState(ctx) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function complete(id, runId, result) {
|
|
45
|
-
|
|
45
|
+
var completed = null;
|
|
46
|
+
var saved = mutate(id, function (rec) {
|
|
46
47
|
if (!rec.activeRun || rec.activeRun.runId !== runId) return false;
|
|
47
48
|
var run = Object.assign({}, rec.activeRun, result || {}, { finishedAt: Date.now() });
|
|
49
|
+
completed = run;
|
|
48
50
|
rec.lastRunAt = run.startedAt || Date.now();
|
|
49
51
|
rec.lastRunResult = run.outcome || "failed";
|
|
50
52
|
rec.runs = Array.isArray(rec.runs) ? rec.runs : [];
|
|
@@ -54,6 +56,8 @@ function createScheduledRunState(ctx) {
|
|
|
54
56
|
rec.activeRun = null;
|
|
55
57
|
return true;
|
|
56
58
|
});
|
|
59
|
+
if (saved && completed && ctx.onTerminal) ctx.onTerminal(ctx.getRecord(id), completed);
|
|
60
|
+
return saved;
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
function recover(rec) {
|
|
@@ -81,7 +85,11 @@ function createScheduledRunState(ctx) {
|
|
|
81
85
|
return true;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
function notifyTerminal(rec, run) {
|
|
89
|
+
if (ctx.onTerminal && rec && run) ctx.onTerminal(rec, run);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { begin: begin, complete: complete, notifyTerminal: notifyTerminal, prepareRecord: prepareRecord, recordLegacy: recordLegacy, recover: recover, update: update };
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
module.exports = { createScheduledRunState: createScheduledRunState };
|
package/lib/scheduler.js
CHANGED
package/lib/sdk-bridge.js
CHANGED
|
@@ -7,12 +7,14 @@ var usersModule = require("./users");
|
|
|
7
7
|
var { getCodexConfig } = require("./codex-defaults");
|
|
8
8
|
var { getKiroConfig } = require("./kiro-defaults");
|
|
9
9
|
var { splitShellSegments, attachSkillDiscovery } = require("./sdk-skill-discovery");
|
|
10
|
+
var skillDiscovery = require("./yoke/skill-discovery");
|
|
10
11
|
var { isSafeBashSegment } = require("./safe-bash-commands");
|
|
11
12
|
var { createMessageQueue } = require("./sdk-message-queue");
|
|
12
13
|
var { attachMessageProcessor } = require("./sdk-message-processor");
|
|
13
14
|
var homeDebateToolPolicy = require("./home-debate-tool-policy");
|
|
14
15
|
var yoke = require("./yoke");
|
|
15
16
|
var sessionTitlePolicy = require("./session-title-policy");
|
|
17
|
+
var sessionTitleGenerator = require("./session-title-generator");
|
|
16
18
|
var loopInterview = require("./project-loop-interview");
|
|
17
19
|
|
|
18
20
|
// Extract serializable tool descriptors from MCP server instances.
|
|
@@ -130,6 +132,7 @@ function createSDKBridge(opts) {
|
|
|
130
132
|
var clayPort = opts.clayPort || 2633;
|
|
131
133
|
var clayTls = opts.clayTls || false;
|
|
132
134
|
var clayAuthToken = opts.clayAuthToken || null;
|
|
135
|
+
var clayBridgeToken = opts.clayBridgeToken || null;
|
|
133
136
|
var getSessionToolDefs = opts.getSessionToolDefs || function () { return []; };
|
|
134
137
|
var canUseSessionTools = opts.canUseSessionTools || function () { return true; };
|
|
135
138
|
var queryToolSnapshots = new WeakMap();
|
|
@@ -153,6 +156,7 @@ function createSDKBridge(opts) {
|
|
|
153
156
|
if (clayTls) bridgeArgs.push("--tls");
|
|
154
157
|
var env = [];
|
|
155
158
|
if (clayAuthToken) env.push({ name: "CLAY_AUTH_TOKEN", value: clayAuthToken });
|
|
159
|
+
if (clayBridgeToken) env.push({ name: "CLAY_BRIDGE_TOKEN", value: clayBridgeToken });
|
|
156
160
|
return [{
|
|
157
161
|
name: "clay-tools",
|
|
158
162
|
command: process.execPath,
|
|
@@ -161,6 +165,23 @@ function createSDKBridge(opts) {
|
|
|
161
165
|
}];
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
function getCodexSessionMcpServer(session, queryGeneration) {
|
|
169
|
+
if (!session || !Number.isInteger(queryGeneration)) return null;
|
|
170
|
+
var bridgeArgs = [
|
|
171
|
+
path.join(__dirname, "yoke", "mcp-bridge-server.js"),
|
|
172
|
+
"--port", String(clayPort),
|
|
173
|
+
"--slug", slug,
|
|
174
|
+
"--session", String(session.localId),
|
|
175
|
+
"--query-generation", String(queryGeneration),
|
|
176
|
+
"--session-only",
|
|
177
|
+
];
|
|
178
|
+
if (clayTls) bridgeArgs.push("--tls");
|
|
179
|
+
var bridgeEnv = {};
|
|
180
|
+
if (clayAuthToken) bridgeEnv.CLAY_AUTH_TOKEN = clayAuthToken;
|
|
181
|
+
if (clayBridgeToken) bridgeEnv.CLAY_BRIDGE_TOKEN = clayBridgeToken;
|
|
182
|
+
return { name: "clay-session-tools", command: process.execPath, args: bridgeArgs, env: bridgeEnv };
|
|
183
|
+
}
|
|
184
|
+
|
|
164
185
|
function getFreshAuthState(force) {
|
|
165
186
|
var now = Date.now();
|
|
166
187
|
if (!force && _cachedFreshAuthState && now - _cachedFreshAuthAt < 15000) {
|
|
@@ -337,7 +358,7 @@ function createSDKBridge(opts) {
|
|
|
337
358
|
}
|
|
338
359
|
|
|
339
360
|
// --- Skill discovery (extracted to sdk-skill-discovery.js) ---
|
|
340
|
-
var skills = attachSkillDiscovery({ cwd: cwd });
|
|
361
|
+
var skills = attachSkillDiscovery({ cwd: cwd, getOptions: function () { return { env: getRuntimeEnv() }; } });
|
|
341
362
|
var discoverSkillDirs = skills.discoverSkillDirs;
|
|
342
363
|
var mergeSkills = skills.mergeSkills;
|
|
343
364
|
|
|
@@ -350,50 +371,27 @@ function createSDKBridge(opts) {
|
|
|
350
371
|
return owner && owner.linuxUser ? owner.linuxUser : null;
|
|
351
372
|
}
|
|
352
373
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
var
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
var entry = session.history[i];
|
|
364
|
-
if (entry.type === "user_message" && entry.text) {
|
|
365
|
-
userMessages.push(entry.text.substring(0, 200));
|
|
366
|
-
if (userMessages.length >= 5) break;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (userMessages.length === 0) {
|
|
370
|
-
console.log("[auto-title] No user messages found in session " + session.localId);
|
|
371
|
-
return;
|
|
374
|
+
function getSessionSkillOptions(session) {
|
|
375
|
+
var runtimeEnv = getRuntimeEnv(session || {});
|
|
376
|
+
if (!osUsers) return { env: runtimeEnv };
|
|
377
|
+
var linuxUser = getSessionLinuxUser(session);
|
|
378
|
+
if (!linuxUser) return { disable: true };
|
|
379
|
+
try {
|
|
380
|
+
var info = require("./os-users").resolveOsUserInfo(linuxUser);
|
|
381
|
+
return Object.assign({}, skillDiscovery.optionsForOsUser(info), { env: runtimeEnv });
|
|
382
|
+
} catch (e) {
|
|
383
|
+
return { disable: true };
|
|
372
384
|
}
|
|
373
|
-
console.log("[auto-title] Calling adapter.generateTitle with " + userMessages.length + " messages for session " + session.localId);
|
|
374
|
-
|
|
375
|
-
sessionAdapter.generateTitle(userMessages, {
|
|
376
|
-
cwd: cwd,
|
|
377
|
-
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
378
|
-
env: getRuntimeEnv(session),
|
|
379
|
-
}).then(function(title) {
|
|
380
|
-
if (!title || title.length < 2) return;
|
|
381
|
-
title = title.substring(0, 100);
|
|
382
|
-
if (!session.titleManuallySet) {
|
|
383
|
-
session.title = title;
|
|
384
|
-
session.titleAutoGenerated = true;
|
|
385
|
-
sm.saveSessionFile(session);
|
|
386
|
-
sm.broadcastSessionList();
|
|
387
|
-
if (session.cliSessionId && typeof adapter.renameSession === "function") {
|
|
388
|
-
adapter.renameSession(session.cliSessionId, title, { dir: cwd }).catch(function () {});
|
|
389
|
-
}
|
|
390
|
-
console.log("[auto-title] Generated title for session " + session.localId + ": " + title);
|
|
391
|
-
}
|
|
392
|
-
}).catch(function(e) {
|
|
393
|
-
console.error("[auto-title] Failed:", e.message || e);
|
|
394
|
-
});
|
|
395
385
|
}
|
|
396
386
|
|
|
387
|
+
var titleGenerator = sessionTitleGenerator.attachSessionTitleGenerator({
|
|
388
|
+
sm: sm,
|
|
389
|
+
cwd: cwd,
|
|
390
|
+
getAdapter: getAdapterForSession,
|
|
391
|
+
getLinuxUser: getSessionLinuxUser,
|
|
392
|
+
getRuntimeEnv: getRuntimeEnv,
|
|
393
|
+
});
|
|
394
|
+
|
|
397
395
|
var msgProcessor = attachMessageProcessor({
|
|
398
396
|
sm: sm,
|
|
399
397
|
send: send,
|
|
@@ -407,9 +405,10 @@ function createSDKBridge(opts) {
|
|
|
407
405
|
adapter: adapter,
|
|
408
406
|
saveImageFile: opts.saveImageFile,
|
|
409
407
|
getSessionLinuxUser: getSessionLinuxUser,
|
|
408
|
+
getSessionSkillOptions: getSessionSkillOptions,
|
|
410
409
|
onProcessingChanged: onProcessingChanged,
|
|
411
410
|
onTurnDone: onTurnDone,
|
|
412
|
-
onAutoTitle: function (session) {
|
|
411
|
+
onAutoTitle: function (session) { titleGenerator.generate(session); },
|
|
413
412
|
opts: opts,
|
|
414
413
|
discoverSkillDirs: discoverSkillDirs,
|
|
415
414
|
mergeSkills: mergeSkills,
|
|
@@ -856,6 +855,8 @@ function createSDKBridge(opts) {
|
|
|
856
855
|
input: input,
|
|
857
856
|
toolUseId: opts.toolUseID,
|
|
858
857
|
decisionReason: opts.decisionReason || "",
|
|
858
|
+
defaultToNo: opts.defaultToNo === true,
|
|
859
|
+
suppressAlwaysAllowRule: opts.suppressAlwaysAllowRule === true,
|
|
859
860
|
signal: opts.signal,
|
|
860
861
|
});
|
|
861
862
|
if (routed) return routed;
|
|
@@ -872,6 +873,8 @@ function createSDKBridge(opts) {
|
|
|
872
873
|
toolInput: input,
|
|
873
874
|
toolUseId: opts.toolUseID,
|
|
874
875
|
decisionReason: opts.decisionReason || "",
|
|
876
|
+
defaultToNo: opts.defaultToNo === true,
|
|
877
|
+
suppressAlwaysAllowRule: opts.suppressAlwaysAllowRule === true,
|
|
875
878
|
};
|
|
876
879
|
|
|
877
880
|
var permMsg = {
|
|
@@ -881,6 +884,8 @@ function createSDKBridge(opts) {
|
|
|
881
884
|
toolInput: input,
|
|
882
885
|
toolUseId: opts.toolUseID,
|
|
883
886
|
decisionReason: opts.decisionReason || "",
|
|
887
|
+
defaultToNo: opts.defaultToNo === true,
|
|
888
|
+
suppressAlwaysAllowRule: opts.suppressAlwaysAllowRule === true,
|
|
884
889
|
vendor: session.vendor || (adapter && adapter.vendor) || "claude",
|
|
885
890
|
};
|
|
886
891
|
sendAndRecord(session, permMsg);
|
|
@@ -907,6 +912,8 @@ function createSDKBridge(opts) {
|
|
|
907
912
|
requestId: requestId,
|
|
908
913
|
toolName: toolName,
|
|
909
914
|
toolInput: input,
|
|
915
|
+
defaultToNo: opts.defaultToNo === true,
|
|
916
|
+
suppressAlwaysAllowRule: opts.suppressAlwaysAllowRule === true,
|
|
910
917
|
});
|
|
911
918
|
}
|
|
912
919
|
|
|
@@ -1031,7 +1038,8 @@ function createSDKBridge(opts) {
|
|
|
1031
1038
|
&& Number(session._sdkQueryGeneration || 0) === codexCatalogCandidate.generation
|
|
1032
1039
|
&& session.cliSessionId === msg.sessionId
|
|
1033
1040
|
&& (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
|
|
1034
|
-
session.codexDynamicToolCatalogVersion = codexCatalogCandidate.version;
|
|
1041
|
+
if (codexCatalogCandidate.version) session.codexDynamicToolCatalogVersion = codexCatalogCandidate.version;
|
|
1042
|
+
if (codexCatalogCandidate.issuesVersion) session.codexIssuesToolCatalogVersion = codexCatalogCandidate.issuesVersion;
|
|
1035
1043
|
sm.saveSessionFile(session);
|
|
1036
1044
|
codexCatalogCandidate = null;
|
|
1037
1045
|
}
|
|
@@ -1379,6 +1387,7 @@ function createSDKBridge(opts) {
|
|
|
1379
1387
|
clayPort: clayPort,
|
|
1380
1388
|
clayTls: clayTls,
|
|
1381
1389
|
clayAuthToken: clayAuthToken,
|
|
1390
|
+
clayBridgeToken: clayBridgeToken,
|
|
1382
1391
|
slug: slug,
|
|
1383
1392
|
env: sessionRuntimeEnv,
|
|
1384
1393
|
});
|
|
@@ -1393,6 +1402,7 @@ function createSDKBridge(opts) {
|
|
|
1393
1402
|
clayPort: clayPort,
|
|
1394
1403
|
clayTls: clayTls,
|
|
1395
1404
|
clayAuthToken: clayAuthToken,
|
|
1405
|
+
clayBridgeToken: clayBridgeToken,
|
|
1396
1406
|
slug: slug,
|
|
1397
1407
|
env: sessionRuntimeEnv,
|
|
1398
1408
|
});
|
|
@@ -1601,7 +1611,11 @@ function createSDKBridge(opts) {
|
|
|
1601
1611
|
if (linuxUser) {
|
|
1602
1612
|
claudeOpts.linuxUser = linuxUser;
|
|
1603
1613
|
claudeOpts.singleTurn = !!session.singleTurn;
|
|
1604
|
-
|
|
1614
|
+
try {
|
|
1615
|
+
claudeOpts.originalHome = require("./os-users").resolveOsUserInfo(linuxUser).home;
|
|
1616
|
+
} catch (e) {
|
|
1617
|
+
claudeOpts.originalHome = null;
|
|
1618
|
+
}
|
|
1605
1619
|
claudeOpts.projectPath = session.cwd || null;
|
|
1606
1620
|
claudeOpts._perfT0 = t0;
|
|
1607
1621
|
// Pass previous worker state for reuse
|
|
@@ -1643,6 +1657,17 @@ function createSDKBridge(opts) {
|
|
|
1643
1657
|
// delivered by this provider.
|
|
1644
1658
|
var sessionToolDefs = getSessionToolDefs(session) || [];
|
|
1645
1659
|
var queryGeneration = Number(session._sdkQueryGeneration || 0);
|
|
1660
|
+
var hasIssueCatalog = false;
|
|
1661
|
+
for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
|
|
1662
|
+
if (sessionToolDefs[issueToolIndex].name === "create_issue") {
|
|
1663
|
+
hasIssueCatalog = true;
|
|
1664
|
+
break;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
|
|
1668
|
+
&& !!session.cliSessionId
|
|
1669
|
+
&& hasIssueCatalog
|
|
1670
|
+
&& Number(session.codexIssuesToolCatalogVersion || 0) < 1;
|
|
1646
1671
|
function queryContextAvailable() {
|
|
1647
1672
|
if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
|
|
1648
1673
|
if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
|
|
@@ -1767,10 +1792,22 @@ function createSDKBridge(opts) {
|
|
|
1767
1792
|
initialTitleProvisional = initialTitleChoice.provisional;
|
|
1768
1793
|
}
|
|
1769
1794
|
|
|
1795
|
+
var modelSwitchGeneration = session._queryGeneration;
|
|
1770
1796
|
var queryOpts = {
|
|
1797
|
+
onModelSwitch: function(change) {
|
|
1798
|
+
if (session._queryGeneration !== modelSwitchGeneration || session.taskStopRequested) return;
|
|
1799
|
+
if (!change || !change.fromModel || !change.toModel || change.fromModel === change.toModel) return;
|
|
1800
|
+
sendAndRecord(session, { type: "informational", level: "info", content: "Model changed from " + change.fromModel + " to " + change.toModel + " (" + change.source + ")." });
|
|
1801
|
+
},
|
|
1771
1802
|
cwd: cwd,
|
|
1803
|
+
slug: slug,
|
|
1804
|
+
clayPort: clayPort,
|
|
1805
|
+
clayTls: clayTls,
|
|
1806
|
+
clayAuthToken: clayAuthToken,
|
|
1807
|
+
clayBridgeToken: clayBridgeToken,
|
|
1772
1808
|
linuxUser: linuxUser || undefined,
|
|
1773
1809
|
env: sessionRuntimeEnv,
|
|
1810
|
+
skillOptions: getSessionSkillOptions(session),
|
|
1774
1811
|
// appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
|
|
1775
1812
|
// REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
|
|
1776
1813
|
// would strip the driver session down to the two-sentence pair preset.
|
|
@@ -1792,6 +1829,9 @@ function createSDKBridge(opts) {
|
|
|
1792
1829
|
toolServers: mergedMcpServers,
|
|
1793
1830
|
toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
|
|
1794
1831
|
dynamicTools: sessionToolDefs,
|
|
1832
|
+
// Codex persists dynamic tools with a thread. A stale authorized Driver
|
|
1833
|
+
// gets this exact-query bridge; its handlers still reauthorize live.
|
|
1834
|
+
sessionMcpServer: recoverCodexIssueTools ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
|
|
1795
1835
|
callDynamicTool: function (toolName, args) {
|
|
1796
1836
|
var toolDef = findSessionToolDef(toolName);
|
|
1797
1837
|
if (toolDef && typeof toolDef.handler === "function") {
|
|
@@ -1862,10 +1902,12 @@ function createSDKBridge(opts) {
|
|
|
1862
1902
|
// project threshold); seeded titles remain fixed.
|
|
1863
1903
|
if (initialTitle && !session.title) {
|
|
1864
1904
|
session.title = initialTitle;
|
|
1905
|
+
session.titleProvisional = initialTitleProvisional;
|
|
1865
1906
|
session.titleAutoGenerated = !initialTitleProvisional;
|
|
1866
1907
|
sm.saveSessionFile(session);
|
|
1867
1908
|
sm.broadcastSessionList();
|
|
1868
1909
|
} else if (initialTitle && session.title === initialTitle) {
|
|
1910
|
+
session.titleProvisional = initialTitleProvisional;
|
|
1869
1911
|
session.titleAutoGenerated = !initialTitleProvisional;
|
|
1870
1912
|
sm.saveSessionFile(session);
|
|
1871
1913
|
}
|
|
@@ -1976,6 +2018,12 @@ function createSDKBridge(opts) {
|
|
|
1976
2018
|
if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
|
|
1977
2019
|
codexCatalogCandidate = { generation: queryGeneration, version: yoke.sessionTools.CODEX_CATALOG_VERSION };
|
|
1978
2020
|
}
|
|
2021
|
+
if (hasIssueCatalog) {
|
|
2022
|
+
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
|
|
2023
|
+
generation: queryGeneration,
|
|
2024
|
+
issuesVersion: 1,
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
1979
2027
|
}
|
|
1980
2028
|
session.streamPromise = processQueryStream(session, codexCatalogCandidate).catch(function(err) {
|
|
1981
2029
|
});
|
|
@@ -2216,24 +2264,12 @@ function createSDKBridge(opts) {
|
|
|
2216
2264
|
clayTls: clayTls,
|
|
2217
2265
|
clayAuthToken: clayAuthToken,
|
|
2218
2266
|
slug: slug,
|
|
2267
|
+
clayBridgeToken: clayBridgeToken,
|
|
2219
2268
|
});
|
|
2220
2269
|
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
var seen = new Set();
|
|
2225
|
-
var combined = [];
|
|
2226
|
-
var all = result.slashCommands.concat(Array.from(sm.skillNames));
|
|
2227
|
-
for (var k = 0; k < all.length; k++) {
|
|
2228
|
-
if (!seen.has(all[k])) {
|
|
2229
|
-
seen.add(all[k]);
|
|
2230
|
-
combined.push(all[k]);
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
sm.slashCommands = combined;
|
|
2234
|
-
sm.setSlashCommandsForVendor(defaultVendor, combined);
|
|
2235
|
-
send({ type: "slash_commands", commands: combined, vendor: defaultVendor });
|
|
2236
|
-
}
|
|
2270
|
+
// Skill and slash-command catalogs are session-owned. The warmup has
|
|
2271
|
+
// no authenticated session target, so never publish its personal
|
|
2272
|
+
// inventory through the project-wide session manager or broadcast.
|
|
2237
2273
|
if (result.defaultModel) {
|
|
2238
2274
|
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
2239
2275
|
sm.defaultModelByVendor[defaultVendor] = sm.defaultModelByVendor[defaultVendor] || result.defaultModel;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
var usersModule = require("./users");
|
|
2
|
+
var turnQueue = require("./sdk-turn-queue");
|
|
3
|
+
var contextUsage = require("./context-usage");
|
|
2
4
|
var yoke = require("./yoke");
|
|
3
5
|
var backgroundTaskTiming = require("./background-task-timing");
|
|
4
6
|
var sessionTitlePolicy = require("./session-title-policy");
|
|
@@ -26,6 +28,7 @@ function attachMessageProcessor(ctx) {
|
|
|
26
28
|
var mergeSkills = ctx.mergeSkills;
|
|
27
29
|
var saveImageFile = ctx.saveImageFile;
|
|
28
30
|
var getSessionLinuxUser = ctx.getSessionLinuxUser || function () { return null; };
|
|
31
|
+
var getSessionSkillOptions = ctx.getSessionSkillOptions || function () { return {}; };
|
|
29
32
|
|
|
30
33
|
function getMateIdForNotification() {
|
|
31
34
|
if (!isMate) return null;
|
|
@@ -206,21 +209,26 @@ function attachMessageProcessor(ctx) {
|
|
|
206
209
|
session.activeBackgroundTasks = [];
|
|
207
210
|
sendToSession(session, { type: "active_background_tasks", tasks: [] });
|
|
208
211
|
if (previousBackgroundTaskCount !== 0) sm.broadcastSessionList();
|
|
209
|
-
var fsSkills = discoverSkillDirs();
|
|
210
|
-
|
|
212
|
+
var fsSkills = discoverSkillDirs(getSessionSkillOptions(session));
|
|
213
|
+
session.skillNames = Array.from(mergeSkills(parsed.skills, fsSkills));
|
|
211
214
|
if (parsed.slashCommands) {
|
|
212
215
|
// Union: SDK slash_commands + merged skills (deduplicated)
|
|
216
|
+
session.vendorSlashCommands = parsed.slashCommands.filter(function (name) {
|
|
217
|
+
return session.skillNames.indexOf(name) === -1;
|
|
218
|
+
});
|
|
213
219
|
var seen = new Set();
|
|
214
220
|
var combined = [];
|
|
215
|
-
var all =
|
|
221
|
+
var all = session.vendorSlashCommands.concat(session.skillNames);
|
|
216
222
|
for (var k = 0; k < all.length; k++) {
|
|
217
223
|
if (!seen.has(all[k])) {
|
|
218
224
|
seen.add(all[k]);
|
|
219
225
|
combined.push(all[k]);
|
|
220
226
|
}
|
|
221
227
|
}
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
session.slashCommands = combined;
|
|
229
|
+
session.slashCommandsByVendor = session.slashCommandsByVendor || {};
|
|
230
|
+
session.slashCommandsByVendor[session.vendor || (adapter && adapter.vendor) || "claude"] = combined;
|
|
231
|
+
sendToSession(session, { type: "slash_commands", commands: combined, vendor: session.vendor || (adapter && adapter.vendor) || "claude" });
|
|
224
232
|
}
|
|
225
233
|
if (parsed.model) {
|
|
226
234
|
var initVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
@@ -563,6 +571,7 @@ function attachMessageProcessor(ctx) {
|
|
|
563
571
|
}
|
|
564
572
|
|
|
565
573
|
session._lastAdapterError = null;
|
|
574
|
+
session._queuedTurnCount = turnQueue.pendingSends(session, parsed);
|
|
566
575
|
var hasQueuedTurn = (session._queuedTurnCount || 0) > 0;
|
|
567
576
|
if (hasQueuedTurn) {
|
|
568
577
|
session._queuedTurnCount--;
|
|
@@ -580,7 +589,13 @@ function attachMessageProcessor(ctx) {
|
|
|
580
589
|
&& /not logged in/i.test(previewTrimmed) && /\/login/i.test(previewTrimmed);
|
|
581
590
|
// Fetch rich context usage breakdown (fire-and-forget, non-blocking)
|
|
582
591
|
if (session.queryInstance && typeof session.queryInstance.getContextUsage === "function") {
|
|
583
|
-
session.queryInstance
|
|
592
|
+
var contextQuery = session.queryInstance;
|
|
593
|
+
var contextTurn = session.turnCount;
|
|
594
|
+
contextUsage.readUsage(session, "summary").then(function(ctxUsage) {
|
|
595
|
+
if (session.queryInstance !== contextQuery || session.turnCount !== contextTurn) return;
|
|
596
|
+
// null means the shared reader suppressed a superseded response;
|
|
597
|
+
// only an adapter's explicit unavailable object may clear the UI.
|
|
598
|
+
if (!ctxUsage) return;
|
|
584
599
|
session.lastContextUsage = ctxUsage;
|
|
585
600
|
sendToSession(session, { type: "context_usage", data: ctxUsage });
|
|
586
601
|
}).catch(function(e) {
|
|
@@ -597,6 +612,11 @@ function attachMessageProcessor(ctx) {
|
|
|
597
612
|
modelUsage: parsed.modelUsage || null,
|
|
598
613
|
sessionId: parsed.sessionId,
|
|
599
614
|
lastStreamInputTokens: lastStreamInput,
|
|
615
|
+
userMessageId: parsed.userMessageId,
|
|
616
|
+
userMessageIds: parsed.userMessageIds,
|
|
617
|
+
resultIndex: parsed.resultIndex,
|
|
618
|
+
resumeReason: parsed.resumeReason,
|
|
619
|
+
localCommand: parsed.localCommand,
|
|
600
620
|
});
|
|
601
621
|
if (parsed.fastModeState) {
|
|
602
622
|
sendAndRecord(session, { type: "fast_mode_state", state: parsed.fastModeState });
|
|
@@ -647,11 +667,13 @@ function attachMessageProcessor(ctx) {
|
|
|
647
667
|
session.streamedText = false;
|
|
648
668
|
sm.broadcastSessionList();
|
|
649
669
|
|
|
650
|
-
// Auto-generate
|
|
651
|
-
|
|
670
|
+
// Auto-generate after the first completed substantive turn. The title
|
|
671
|
+
// coordinator handles retries while generation is pending or failed.
|
|
672
|
+
if (session.turnCount >= sessionTitlePolicy.generationTurn(isMate)
|
|
652
673
|
&& !session.titleAutoGenerated
|
|
653
674
|
&& !session.titleManuallySet
|
|
654
675
|
&& !session.loop
|
|
676
|
+
&& !(session.sessionProvenance && session.sessionProvenance.kind === "worker")
|
|
655
677
|
&& onAutoTitle) {
|
|
656
678
|
try { onAutoTitle(session); } catch (e) {
|
|
657
679
|
console.error("[auto-title] onAutoTitle threw:", e.message || e);
|
|
@@ -769,10 +791,12 @@ function attachMessageProcessor(ctx) {
|
|
|
769
791
|
var info = parsed.rateLimitInfo;
|
|
770
792
|
console.log("[sdk-bridge] rate_limit_event for session " + session.localId + ": status=" + info.status + " resetsAt=" + info.resetsAt + " isUsingOverage=" + info.isUsingOverage + " isProcessing=" + session.isProcessing);
|
|
771
793
|
|
|
772
|
-
//
|
|
794
|
+
// Persist the scoped reset snapshot so reconnect/history restore can reproject it.
|
|
773
795
|
if (info.rateLimitType && info.resetsAt) {
|
|
774
|
-
|
|
796
|
+
sendAndRecord(session, {
|
|
775
797
|
type: "rate_limit_usage",
|
|
798
|
+
sessionId: session.localId,
|
|
799
|
+
vendor: session.vendor || "claude",
|
|
776
800
|
rateLimitType: info.rateLimitType,
|
|
777
801
|
resetsAt: info.resetsAt * 1000,
|
|
778
802
|
status: info.status,
|
|
@@ -783,6 +807,8 @@ function attachMessageProcessor(ctx) {
|
|
|
783
807
|
if (info.status === "allowed_warning" || info.status === "rejected") {
|
|
784
808
|
sendAndRecord(session, {
|
|
785
809
|
type: "rate_limit",
|
|
810
|
+
sessionId: session.localId,
|
|
811
|
+
vendor: session.vendor || "claude",
|
|
786
812
|
status: info.status,
|
|
787
813
|
resetsAt: info.resetsAt ? info.resetsAt * 1000 : null,
|
|
788
814
|
rateLimitType: info.rateLimitType || null,
|
|
@@ -879,15 +905,20 @@ function attachMessageProcessor(ctx) {
|
|
|
879
905
|
// init) and push a full replacement to the client.
|
|
880
906
|
var cmdSeen = new Set();
|
|
881
907
|
var cmdCombined = [];
|
|
882
|
-
|
|
908
|
+
session.vendorSlashCommands = (parsed.commandNames || []).filter(function (name) {
|
|
909
|
+
return (session.skillNames || []).indexOf(name) === -1;
|
|
910
|
+
});
|
|
911
|
+
var cmdAll = session.vendorSlashCommands.concat(session.skillNames || []);
|
|
883
912
|
for (var ci = 0; ci < cmdAll.length; ci++) {
|
|
884
913
|
if (!cmdSeen.has(cmdAll[ci])) {
|
|
885
914
|
cmdSeen.add(cmdAll[ci]);
|
|
886
915
|
cmdCombined.push(cmdAll[ci]);
|
|
887
916
|
}
|
|
888
917
|
}
|
|
889
|
-
|
|
890
|
-
|
|
918
|
+
session.slashCommands = cmdCombined;
|
|
919
|
+
session.slashCommandsByVendor = session.slashCommandsByVendor || {};
|
|
920
|
+
session.slashCommandsByVendor[session.vendor || (adapter && adapter.vendor) || "claude"] = cmdCombined;
|
|
921
|
+
sendToSession(session, { type: "slash_commands", commands: cmdCombined, vendor: session.vendor || (adapter && adapter.vendor) || "claude" });
|
|
891
922
|
|
|
892
923
|
} else if (parsed.yokeType === "worker_shutting_down") {
|
|
893
924
|
// Surface non-routine teardown reasons; "host_exit" is the normal close.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
var path = require("path");
|
|
1
|
+
var skillDiscovery = require("./yoke/skill-discovery");
|
|
3
2
|
|
|
4
3
|
// Split shell command on operators (&&, ||, ;, |) while respecting quotes
|
|
5
4
|
// and parentheses. Returns array of command segments.
|
|
@@ -79,34 +78,13 @@ function splitShellSegments(cmd) {
|
|
|
79
78
|
|
|
80
79
|
function attachSkillDiscovery(ctx) {
|
|
81
80
|
var cwd = ctx.cwd;
|
|
81
|
+
var getOptions = ctx.getOptions || function () { return {}; };
|
|
82
82
|
|
|
83
|
-
function discoverSkillDirs() {
|
|
83
|
+
function discoverSkillDirs(options) {
|
|
84
|
+
var discovered = skillDiscovery.discoverSkills(cwd, options || getOptions());
|
|
84
85
|
var skills = {};
|
|
85
|
-
var
|
|
86
|
-
path.
|
|
87
|
-
path.join(cwd, ".claude", "skills"),
|
|
88
|
-
];
|
|
89
|
-
for (var d = 0; d < dirs.length; d++) {
|
|
90
|
-
var base = dirs[d];
|
|
91
|
-
var entries;
|
|
92
|
-
try {
|
|
93
|
-
entries = fs.readdirSync(base, { withFileTypes: true });
|
|
94
|
-
} catch (e) {
|
|
95
|
-
continue; // directory doesn't exist
|
|
96
|
-
}
|
|
97
|
-
for (var i = 0; i < entries.length; i++) {
|
|
98
|
-
var entry = entries[i];
|
|
99
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
100
|
-
var skillDir = path.join(base, entry.name);
|
|
101
|
-
var skillMd = path.join(skillDir, "SKILL.md");
|
|
102
|
-
try {
|
|
103
|
-
fs.accessSync(skillMd, fs.constants.R_OK);
|
|
104
|
-
// project skills override global skills with same name
|
|
105
|
-
skills[entry.name] = skillDir;
|
|
106
|
-
} catch (e) {
|
|
107
|
-
// no SKILL.md, skip
|
|
108
|
-
}
|
|
109
|
-
}
|
|
86
|
+
for (var i = 0; i < discovered.length; i++) {
|
|
87
|
+
skills[discovered[i].name] = discovered[i].path.replace(/\/SKILL\.md$/, "");
|
|
110
88
|
}
|
|
111
89
|
return skills;
|
|
112
90
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Results report queued sends, which can merge into fewer model turns.
|
|
2
|
+
function pendingSends(session, event) {
|
|
3
|
+
if (Number.isInteger(event.queuedTurnCount) && event.queuedTurnCount >= 0) {
|
|
4
|
+
var query = session.queryInstance;
|
|
5
|
+
var current = query && typeof query.getSubmittedMessageCount === "function" ? query.getSubmittedMessageCount() : event.submittedMessageCount;
|
|
6
|
+
var later = Number.isInteger(current) && Number.isInteger(event.submittedMessageCount)
|
|
7
|
+
? Math.max(0, current - event.submittedMessageCount) : 0;
|
|
8
|
+
return event.queuedTurnCount + later;
|
|
9
|
+
}
|
|
10
|
+
var answered = Number.isInteger(event.answeredUserMessageCount) && event.answeredUserMessageCount > 0 ? event.answeredUserMessageCount : 1;
|
|
11
|
+
return Math.max(0, (session._queuedTurnCount || 0) - answered + 1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { pendingSends: pendingSends };
|