clay-server 5.2.0-beta.5 → 5.2.0-beta.6
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/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-skill-install.js +1 -33
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +8 -7
- package/lib/project-http.js +4 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-user-message.js +1 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/public/app.js +1 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +11 -2
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-messages.js +36 -5
- package/lib/public/modules/app-notifications.js +1 -2
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +127 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server.js +1 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +17 -89
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/vendor-login-browser.js +1 -11
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +267 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// One next-message admission window, bound to the exact live provider query.
|
|
2
|
+
// This is not turn completion: processing and pending terminal counts survive.
|
|
3
|
+
function available(session) {
|
|
4
|
+
var credit = session && session._foregroundAdmission;
|
|
5
|
+
return !!(credit && session.queryInstance === credit.query
|
|
6
|
+
&& Number(session._sdkQueryGeneration || 0) === credit.generation
|
|
7
|
+
&& !session.taskStopRequested && !session._pendingMessageDrainPaused
|
|
8
|
+
&& !(session.abortController && session.abortController.signal.aborted));
|
|
9
|
+
}
|
|
10
|
+
function offer(session) {
|
|
11
|
+
if (!session.queryInstance || session.taskStopRequested || session.abortController && session.abortController.signal.aborted) return false;
|
|
12
|
+
session._foregroundAdmission = { query: session.queryInstance, generation: Number(session._sdkQueryGeneration || 0) };
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
function take(session) { delete session._foregroundAdmission; }
|
|
16
|
+
module.exports = { available: available, offer: offer, take: take };
|
|
@@ -1,37 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
function extractMcpDescriptors(mcpServers) {
|
|
4
|
-
if (!mcpServers) return null;
|
|
5
|
-
// Claude's adapter uses Zod v3 for JSON Schema tools; other servers use v4.
|
|
6
|
-
// Use the MCP SDK's version-aware converter for both kinds of registered tool.
|
|
7
|
-
var toJSONSchema = require("@modelcontextprotocol/sdk/server/zod-json-schema-compat.js").toJsonSchemaCompat;
|
|
8
|
-
var descriptors = [];
|
|
9
|
-
var names = Object.keys(mcpServers);
|
|
10
|
-
for (var i = 0; i < names.length; i++) {
|
|
11
|
-
var serverName = names[i];
|
|
12
|
-
var server = mcpServers[serverName];
|
|
13
|
-
if (!server || !server.instance || !server.instance._registeredTools) continue;
|
|
14
|
-
var tools = [];
|
|
15
|
-
var toolNames = Object.keys(server.instance._registeredTools);
|
|
16
|
-
for (var j = 0; j < toolNames.length; j++) {
|
|
17
|
-
var toolName = toolNames[j];
|
|
18
|
-
var toolDef = server.instance._registeredTools[toolName];
|
|
19
|
-
var inputSchema = { type: "object", properties: {} };
|
|
20
|
-
try {
|
|
21
|
-
if (toolDef._clayInputSchema) inputSchema = toolDef._clayInputSchema;
|
|
22
|
-
else if (toolDef.inputSchema) inputSchema = toJSONSchema(toolDef.inputSchema);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
throw new Error("Cannot serialize MCP tool schema " + serverName + "/" + toolName + ": " + e.message);
|
|
25
|
-
}
|
|
26
|
-
tools.push({
|
|
27
|
-
name: toolName,
|
|
28
|
-
description: toolDef.description || toolName,
|
|
29
|
-
inputSchema: inputSchema,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
if (tools.length > 0) descriptors.push({ serverName: serverName, tools: tools });
|
|
33
|
-
}
|
|
34
|
-
return descriptors.length > 0 ? descriptors : null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = { extractMcpDescriptors: extractMcpDescriptors };
|
|
1
|
+
// Compatibility export; SDK serialization is owned by YOKE.
|
|
2
|
+
module.exports = require("./yoke/sdk-mcp-descriptors");
|
|
@@ -42,9 +42,20 @@ function attachMessageProcessor(ctx) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function sendAndRecord(session, obj, clientObj) {
|
|
45
|
+
obj = require("./yoke/usage").clientEvent(obj, session.vendor || "claude");
|
|
45
46
|
sm.sendAndRecord(session, obj, clientObj);
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function recordTurnActivity(session, activity) {
|
|
50
|
+
// An authoritative input card owns progress until its request settles.
|
|
51
|
+
var awaitingInput = ["pendingPermissions", "pendingAskUser", "pendingElicitations", "pendingUserDialogs"].some(function (key) {
|
|
52
|
+
return Object.keys(session[key] || {}).length > 0;
|
|
53
|
+
});
|
|
54
|
+
if (!awaitingInput && ["waiting", "running", "background"].indexOf(activity.phase) !== -1) sendAndRecord(session, {
|
|
55
|
+
type: "turn_activity", phase: activity.phase, turnId: activity.turnId || null, disposition: activity.disposition || null
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
function parseGeneratedImage(parsed) {
|
|
49
60
|
var data = parsed.data || "";
|
|
50
61
|
var mediaType = parsed.mediaType || "image/png";
|
|
@@ -77,8 +88,8 @@ function attachMessageProcessor(ctx) {
|
|
|
77
88
|
|| !!authLinuxUser
|
|
78
89
|
|| (authUser && authUser.role === "admin");
|
|
79
90
|
var vendorInfo = yoke.getVendorInfo(session.vendor);
|
|
80
|
-
var authTitle = ((vendorInfo && vendorInfo.displayName) || "
|
|
81
|
-
var loginCommand = (vendorInfo && vendorInfo.loginCommand) || "
|
|
91
|
+
var authTitle = ((vendorInfo && vendorInfo.displayName) || "The provider") + " is not logged in.";
|
|
92
|
+
var loginCommand = (vendorInfo && vendorInfo.loginCommand) || "";
|
|
82
93
|
var _nmLogin = getNotificationsModule();
|
|
83
94
|
// Authentication recovery is transient UI state, not transcript content.
|
|
84
95
|
// Recording it makes history replay look like a fresh auth failure and
|
|
@@ -163,6 +174,7 @@ function attachMessageProcessor(ctx) {
|
|
|
163
174
|
}
|
|
164
175
|
|
|
165
176
|
function processSDKMessage(session, parsed) {
|
|
177
|
+
parsed = require("./yoke/auth-events").normalize(parsed, session.responsePreview, session.vendor || "claude");
|
|
166
178
|
// Timing: log key SDK milestones relative to query start
|
|
167
179
|
if (session._queryStartTs) {
|
|
168
180
|
var _elapsed = Date.now() - session._queryStartTs;
|
|
@@ -378,7 +390,7 @@ function attachMessageProcessor(ctx) {
|
|
|
378
390
|
}) : [];
|
|
379
391
|
sendAndRecord(session, {
|
|
380
392
|
type: "tool_executing",
|
|
381
|
-
id: parsed.turnId || "
|
|
393
|
+
id: parsed.turnId || "provider-plan",
|
|
382
394
|
name: "TodoWrite",
|
|
383
395
|
input: {
|
|
384
396
|
todos: todos,
|
|
@@ -546,7 +558,32 @@ function attachMessageProcessor(ctx) {
|
|
|
546
558
|
}
|
|
547
559
|
}
|
|
548
560
|
|
|
561
|
+
} else if (parsed.yokeType === "turn_activity") {
|
|
562
|
+
recordTurnActivity(session, parsed);
|
|
563
|
+
} else if (parsed.yokeType === "foreground_available") {
|
|
564
|
+
// A provider observed an admission boundary while background work still
|
|
565
|
+
// owns its terminal. Do not synthesize success or clear processing/tasks.
|
|
566
|
+
// Persist the presentation boundary before a synchronous next admission.
|
|
567
|
+
sendAndRecord(session, { type: "assistant_boundary" });
|
|
568
|
+
recordTurnActivity(session, { phase: "background" });
|
|
569
|
+
if (require('./sdk-foreground-admission').offer(session) && opts && typeof opts.onSessionAvailable === 'function') opts.onSessionAvailable(session);
|
|
549
570
|
} else if (parsed.yokeType === "result") {
|
|
571
|
+
if (parsed.backgroundOnly === true) {
|
|
572
|
+
// Observed terminal of an older foreground turn. Account for the
|
|
573
|
+
// outstanding send without settling a newer turn's reviews/callbacks.
|
|
574
|
+
session._queuedTurnCount = turnQueue.pendingSends(session, parsed);
|
|
575
|
+
if (session._queuedTurnCount > 0) session._queuedTurnCount--;
|
|
576
|
+
else {
|
|
577
|
+
session.isProcessing = false;
|
|
578
|
+
session._awaitingTurnResult = false;
|
|
579
|
+
onProcessingChanged();
|
|
580
|
+
sendAndRecord(session, { type: "done", code: 0 });
|
|
581
|
+
}
|
|
582
|
+
sendAndRecord(session, { type: "background_result", usage: require('./yoke/usage').accounting(parsed.usage), cost: parsed.cost, duration: parsed.duration });
|
|
583
|
+
sm.broadcastSessionList();
|
|
584
|
+
if (opts && typeof opts.onSessionAvailable === 'function') opts.onSessionAvailable(session);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
550
587
|
// SDK 0.3.274 batches queued background-task completion wakes into one
|
|
551
588
|
// model call. Every wake still emits a result, but all results before
|
|
552
589
|
// the final wake are successful task-notification-origin placeholders
|
|
@@ -616,7 +653,7 @@ function attachMessageProcessor(ctx) {
|
|
|
616
653
|
session._queuedTurnCount = 0;
|
|
617
654
|
taskLifecycle.clearAll(session);
|
|
618
655
|
onProcessingChanged();
|
|
619
|
-
sendAndRecord(session, { type: "error", text: "
|
|
656
|
+
sendAndRecord(session, { type: "error", text: "The provider failed to start: " + parsed.startupFailureReason });
|
|
620
657
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
621
658
|
sm.broadcastSessionList();
|
|
622
659
|
return;
|
|
@@ -657,12 +694,6 @@ function attachMessageProcessor(ctx) {
|
|
|
657
694
|
onProcessingChanged();
|
|
658
695
|
}
|
|
659
696
|
// Recognize only short, zero-cost CLI login responses, not ordinary prose.
|
|
660
|
-
var previewTrimmed = (session.responsePreview || "").trim();
|
|
661
|
-
var isZeroCost = !parsed.cost || parsed.cost === 0;
|
|
662
|
-
var isLoginPrompt = isZeroCost && previewTrimmed.length < 100
|
|
663
|
-
&& ((/not logged in/i.test(previewTrimmed) && /\/login/i.test(previewTrimmed))
|
|
664
|
-
|| ((session.vendor || "claude") === "claude"
|
|
665
|
-
&& /^Failed to authenticate: OAuth session expired and could not be refreshed\.?$/i.test(previewTrimmed)));
|
|
666
697
|
// Fetch rich context usage breakdown (fire-and-forget, non-blocking)
|
|
667
698
|
if (session.queryInstance && typeof session.queryInstance.getContextUsage === "function") {
|
|
668
699
|
var contextQuery = session.queryInstance;
|
|
@@ -684,7 +715,8 @@ function attachMessageProcessor(ctx) {
|
|
|
684
715
|
type: "result",
|
|
685
716
|
cost: parsed.cost,
|
|
686
717
|
duration: parsed.duration,
|
|
687
|
-
usage: parsed.usage
|
|
718
|
+
usage: require("./yoke/usage").accounting(parsed.usage),
|
|
719
|
+
context: require("./yoke/usage").turnContext(Object.assign({}, parsed, { lastStreamInputTokens: lastStreamInput })),
|
|
688
720
|
modelUsage: parsed.modelUsage || null,
|
|
689
721
|
sessionId: parsed.sessionId,
|
|
690
722
|
lastStreamInputTokens: lastStreamInput,
|
|
@@ -699,7 +731,7 @@ function attachMessageProcessor(ctx) {
|
|
|
699
731
|
}
|
|
700
732
|
// Recover both missing login and expired OAuth sessions from the SDK.
|
|
701
733
|
// This is a short canned response with zero cost, not actual AI output.
|
|
702
|
-
if (
|
|
734
|
+
if (parsed.authenticationRequired === true) {
|
|
703
735
|
emitAuthRequired(session);
|
|
704
736
|
}
|
|
705
737
|
sendAndRecord(session, { type: "done", code: 0 });
|
|
@@ -887,7 +919,7 @@ function attachMessageProcessor(ctx) {
|
|
|
887
919
|
resetsAt: info.resetsAt ? info.resetsAt * 1000 : null,
|
|
888
920
|
rateLimitType: info.rateLimitType || null,
|
|
889
921
|
utilization: info.utilization || null,
|
|
890
|
-
isUsingOverage: info.isUsingOverage === true
|
|
922
|
+
isUsingOverage: info.isUsingOverage === true,
|
|
891
923
|
overageEnabled: typeof info.overageEnabled === "boolean" ? info.overageEnabled : null,
|
|
892
924
|
overageStatus: info.overageStatus || null,
|
|
893
925
|
overageResetsAt: info.overageResetsAt ? info.overageResetsAt * 1000 : null,
|
|
@@ -979,6 +1011,7 @@ function attachMessageProcessor(ctx) {
|
|
|
979
1011
|
}
|
|
980
1012
|
|
|
981
1013
|
} else if (parsed.yokeType === "commands_changed") {
|
|
1014
|
+
if (Array.isArray(parsed.skills)) session.skillNames = parsed.skills.slice();
|
|
982
1015
|
// Mid-session command list change: rebuild the union with skills (same as
|
|
983
1016
|
// init) and push a full replacement to the client.
|
|
984
1017
|
var cmdSeen = new Set();
|
|
@@ -1053,12 +1086,12 @@ function attachMessageProcessor(ctx) {
|
|
|
1053
1086
|
// processQueryStream can finish the turn when the iterator closes.
|
|
1054
1087
|
var adapterErrorText = parsed.text || parsed.message || parsed.error || "Agent runtime error";
|
|
1055
1088
|
session._lastAdapterError = adapterErrorText;
|
|
1056
|
-
var
|
|
1057
|
-
if (
|
|
1089
|
+
var classifiedError = require("./yoke/errors").classify(session.vendor || "claude", adapterErrorText);
|
|
1090
|
+
if (classifiedError && classifiedError.kind === "session_writer_conflict") {
|
|
1058
1091
|
sendAndRecord(session, {
|
|
1059
1092
|
type: "session_writer_conflict",
|
|
1060
|
-
vendor: session.vendor || "
|
|
1061
|
-
text:
|
|
1093
|
+
vendor: session.vendor || "claude",
|
|
1094
|
+
text: classifiedError.text,
|
|
1062
1095
|
});
|
|
1063
1096
|
} else {
|
|
1064
1097
|
sendAndRecord(session, { type: "error", text: adapterErrorText });
|
|
@@ -9,6 +9,7 @@ function recordQuotaSnapshot(session, info, sendAndRecord) {
|
|
|
9
9
|
return Object.assign({}, window, { resetsAt: window.resetsAt ? window.resetsAt * 1000 : null });
|
|
10
10
|
}),
|
|
11
11
|
};
|
|
12
|
+
if (Number.isFinite(info.observedAtMs)) message.observedAtMs = info.observedAtMs;
|
|
12
13
|
var snapshots = session._quotaSnapshots || (session._quotaSnapshots = Object.create(null));
|
|
13
14
|
var key = JSON.stringify([session.ownerId, session.vendor, info.limitId]);
|
|
14
15
|
var serialized = JSON.stringify(message);
|
|
@@ -20,7 +21,6 @@ function recordQuotaSnapshot(session, info, sendAndRecord) {
|
|
|
20
21
|
function attachQuotaSnapshots(sm) {
|
|
21
22
|
if (typeof sm.addOnSessionViewed !== "function") return;
|
|
22
23
|
sm.addOnSessionViewed(function (session, ws, send) {
|
|
23
|
-
if (session.vendor !== "codex") return;
|
|
24
24
|
var seen = Object.create(null);
|
|
25
25
|
var history = session.history || [];
|
|
26
26
|
// A deduplicated snapshot may precede the initial page of chat history.
|
|
@@ -1,155 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function attachResponseRecovery(ctx) {
|
|
3
|
-
var states = new Map();
|
|
4
|
-
var schedule = ctx.setTimeout || setTimeout;
|
|
5
|
-
var unschedule = ctx.clearTimeout || clearTimeout;
|
|
6
|
-
var waitMs = ctx.waitMs || 180000;
|
|
7
|
-
var stopMs = ctx.stopMs || 10000;
|
|
8
|
-
|
|
9
|
-
function live(session, state) {
|
|
10
|
-
return states.get(session) === state && !session.destroying && !session.taskStopRequested
|
|
11
|
-
&& state.ownerId === session.ownerId && state.vendor === session.vendor
|
|
12
|
-
&& (!ctx.sessions || ctx.sessions.get(session.localId) === session);
|
|
13
|
-
}
|
|
14
|
-
function clearTimer(state) {
|
|
15
|
-
if (state.timer) unschedule(state.timer);
|
|
16
|
-
state.timer = null;
|
|
17
|
-
}
|
|
18
|
-
function cancel(session, expected) {
|
|
19
|
-
var state = states.get(session);
|
|
20
|
-
if (expected && state !== expected) return;
|
|
21
|
-
if (state) clearTimer(state);
|
|
22
|
-
states.delete(session);
|
|
23
|
-
}
|
|
24
|
-
function protectedWait(session) {
|
|
25
|
-
return [session.pendingPermissions, session.pendingAskUser, session.pendingElicitations,
|
|
26
|
-
session.pendingUserDialogs, session.activeTaskToolIds].some(function(map) {
|
|
27
|
-
return map && Object.keys(map).length > 0;
|
|
28
|
-
}) || (session.activeBackgroundTasks && session.activeBackgroundTasks.length > 0);
|
|
29
|
-
}
|
|
30
|
-
function arm(session, state) {
|
|
31
|
-
clearTimer(state);
|
|
32
|
-
if (!state.turns.length || state.turns[0].progress || state.stopping) return;
|
|
33
|
-
state.timer = schedule(function() { expire(session, state); }, waitMs);
|
|
34
|
-
if (state.timer && state.timer.unref) state.timer.unref();
|
|
35
|
-
}
|
|
36
|
-
function snapshot(payload) {
|
|
37
|
-
return Object.assign({}, payload, { images: payload.images ? JSON.parse(JSON.stringify(payload.images)) : payload.images });
|
|
38
|
-
}
|
|
39
|
-
function begin(session, payload, retryState) {
|
|
40
|
-
if (retryState) return retryState;
|
|
41
|
-
cancel(session);
|
|
42
|
-
var state = { turns: [snapshot(payload)], timer: null, stopping: false, retried: false, ownerId: session.ownerId, vendor: session.vendor };
|
|
43
|
-
states.set(session, state);
|
|
44
|
-
arm(session, state);
|
|
45
|
-
return state;
|
|
46
|
-
}
|
|
47
|
-
function enqueue(session, payload) {
|
|
48
|
-
var state = states.get(session);
|
|
49
|
-
if (!state) return begin(session, payload);
|
|
50
|
-
state.turns.push(snapshot(payload));
|
|
51
|
-
return state;
|
|
52
|
-
}
|
|
53
|
-
function progress(session) {
|
|
54
|
-
var state = states.get(session);
|
|
55
|
-
if (!state || state.stopping || !state.turns.length) return;
|
|
56
|
-
state.turns[0].progress = true;
|
|
57
|
-
clearTimer(state);
|
|
58
|
-
}
|
|
59
|
-
function event(session, msg) {
|
|
60
|
-
var state = states.get(session);
|
|
61
|
-
if (!state || state.stopping || !msg) return;
|
|
62
|
-
var type = msg.yokeType;
|
|
63
|
-
if (type === 'error' || type === 'interrupted') { cancel(session); return; }
|
|
64
|
-
if (type === 'result') {
|
|
65
|
-
if (msg.subtype === 'success' && !msg.is_error && msg.numTurns === 0
|
|
66
|
-
&& msg.origin && msg.origin.kind === 'task-notification'
|
|
67
|
-
&& !msg.localCommand && msg.resultText === '') return;
|
|
68
|
-
var remaining = require('./sdk-turn-queue').pendingSends(session, msg);
|
|
69
|
-
state.turns = remaining ? state.turns.slice(-remaining) : [];
|
|
70
|
-
state.retried = false;
|
|
71
|
-
arm(session, state);
|
|
72
|
-
if (!state.turns.length) cancel(session);
|
|
73
|
-
} else if ((type === 'text_delta' || type === 'thinking_delta') && (msg.text || msg.delta)
|
|
74
|
-
|| type === 'tool_start' || type === 'tool_input_delta' || type === 'tool_progress'
|
|
75
|
-
|| type === 'task_started' || type === 'task_progress' || type === 'subagent_message'
|
|
76
|
-
|| type === 'message' && msg.messageRole === 'assistant' && msg.content && msg.content.length) {
|
|
77
|
-
progress(session);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function finish(session, state, text) {
|
|
81
|
-
if (!live(session, state)) { cancel(session, state); return; }
|
|
82
|
-
cancel(session);
|
|
83
|
-
// Fence late output when a transport cannot confirm termination.
|
|
84
|
-
session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
|
|
85
|
-
session.isProcessing = false;
|
|
86
|
-
session._awaitingTurnResult = false;
|
|
87
|
-
session._queuedTurnCount = 0;
|
|
88
|
-
ctx.changed();
|
|
89
|
-
ctx.record(session, { type: 'thinking_stop' });
|
|
90
|
-
ctx.record(session, { type: 'error', text: text });
|
|
91
|
-
ctx.record(session, { type: 'done', code: 1 });
|
|
92
|
-
ctx.broadcast();
|
|
93
|
-
}
|
|
94
|
-
async function expire(session, state) {
|
|
95
|
-
clearTimer(state);
|
|
96
|
-
if (!live(session, state)) { cancel(session, state); return; }
|
|
97
|
-
// Never stop a known tool or a human decision because prose is absent.
|
|
98
|
-
if (protectedWait(session)) { progress(session); return; }
|
|
99
|
-
state.stopping = true;
|
|
100
|
-
ctx.record(session, { type: 'info', text: 'Claude has not responded for three minutes. Stopping this attempt safely before retrying.' });
|
|
101
|
-
var timeout;
|
|
102
|
-
var stopped = false;
|
|
103
|
-
try {
|
|
104
|
-
var stopAttempt = (async function() {
|
|
105
|
-
var handle = session.queryInstance || state.handle;
|
|
106
|
-
if (!handle && session.abortController) session.abortController.abort();
|
|
107
|
-
if (!handle && state.starting) await state.starting;
|
|
108
|
-
handle = handle || session.queryInstance || state.handle;
|
|
109
|
-
if (handle) {
|
|
110
|
-
if (typeof handle.stopAndWait !== 'function') return false;
|
|
111
|
-
await handle.stopAndWait();
|
|
112
|
-
if (session.streamPromise) await session.streamPromise;
|
|
113
|
-
}
|
|
114
|
-
return true;
|
|
115
|
-
})();
|
|
116
|
-
session._responseRecoveryStopPending = stopAttempt;
|
|
117
|
-
stopAttempt.then(function(confirmed) {
|
|
118
|
-
if (confirmed && session._responseRecoveryStopPending === stopAttempt) session._responseRecoveryStopPending = null;
|
|
119
|
-
}, function() {});
|
|
120
|
-
stopped = await Promise.race([
|
|
121
|
-
stopAttempt,
|
|
122
|
-
new Promise(function(resolve) { timeout = schedule(function() { resolve(false); }, stopMs); }),
|
|
123
|
-
]);
|
|
124
|
-
} catch (e) { stopped = false; }
|
|
125
|
-
if (timeout) unschedule(timeout);
|
|
126
|
-
if (!live(session, state)) { cancel(session, state); return; }
|
|
127
|
-
if (!stopped) {
|
|
128
|
-
finish(session, state, 'Claude could not be confirmed stopped. Automatic retry was cancelled to avoid running your request twice. Check your Claude connection before trying again.');
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (state.retried || state.turns.length !== 1) {
|
|
132
|
-
finish(session, state, state.retried
|
|
133
|
-
? 'Claude still has not responded after one automatic retry. Please send your message again or check your Claude connection.'
|
|
134
|
-
: 'Claude did not respond. Several messages were queued, so automatic replay was cancelled to avoid duplicating work. Please send the unanswered messages again.');
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
state.retried = true;
|
|
138
|
-
state.stopping = false;
|
|
139
|
-
session.isProcessing = true;
|
|
140
|
-
ctx.changed();
|
|
141
|
-
ctx.record(session, { type: 'info', text: 'The previous attempt has stopped. Retrying your original message once.' });
|
|
142
|
-
ctx.send(session, { type: 'status', status: 'processing' });
|
|
143
|
-
arm(session, state);
|
|
144
|
-
var payload = state.turns[0];
|
|
145
|
-
await ctx.restart(session, payload, state);
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
begin: begin, enqueue: enqueue, event: event, progress: progress, cancel: cancel,
|
|
149
|
-
current: function(session) { return states.get(session); },
|
|
150
|
-
valid: live,
|
|
151
|
-
stopping: function(session) { var state = states.get(session); return !!(state && state.stopping); },
|
|
152
|
-
dispose: function() { states.forEach(function(state) { clearTimer(state); }); states.clear(); },
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
module.exports = { attachResponseRecovery: attachResponseRecovery };
|
|
1
|
+
module.exports = require("./yoke/claude-response-recovery");
|