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.
Files changed (151) hide show
  1. package/lib/claude-hook-installer.js +1 -267
  2. package/lib/claude-jsonl-watcher.js +1 -204
  3. package/lib/clay-skill-install.js +1 -33
  4. package/lib/cli-sessions.js +1 -300
  5. package/lib/codex-defaults.js +1 -28
  6. package/lib/context-usage.js +3 -2
  7. package/lib/daemon.js +5 -55
  8. package/lib/default-ai-runtime.js +4 -8
  9. package/lib/email-mcp-server.js +1 -1
  10. package/lib/kiro-defaults.js +1 -22
  11. package/lib/mcp-proxy.js +2 -32
  12. package/lib/mcp-query-bridge.js +1 -1
  13. package/lib/mcp-source-adapters.js +1 -91
  14. package/lib/mcp-source-context.js +1 -42
  15. package/lib/mcp-source-discovery.js +1 -169
  16. package/lib/mcp-source-plugins.js +1 -68
  17. package/lib/mcp-source-restrictions.js +1 -47
  18. package/lib/mcp-tool-policy.js +1 -26
  19. package/lib/os-user-diagnostics.js +2 -4
  20. package/lib/os-users.js +4 -65
  21. package/lib/project-connection.js +8 -7
  22. package/lib/project-http.js +4 -4
  23. package/lib/project-mcp.js +2 -2
  24. package/lib/project-pair-result-delivery.js +2 -2
  25. package/lib/project-pair-usage.js +12 -13
  26. package/lib/project-pending-message-queue.js +3 -1
  27. package/lib/project-session-history-actions.js +5 -3
  28. package/lib/project-session-pair.js +1 -1
  29. package/lib/project-session-runtime-actions.js +2 -1
  30. package/lib/project-session-spawn.js +2 -3
  31. package/lib/project-session-vendor.js +2 -1
  32. package/lib/project-sessions.js +18 -26
  33. package/lib/project-user-message.js +1 -0
  34. package/lib/project-vendor-login.js +6 -36
  35. package/lib/public/app.js +1 -0
  36. package/lib/public/css/messages.css +6 -2
  37. package/lib/public/modules/app-favicon.js +11 -2
  38. package/lib/public/modules/app-header.js +4 -2
  39. package/lib/public/modules/app-loop-ui.js +2 -0
  40. package/lib/public/modules/app-messages.js +36 -5
  41. package/lib/public/modules/app-notifications.js +1 -2
  42. package/lib/public/modules/app-panels.js +61 -123
  43. package/lib/public/modules/app-rate-limit.js +7 -27
  44. package/lib/public/modules/chat-render-runtime.js +3 -0
  45. package/lib/public/modules/mcp-connection-view.js +1 -1
  46. package/lib/public/modules/permission-control.js +5 -4
  47. package/lib/public/modules/scheduler.js +2 -0
  48. package/lib/public/modules/session-create-form.js +10 -10
  49. package/lib/public/modules/settings-defaults.js +27 -28
  50. package/lib/public/modules/tools.js +2 -1
  51. package/lib/public/modules/tui-grab.js +1 -2
  52. package/lib/public/modules/vendor-login-browser.js +5 -3
  53. package/lib/public/modules/vendor-login.js +2 -2
  54. package/lib/public/modules/vendor-priority.js +15 -1
  55. package/lib/public/muse-avatar.svg +4 -0
  56. package/lib/safe-bash-commands.js +3 -45
  57. package/lib/sdk-bridge.js +127 -395
  58. package/lib/sdk-foreground-admission.js +16 -0
  59. package/lib/sdk-mcp-descriptors.js +2 -37
  60. package/lib/sdk-message-processor.js +50 -17
  61. package/lib/sdk-quota-snapshot.js +1 -1
  62. package/lib/sdk-response-recovery.js +1 -155
  63. package/lib/sdk-worker.js +2 -677
  64. package/lib/server.js +1 -1
  65. package/lib/session-fork-history.js +21 -0
  66. package/lib/session-hygiene.js +3 -35
  67. package/lib/session-permission-mode.js +4 -3
  68. package/lib/session-skill-scope.js +11 -0
  69. package/lib/sessions.js +17 -89
  70. package/lib/tool-llm.js +1 -1
  71. package/lib/tui-transcript-index.js +1 -132
  72. package/lib/vendor-login-browser.js +1 -11
  73. package/lib/yoke/acp-agent-profiles.js +5 -3
  74. package/lib/yoke/adapters/antigravity.js +3 -14
  75. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  76. package/lib/yoke/adapters/claude.js +7 -42
  77. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  78. package/lib/yoke/adapters/codex.js +1 -0
  79. package/lib/yoke/adapters/kiro.js +3 -1
  80. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  81. package/lib/yoke/adapters/muse.js +122 -0
  82. package/lib/yoke/auth-events.js +17 -0
  83. package/lib/yoke/claude-hook-installer.js +267 -0
  84. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  85. package/lib/yoke/claude-maintenance.js +55 -0
  86. package/lib/yoke/claude-permission-patterns.js +53 -0
  87. package/lib/yoke/claude-response-recovery.js +155 -0
  88. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  89. package/lib/yoke/clay-skill-install.js +33 -0
  90. package/lib/yoke/cli-sessions.js +300 -0
  91. package/lib/yoke/codex-app-server.js +7 -12
  92. package/lib/yoke/configuration.js +13 -0
  93. package/lib/yoke/discovery.js +70 -0
  94. package/lib/yoke/errors.js +8 -0
  95. package/lib/yoke/host-environment.js +7 -0
  96. package/lib/yoke/index.js +9 -51
  97. package/lib/yoke/interface.js +116 -0
  98. package/lib/yoke/kiro-acp-server.js +5 -5
  99. package/lib/yoke/legacy-sessions.js +89 -0
  100. package/lib/yoke/login-protocol.js +25 -0
  101. package/lib/yoke/maintenance.js +12 -0
  102. package/lib/yoke/mcp-proxy.js +32 -0
  103. package/lib/yoke/mcp-source-adapters.js +91 -0
  104. package/lib/yoke/mcp-source-context.js +42 -0
  105. package/lib/yoke/mcp-source-discovery.js +169 -0
  106. package/lib/yoke/mcp-source-plugins.js +68 -0
  107. package/lib/yoke/mcp-source-restrictions.js +47 -0
  108. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  109. package/lib/yoke/model-preferences.js +6 -0
  110. package/lib/yoke/muse-artifacts.js +36 -0
  111. package/lib/yoke/muse-controls.js +50 -0
  112. package/lib/yoke/muse-input.js +64 -0
  113. package/lib/yoke/muse-items.js +157 -0
  114. package/lib/yoke/muse-policy.js +52 -0
  115. package/lib/yoke/muse-query.js +428 -0
  116. package/lib/yoke/muse-runtime.js +211 -0
  117. package/lib/yoke/muse-services.js +79 -0
  118. package/lib/yoke/muse-skills.js +79 -0
  119. package/lib/yoke/muse-turns.js +71 -0
  120. package/lib/yoke/options/acp.js +2 -0
  121. package/lib/yoke/options/antigravity.js +2 -0
  122. package/lib/yoke/options/claude.js +21 -0
  123. package/lib/yoke/options/codex-defaults.js +29 -0
  124. package/lib/yoke/options/codex-settings.js +21 -0
  125. package/lib/yoke/options/codex.js +6 -0
  126. package/lib/yoke/options/kiro-defaults.js +22 -0
  127. package/lib/yoke/options/kiro.js +5 -0
  128. package/lib/yoke/options/muse.js +5 -0
  129. package/lib/yoke/provider-contract.js +5 -0
  130. package/lib/yoke/providers/antigravity-executable.js +8 -0
  131. package/lib/yoke/providers/claude-executable.js +40 -0
  132. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  133. package/lib/yoke/providers/claude-process.js +97 -0
  134. package/lib/yoke/providers/claude-provision.js +75 -0
  135. package/lib/yoke/providers/codex-errors.js +8 -0
  136. package/lib/yoke/providers/muse-executable.js +17 -0
  137. package/lib/yoke/provisioning.js +6 -0
  138. package/lib/yoke/query-options.js +10 -0
  139. package/lib/yoke/response-recovery.js +41 -0
  140. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  141. package/lib/yoke/sdk-server-factory.js +7 -0
  142. package/lib/yoke/session-history.js +8 -0
  143. package/lib/yoke/session-tool-transport.js +31 -13
  144. package/lib/yoke/settings.js +23 -0
  145. package/lib/yoke/source-locations.js +4 -0
  146. package/lib/yoke/title-query.js +18 -7
  147. package/lib/yoke/tui-transcript-index.js +132 -0
  148. package/lib/yoke/usage.js +48 -0
  149. package/lib/yoke/user-input.js +13 -0
  150. package/lib/yoke/vendor-registry.js +81 -1
  151. package/package.json +3 -2
@@ -16,16 +16,14 @@ function contextStatus(session) {
16
16
  var used = null;
17
17
  var window = null;
18
18
  var source = "unavailable";
19
+ var measurement = "unavailable";
19
20
  if (snapshot && typeof snapshot === "object") {
20
- if (session.vendor === "claude") {
21
- used = firstNumber(snapshot.totalTokens);
22
- window = firstNumber(snapshot.maxTokens);
23
- if (used !== null || window !== null) source = "claude_sdk_context_usage";
24
- } else {
25
- used = firstNumber(snapshot.input_tokens);
26
- window = firstNumber(snapshot.contextWindow);
27
- if (used !== null || window !== null) source = "adapter_context_usage";
28
- }
21
+ var current = require("./yoke/usage").context(snapshot, session.vendor || "claude").context;
22
+ measurement = current.measurement;
23
+ used = firstNumber(current.usedTokens);
24
+ window = firstNumber(current.windowTokens);
25
+ if (used !== null || window !== null) source = current.source;
26
+
29
27
  }
30
28
  var history = session.history || [];
31
29
  var cumulativeInput = 0;
@@ -46,9 +44,9 @@ function contextStatus(session) {
46
44
  if (typeof item._ts === "number") lastCompactionAt = item._ts;
47
45
  }
48
46
  if (item.type !== "result") continue;
49
- var usage = item.usage || {};
50
- var input = firstNumber(usage.input_tokens, usage.inputTokens);
51
- var output = firstNumber(usage.output_tokens, usage.outputTokens);
47
+ var usage = require("./yoke/usage").accounting(item.usage) || {};
48
+ var input = firstNumber(usage.inputTokens);
49
+ var output = firstNumber(usage.outputTokens);
52
50
  if (input !== null) { cumulativeInput += input; observedInput = true; }
53
51
  if (output !== null) { cumulativeOutput += output; observedOutput = true; }
54
52
  results++;
@@ -59,13 +57,14 @@ function contextStatus(session) {
59
57
  if (used !== null && window !== null && used > window) {
60
58
  used = null;
61
59
  source = "unavailable";
60
+ measurement = "unavailable";
62
61
  }
63
62
  // Older result records contain lastStreamInputTokens, but that field was
64
63
  // populated from adapter snapshots that could be cumulative or inflated by
65
64
  // cache accounting. It is not safe evidence of current context occupancy.
66
65
  var ratio = used !== null && window !== null && window > 0 ? Math.min(1, Math.round((used / window) * 1000) / 1000) : null;
67
66
  return {
68
- current: { source: source, usedTokens: used, windowTokens: window, usedRatio: ratio,
67
+ current: { source: source, measurement: measurement, usedTokens: used, windowTokens: window, usedRatio: ratio,
69
68
  scope: used === null && window === null ? "unknown" : (used !== null && window !== null ? "current_context" : "partial_current_context") },
70
69
  cumulative: { inputTokens: observedInput ? cumulativeInput : null, outputTokens: observedOutput ? cumulativeOutput : null, tasksObserved: results, scope: "loaded_session_history", complete: false },
71
70
  lastTask: lastTask,
@@ -249,7 +249,8 @@ function createPendingMessageQueue(opts) {
249
249
  return true;
250
250
  }
251
251
  function consumeOne(session, dispatch) {
252
- if (loadError || !session || session._pendingMessageClaimId || isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested || session._queryStarting || session._awaitingTurnResult || session.isProcessing) return null;
252
+ var foreground = require('./sdk-foreground-admission');
253
+ if (loadError || !session || session._pendingMessageClaimId || isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested || session._queryStarting || (session._awaitingTurnResult || session.isProcessing) && !foreground.available(session)) return null;
253
254
  if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()) return null;
254
255
  for (var i = 0; i < data.items.length; i++) {
255
256
  var item = data.items[i];
@@ -262,6 +263,7 @@ function createPendingMessageQueue(opts) {
262
263
  data.revision++;
263
264
  try { persist(); } catch (e) { restore(before); return null; }
264
265
  session._pendingMessageClaimId = item.id;
266
+ foreground.take(session);
265
267
  broadcast(session, { type: "pending_message_claimed", sessionId: session.localId, projectSlug: opts.slug, paused: isPaused(session), id: item.id, revision: data.revision });
266
268
  var completed = false;
267
269
  function complete(ok, error) { if (completed) return false; completed = true; return finishClaim(session, item.id, ok, error); }
@@ -105,7 +105,10 @@ function attachSessionHistoryActions(ctx) {
105
105
  if (session.messageUUIDs[fi].uuid === uuid) { targetIdx = fi; break; }
106
106
  }
107
107
  var forkHistory = [];
108
- if (targetIdx >= 0) {
108
+ if (result.localHistoryEndIndex !== undefined) {
109
+ if (!Number.isInteger(result.localHistoryEndIndex) || result.localHistoryEndIndex < 0 || result.localHistoryEndIndex > session.history.length) throw new Error("The provider fork exists but its local history boundary is unavailable; inspect before retrying");
110
+ forkHistory = session.history.slice(0, result.localHistoryEndIndex);
111
+ } else if (targetIdx >= 0) {
109
112
  var trimTo = session.messageUUIDs[targetIdx].historyIndex;
110
113
  forkHistory = session.history.slice(0, trimTo);
111
114
  } else {
@@ -133,8 +136,7 @@ function attachSessionHistoryActions(ctx) {
133
136
  return forked;
134
137
  } else {
135
138
  // Read history from CLI session files
136
- var cliSess = require("./cli-sessions");
137
- var history = await cliSess.readCliSessionHistory(resolveSessionHome(session), cwd, result.sessionId);
139
+ var history = await require("./yoke/session-history").read(session.vendor || "claude", resolveSessionHome(session), cwd, result.sessionId);
138
140
  revalidate();
139
141
  var options = {
140
142
  history: history,
@@ -350,7 +350,7 @@ function attachSessionPair(ctx) {
350
350
  if (boundRoles && boundRoles.workers.indexOf(boundSession) !== -1) return taskControl.workerToolDefs(boundSession);
351
351
  var adHocSplit = !!(group && !group.pair);
352
352
  if (!driverEligibility.isEligibleDriverSession(boundSession, sm)) return [];
353
- var persistentCatalog = boundSession.vendor === "codex" && (!group || !!group.pair);
353
+ var persistentCatalog = require("./yoke/session-tool-transport").contract(boundSession.vendor || "claude").catalogLifetime === "conversation" && (!group || !!group.pair);
354
354
  if (!group && !persistentCatalog) {
355
355
  return workerProposal.getToolDefs(boundSession)
356
356
  .concat(workerPermission.getToolDefs(boundSession, { dormantDriver: true }));
@@ -11,7 +11,8 @@ function attachSessionRuntimeActions(ctx) {
11
11
  var vendor = session.vendor || ctx.sm.defaultVendor || "claude";
12
12
  var ids = Array.from(new Set([session.terminalId, session.runtimeTerminalId].filter(function (id) { return typeof id === "number"; })));
13
13
  if (!ids.length && (session.runtimeMode || session.mode || "gui") === "gui") return { status: "completed", runtimeMode: "gui", runtimeEffect: "none", threadPreserved: true };
14
- if (vendor !== "claude" || !session.cliSessionId) throw new Error("Only a legacy Claude terminal thread can return to GUI chat.");
14
+ var lifecycle = require("./yoke/provider-contract").resolve(vendor);
15
+ if (typeof lifecycle.supportsLegacyGuiResume !== "function" || !lifecycle.supportsLegacyGuiResume(session)) throw new Error("This provider cannot return a legacy terminal thread to GUI chat.");
15
16
  var closed = 0, persisted = false;
16
17
  var fields = ['terminalId', 'runtimeTerminalId'];
17
18
  var originalFields = fields.map(function (field) { return session[field]; });
@@ -1,6 +1,5 @@
1
1
  var yoke = require("./yoke");
2
2
  var sessionSpawnMcp = require("./session-spawn-mcp-server");
3
- var cliSessions = require("./cli-sessions");
4
3
  var os = require("os");
5
4
  var osUsers = require("./os-users");
6
5
 
@@ -161,7 +160,7 @@ function attachSessionSpawn(ctx) {
161
160
  var sm = ctx.sm;
162
161
  var queue = createSpawnQueue(SPAWN_CONCURRENCY);
163
162
  var adapters = ctx.adapters || {};
164
- var readCliSessionHistory = ctx.readCliSessionHistory || cliSessions.readCliSessionHistory;
163
+ var readCliSessionHistory = ctx.readCliSessionHistory || function(home, project, sessionId, provider) { return require("./yoke/session-history").read(provider || "claude", home, project, sessionId); };
165
164
 
166
165
  function resolveSessionHome(session) {
167
166
  var linuxUser = ctx.getLinuxUserForSession(session);
@@ -283,7 +282,7 @@ function attachSessionSpawn(ctx) {
283
282
  forkHistory = parent.history.slice();
284
283
  } else {
285
284
  forkHistory = await readCliSessionHistory(
286
- resolveSessionHome(parent), ctx.cwd, forkResult.sessionId
285
+ resolveSessionHome(parent), ctx.cwd, forkResult.sessionId, parent.vendor
287
286
  );
288
287
  }
289
288
  } catch (e) {
@@ -18,7 +18,8 @@ function attachSessionVendor(ctx) {
18
18
  session.vendor = vendor;
19
19
  session.model = null;
20
20
  session.effort = yoke.clampEffort(vendor, session.effort) || null;
21
- if (session.permissionMode === 'auto' && vendor !== 'claude') session.permissionMode = 'default';
21
+ var modes = yoke.sessionTools.contract(ctx.adapters[vendor]).permissionModes;
22
+ if (session.permissionMode === 'auto' && (!Array.isArray(modes) || modes.indexOf('auto') < 0)) session.permissionMode = 'default';
22
23
  session.effectivePermissionMode = null;
23
24
  try { if (ctx.sm.saveSessionFile(session) !== true) throw new Error('Could not persist the provider selection.'); }
24
25
  catch (error) { Object.assign(session, previous); throw error; }
@@ -2,7 +2,7 @@ var serverSettingsMessages = require("./project-server-settings");
2
2
  var fs = require("fs");
3
3
  var path = require("path");
4
4
  var { execFileSync } = require("child_process");
5
- var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
5
+ var providerSettings = require("./yoke/settings");
6
6
  var updateSnooze = require("./update-snooze");
7
7
  var yoke = require("./yoke");
8
8
  var sessionVisibility = require("./session-visibility");
@@ -258,7 +258,7 @@ function attachSessions(ctx) {
258
258
  if (!session || !session.cliSessionId) return;
259
259
  if (session._titleWatcherStop) return; // already watching
260
260
  var watcher;
261
- try { watcher = require("./claude-jsonl-watcher"); } catch (e) { return; }
261
+ try { watcher = require("./yoke/claude-jsonl-watcher"); } catch (e) { return; }
262
262
  var home = resolveSessionHome(session);
263
263
  var jsonlPath = watcher.jsonlPathFor(home, cwd, session.cliSessionId);
264
264
  if (!jsonlPath) return;
@@ -326,7 +326,7 @@ function attachSessions(ctx) {
326
326
  // message right away. The transcript is small enough that a full
327
327
  // re-send beats maintaining a delta protocol.
328
328
  try {
329
- var newIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
329
+ var newIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
330
330
  send({
331
331
  type: "tui_transcript_state",
332
332
  id: s.localId,
@@ -453,7 +453,7 @@ function attachSessions(ctx) {
453
453
  function prepareTuiSessionForGuiView(session, options) {
454
454
  if (!session || session.cliSessionId == null) return;
455
455
  var cliSess;
456
- try { cliSess = require("./cli-sessions"); } catch (e) { return; }
456
+ try { cliSess = require("./yoke/cli-sessions"); } catch (e) { return; }
457
457
  // Re-read whenever the jsonl has changed since the last hydrate (a TUI
458
458
  // turn appended messages), not just when history is empty. The earlier
459
459
  // "already hydrated" short-circuit kept stale history after a
@@ -603,7 +603,7 @@ function attachSessions(ctx) {
603
603
  var from = sm.findTurnBoundary(session.history, Math.max(0, targetFrom));
604
604
  var to = before;
605
605
  var items = session.history.slice(from, to).map(function (item, index) {
606
- return Object.assign({}, hydrateImageRefs(item), { _historyIndex: from + index });
606
+ return Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(item, session.vendor || "claude")), { _historyIndex: from + index });
607
607
  });
608
608
  sendTo(ws, {
609
609
  type: "history_prepend",
@@ -918,7 +918,7 @@ function attachSessions(ctx) {
918
918
  return true;
919
919
  }
920
920
  try {
921
- var tprIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
921
+ var tprIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
922
922
  sendTo(ws, {
923
923
  type: "tui_transcript_state",
924
924
  id: tprId,
@@ -1099,14 +1099,14 @@ function attachSessions(ctx) {
1099
1099
  var pid = msg.pid;
1100
1100
  if (!pid || typeof pid !== "number") return true;
1101
1101
  // Verify target is actually a claude process before killing
1102
- if (!sdk.isClaudeProcess(pid)) {
1103
- console.error("[project] Refused to kill PID " + pid + ": not a claude process");
1104
- sendTo(ws, { type: "error", text: "Process " + pid + " is not a Claude process." });
1102
+ if (!sdk.isProviderProcess(pid, getSessionForWs(ws))) {
1103
+ console.error("[project] Refused to kill PID " + pid + ": not a verified provider process");
1104
+ sendTo(ws, { type: "error", text: "Process " + pid + " is not a verified provider process." });
1105
1105
  return true;
1106
1106
  }
1107
1107
  try {
1108
1108
  process.kill(pid, "SIGTERM");
1109
- console.log("[project] Sent SIGTERM to conflicting Claude process PID " + pid);
1109
+ console.log("[project] Sent SIGTERM to conflicting provider process PID " + pid);
1110
1110
  sendTo(ws, { type: "process_killed", pid: pid });
1111
1111
  } catch (e) {
1112
1112
  console.error("[project] Failed to kill PID " + pid + ":", e.message);
@@ -1367,20 +1367,12 @@ function attachSessions(ctx) {
1367
1367
  return true;
1368
1368
  }
1369
1369
 
1370
- // Codex-specific settings (stored on sessionManager, passed to adapter via adapterOptions)
1371
- if (msg.type === "set_codex_approval") {
1372
- sm.codexApproval = getCodexConfig({ codexApproval: msg.approval }).approval;
1373
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1374
- return true;
1375
- }
1376
- if (msg.type === "set_codex_sandbox") {
1377
- sm.codexSandbox = msg.sandbox || CODEX_DEFAULTS.sandbox;
1378
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1379
- return true;
1380
- }
1381
- if (msg.type === "set_codex_websearch") {
1382
- sm.codexWebSearch = msg.webSearch || CODEX_DEFAULTS.webSearch;
1383
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1370
+ var providerChange;
1371
+ try { providerChange = providerSettings.change(msg, sm); }
1372
+ catch (error) { sendTo(ws, { type: "error", text: error.message }); return true; }
1373
+ if (providerChange) {
1374
+ Object.keys(providerChange.patch).forEach(function(key) { sm[key] = providerChange.patch[key]; });
1375
+ providerChange.messages.forEach(function(message) { send(message); });
1384
1376
  return true;
1385
1377
  }
1386
1378
 
@@ -1924,7 +1916,7 @@ function attachSessions(ctx) {
1924
1916
  var galManaged = [];
1925
1917
  var galUser = [];
1926
1918
  try {
1927
- var galInstaller = require("./claude-hook-installer");
1919
+ var galInstaller = require("./yoke/claude-hook-installer");
1928
1920
  galManaged = galInstaller.CLAY_MANAGED_ALLOW || [];
1929
1921
  } catch (e) {}
1930
1922
  if (galUid) {
@@ -1950,7 +1942,7 @@ function attachSessions(ctx) {
1950
1942
  // home (OS-mode: getent passwd; single-user: os.homedir()).
1951
1943
  var salEffective = false;
1952
1944
  try {
1953
- var salInstaller = require("./claude-hook-installer");
1945
+ var salInstaller = require("./yoke/claude-hook-installer");
1954
1946
  var salHome = null;
1955
1947
  if (osUsers && ws._clayUser && ws._clayUser.linuxUser) {
1956
1948
  try {
@@ -488,6 +488,7 @@ function attachUserMessage(ctx) {
488
488
  if (queuedResult.ok) {
489
489
  messageDelivery.acknowledge(ws, deliveryReceipt, null, session);
490
490
  sendTo(ws, { type: "message_queued", clientMessageId: msg.clientMessageId || null, sessionId: session.localId, projectSlug: slug, state: "pending", paused: pendingMessageQueue.isPaused(session), revision: pendingMessageQueue.getRevision() });
491
+ if (require('./sdk-foreground-admission').available(session)) setImmediate(function () { consumePendingMessage(session); });
491
492
  } else {
492
493
  sendTo(ws, { type: "error", text: queuedResult.error || "Unable to queue message" });
493
494
  }
@@ -25,37 +25,11 @@ var yoke = require("./yoke");
25
25
  // Login CLIs print a success line and then hand the shell back, so the PTY
26
26
  // itself does not exit. Watch the output stream instead. Deliberately narrow:
27
27
  // "Successfully logged out" and MCP-server logins must not match.
28
- var LOGIN_SUCCESS_PATTERNS = [
29
- /successfully logged in(?![ \t]+to[ \t]+mcp)/i,
30
- /logged in successfully/i,
31
- /login successful/i,
32
- /successfully authenticated/i,
33
- /authentication successful/i,
34
- /signed in successfully/i,
35
- /signed in with your [a-z ]+ account/i,
36
- /you(?:'re| are) (?:now )?(?:logged|signed) in/i,
37
- ];
38
-
39
- // Rolling window kept per flow so a success line split across PTY chunks is
40
- // still matched, without buffering the whole login transcript.
41
28
  var OUTPUT_TAIL_MAX = 4096;
42
-
43
- // Grace period between detecting success and killing the terminal, so the user
44
- // actually sees the confirmation line before the modal closes.
45
29
  var CLOSE_AFTER_SUCCESS_MS = 2000;
46
-
47
- var ANSI_PATTERN = /\x1b\[[0-9;?]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\r/g;
48
-
49
- function stripAnsi(text) {
50
- return String(text).replace(ANSI_PATTERN, "");
51
- }
52
-
53
- function looksLikeLoginSuccess(text) {
54
- for (var i = 0; i < LOGIN_SUCCESS_PATTERNS.length; i++) {
55
- if (LOGIN_SUCCESS_PATTERNS[i].test(text)) return true;
56
- }
57
- return false;
58
- }
30
+ var loginProtocol = require("./yoke/login-protocol");
31
+ var stripAnsi = loginProtocol.stripAnsi;
32
+ var looksLikeLoginSuccess = loginProtocol.successful;
59
33
 
60
34
  /**
61
35
  * ctx fields:
@@ -96,11 +70,7 @@ function attachVendorLogin(ctx) {
96
70
  cancel: function (vendor) { closeFlowTerminal(discardFlow(vendor)); broadcastState(); },
97
71
  });
98
72
 
99
- function loginCommandFor(vendor) {
100
- if (vendor === "claude") return "claude auth login";
101
- var info = vendorRegistry.getVendorInfo(vendor);
102
- return (info && info.loginCommand) || "claude login";
103
- }
73
+ function loginCommandFor(vendor) { return loginProtocol.command(vendor, false); }
104
74
 
105
75
  function vendorDisplayName(vendor) {
106
76
  var info = vendorRegistry.getVendorInfo(vendor);
@@ -284,8 +254,8 @@ function attachVendorLogin(ctx) {
284
254
  var key = flowKey(ws, vendor);
285
255
  var identity = resolveLoginIdentity(ws, msg);
286
256
  var command = loginCommandFor(vendor);
287
- var browserMode = msg.mode === "browser" && (vendor === "claude" || vendor === "codex");
288
- if (browserMode) command = vendor === "codex" ? "codex login" : "claude auth login";
257
+ var browserMode = msg.mode === "browser" && !!(vendorRegistry.getVendorInfo(vendor) || {}).browserLoginCommand;
258
+ if (browserMode) command = loginProtocol.command(vendor, true);
289
259
  var flow = {
290
260
  vendor: vendor,
291
261
  key: key,
package/lib/public/app.js CHANGED
@@ -583,6 +583,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
583
583
  // rendering
584
584
  currentMsgTs: null,
585
585
  richContextUsage: null,
586
+ contextMeasurement: "unavailable",
586
587
  currentMsgEl: null,
587
588
  currentFullText: "",
588
589
  matePreThinkingEl: null,
@@ -675,7 +675,8 @@ pre.mermaid-error {
675
675
 
676
676
  .thinking-live .thinking-chevron { display: none; }
677
677
 
678
- .thinking-live .thinking-label {
678
+ .thinking-live .thinking-label,
679
+ .activity-inline .activity-text.activity-state-label {
679
680
  background: linear-gradient(100deg, var(--text-muted) 40%, var(--text-dimmer) 50%, var(--text-muted) 60%);
680
681
  background-size: 300% 100%;
681
682
  background-clip: text;
@@ -690,7 +691,10 @@ pre.mermaid-error {
690
691
  }
691
692
 
692
693
  @media (prefers-reduced-motion: reduce) {
693
- .thinking-live .thinking-label { animation: none; background: none; color: var(--text-muted); }
694
+ .thinking-live .thinking-label,
695
+ .activity-inline .activity-text.activity-state-label {
696
+ animation: none; background: none; color: var(--text-muted); -webkit-text-fill-color: currentColor;
697
+ }
694
698
  }
695
699
 
696
700
  .thinking-header[aria-disabled="true"] { cursor: default; }
@@ -197,7 +197,7 @@ export function stopUrgentBlink() {
197
197
 
198
198
  export function setActivity(text) {
199
199
  var thinkingState = store.get('thinkingState');
200
- if (text === "thinking" && thinkingState && thinkingState.active) {
200
+ if ((text === "thinking" || text === "running") && thinkingState && thinkingState.active) {
201
201
  // Live Thinking already owns the tail progress UI. Compaction and tool
202
202
  // activity intentionally continue through so their distinct states show.
203
203
  if (getActivityEl()) {
@@ -225,9 +225,18 @@ export function setActivity(text) {
225
225
  _actEl.innerHTML =
226
226
  '<div class="mate-thinking-dots"><span></span><span></span><span></span></div>';
227
227
  }
228
+ _actEl.setAttribute("role", "status");
229
+ _actEl.setAttribute("aria-live", "polite");
228
230
  setActivityEl(_actEl);
229
- addToMessages(_actEl);
230
231
  }
232
+ var activity = getActivityEl();
233
+ var labels = { waiting: "Waiting for provider", running: "Working", background: "Background work pending" };
234
+ var label = activity.querySelector(".activity-state-label");
235
+ if (labels[text]) {
236
+ if (!label) { label = document.createElement("span"); label.className = "activity-state-label activity-text"; activity.appendChild(label); }
237
+ label.textContent = labels[text];
238
+ } else if (label) label.remove();
239
+ addToMessages(activity);
231
240
  scrollToBottom();
232
241
  } else {
233
242
  if (getActivityEl()) {
@@ -267,14 +267,16 @@ export function initHeader() {
267
267
  addRow("Local ID", split.panes[pi].sessionId);
268
268
  if (member && member.cliSessionId) {
269
269
  addRow("Session ID", member.cliSessionId);
270
- addRow("Resume", "claude --resume " + member.cliSessionId);
270
+ var memberResume = ((store.get("vendorInfo") || {})[member.vendor || "claude"] || {}).terminalResumeCommand;
271
+ if (memberResume) addRow("Resume", memberResume + " " + member.cliSessionId);
271
272
  }
272
273
  }
273
274
  } else {
274
275
  var s = store.snap();
275
276
  if (s.cliSessionId) addRow("Session ID", s.cliSessionId);
276
277
  if (s.activeSessionId) addRow("Local ID", s.activeSessionId);
277
- if (s.cliSessionId) addRow("Resume", "claude --resume " + s.cliSessionId);
278
+ var resume = ((s.vendorInfo || {})[s.currentVendor] || {}).terminalResumeCommand;
279
+ if (s.cliSessionId && resume) addRow("Resume", resume + " " + s.cliSessionId);
278
280
  }
279
281
 
280
282
  document.body.appendChild(pop);
@@ -657,6 +657,8 @@ function renderPreviewModelTab(bodyEl) {
657
657
  }
658
658
 
659
659
  var opts = {
660
+ effortLevels: ((store.get("vendorInfo") || {})[store.get("currentVendor")] || {}).effortLevels || [],
661
+ configuration: ((store.get("vendorInfo") || {})[store.get("currentVendor")] || {}).configuration || {},
660
662
  models: s.currentModels || [],
661
663
  currentModel: _previewLoopSettings.model || "",
662
664
  currentMode: _previewLoopSettings.permissionMode || "default",
@@ -282,7 +282,7 @@ export function processMessage(msg) {
282
282
 
283
283
  // Restore accurate context data from the last result in full history
284
284
  if (msg.lastUsage || msg.lastModelUsage) {
285
- accumulateContext(msg.lastCost, msg.lastUsage, msg.lastModelUsage, msg.lastStreamInputTokens);
285
+ accumulateContext(msg.lastCost, msg.lastUsage, msg.lastModelUsage, msg.lastStreamInputTokens, msg.context);
286
286
  }
287
287
  updateContextPanel();
288
288
  updateUsagePanel();
@@ -290,6 +290,7 @@ export function processMessage(msg) {
290
290
  // the restored turn instead of starting a second answer bubble.
291
291
  if (store.get('sessionIsProcessing')) {
292
292
  flushStreamBuffer();
293
+ if (store.get('turnActivity')) setActivity(store.get('turnActivity').phase);
293
294
  } else {
294
295
  markAllToolsDone();
295
296
  finalizeAssistantBlock();
@@ -912,7 +913,7 @@ export function processMessage(msg) {
912
913
  ? msg.runtimeTerminalId
913
914
  : (typeof msg.terminalId === "number" ? msg.terminalId : null);
914
915
  var _sessionRole = msg.sessionRole === "worker" ? "worker" : "driver";
915
- store.set({ activeSessionId: msg.id, sessionOriginId: msg.sessionOriginId || null, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || "", currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode'), effectivePermissionMode: msg.effectivePermissionMode || null, permissionCapabilities: msg.permissionCapabilities || { auto: false, mcpOverride: false }, mcpPermissionModeOverrides: msg.mcpPermissionModeOverrides || {}, activeSessionMateId: _sessionRole === "worker" ? null : (store.get('activeProjectMateId') || null), activeSessionMateName: _sessionRole === "worker" ? "" : (store.get('activeProjectMateName') || "") });
916
+ store.set({ activeSessionId: msg.id, sessionOriginId: msg.sessionOriginId || null, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || "", currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, turnActivity: null, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode'), effectivePermissionMode: msg.effectivePermissionMode || null, permissionCapabilities: msg.permissionCapabilities || { auto: false, mcpOverride: false }, mcpPermissionModeOverrides: msg.mcpPermissionModeOverrides || {}, activeSessionMateId: _sessionRole === "worker" ? null : (store.get('activeProjectMateId') || null), activeSessionMateName: _sessionRole === "worker" ? "" : (store.get('activeProjectMateName') || "") });
916
917
  handleScheduledTaskSessionSwitched(msg);
917
918
  requestLoopInterviewState();
918
919
  activateDeliverySession(msg.id, msg.cliSessionId || null, msg.sessionOriginId || null);
@@ -1039,7 +1040,11 @@ export function processMessage(msg) {
1039
1040
  // running session, where history_done keeps current tools active.
1040
1041
  // Question answers, plan feedback and Driver delegations are injected
1041
1042
  // mid-turn, so tools before them may still be running.
1042
- else if (!msg.askUserAnswer && !msg.planFeedback && !msg.delegated) markAllToolsDone();
1043
+ else if (!msg.askUserAnswer && !msg.planFeedback && !msg.delegated) {
1044
+ markAllToolsDone();
1045
+ // Older histories predate explicit foreground presentation boundaries.
1046
+ finalizeAssistantBlock();
1047
+ }
1043
1048
  resetThinkingGroup();
1044
1049
  if (msg.planContent) {
1045
1050
  setPlanContent(msg.planContent);
@@ -1136,7 +1141,7 @@ export function processMessage(msg) {
1136
1141
  // events make it through.
1137
1142
  removeMatePreThinking();
1138
1143
  if (!(store.get('dmMode') && store.get('dmTargetUser') && store.get('dmTargetUser').isMate) && !store.get('matePreThinkingEl')) {
1139
- setActivity("thinking");
1144
+ setActivity((store.get("turnActivity") || {}).phase || "thinking");
1140
1145
  }
1141
1146
  }
1142
1147
  break;
@@ -1172,8 +1177,24 @@ export function processMessage(msg) {
1172
1177
  }
1173
1178
  break;
1174
1179
 
1180
+ case "turn_activity":
1181
+ if (["waiting", "running", "background"].indexOf(msg.phase) === -1) break;
1182
+ store.set({ turnActivity: { phase: msg.phase, turnId: msg.turnId || null, disposition: msg.disposition || null } });
1183
+ if (!store.get('replayingHistory') && store.get('sessionIsProcessing')) {
1184
+ removeMatePreThinking();
1185
+ setActivity(msg.phase);
1186
+ }
1187
+ break;
1188
+
1189
+ case "assistant_boundary":
1190
+ // Foreground presentation ended; background work still owns its real
1191
+ // terminal. Flush pending animation text without finishing tools/Stop.
1192
+ finalizeAssistantBlock();
1193
+ break;
1194
+
1175
1195
  case "delta":
1176
1196
  if (typeof msg.text !== "string") break;
1197
+ store.set({ turnActivity: null });
1177
1198
  removeMatePreThinking();
1178
1199
  stopThinking();
1179
1200
  setActivity(null);
@@ -1221,6 +1242,8 @@ export function processMessage(msg) {
1221
1242
  renderMcpDebateProposal(msg.id, msg.input);
1222
1243
  startUrgentBlink();
1223
1244
  } else if (msg.name === "AskUserQuestion" && msg.input && msg.input.questions) {
1245
+ store.set({ turnActivity: null });
1246
+ setActivity(null);
1224
1247
  var askTool = getTools()[msg.id];
1225
1248
  if (askTool) {
1226
1249
  if (askTool.el) askTool.el.style.display = "none";
@@ -1301,6 +1324,7 @@ export function processMessage(msg) {
1301
1324
  break;
1302
1325
 
1303
1326
  case "permission_request":
1327
+ store.set({ turnActivity: null });
1304
1328
  setActivity(null);
1305
1329
  renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor, msg);
1306
1330
  startUrgentBlink();
@@ -1317,12 +1341,15 @@ export function processMessage(msg) {
1317
1341
  break;
1318
1342
 
1319
1343
  case "permission_request_pending":
1344
+ store.set({ turnActivity: null });
1320
1345
  setActivity(null);
1321
1346
  renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor, msg);
1322
1347
  startUrgentBlink();
1323
1348
  break;
1324
1349
 
1325
1350
  case "elicitation_request":
1351
+ store.set({ turnActivity: null });
1352
+ setActivity(null);
1326
1353
  renderElicitationRequest(msg);
1327
1354
  startUrgentBlink();
1328
1355
  break;
@@ -1337,6 +1364,8 @@ export function processMessage(msg) {
1337
1364
  break;
1338
1365
 
1339
1366
  case "user_dialog_request":
1367
+ store.set({ turnActivity: null });
1368
+ setActivity(null);
1340
1369
  renderUserDialogRequest(msg);
1341
1370
  startUrgentBlink();
1342
1371
  break;
@@ -1402,7 +1431,7 @@ export function processMessage(msg) {
1402
1431
  finalizeAssistantBlock();
1403
1432
  addTurnMeta(msg.cost, msg.duration);
1404
1433
  accumulateUsage(msg.cost, msg.usage);
1405
- accumulateContext(msg.cost, msg.usage, msg.modelUsage, msg.lastStreamInputTokens);
1434
+ accumulateContext(msg.cost, msg.usage, msg.modelUsage, msg.lastStreamInputTokens, msg.context);
1406
1435
  break;
1407
1436
 
1408
1437
  case "context_usage":
@@ -1415,6 +1444,7 @@ export function processMessage(msg) {
1415
1444
  break;
1416
1445
 
1417
1446
  case "done":
1447
+ store.set({ turnActivity: null });
1418
1448
  removeMatePreThinking();
1419
1449
  setActivity(null);
1420
1450
  finishThinkingTurn();
@@ -1438,6 +1468,7 @@ export function processMessage(msg) {
1438
1468
  break;
1439
1469
 
1440
1470
  case "error":
1471
+ store.set({ turnActivity: null });
1441
1472
  removeMatePreThinking();
1442
1473
  setActivity(null);
1443
1474
  finishThinkingTurn();
@@ -26,8 +26,7 @@ function getVendorLoginCommand(vendor) {
26
26
  var vendors = store.get('vendorInfo') || {};
27
27
  var info = vendors[vendor];
28
28
  if (info && info.loginCommand) return info.loginCommand;
29
- var fallbacks = { codex: "codex login --device-auth", claude: "claude login" };
30
- return fallbacks[vendor] || fallbacks.claude;
29
+ return "";
31
30
  }
32
31
 
33
32
  // --- Pending TUI attention tracking ---