clay-server 5.4.0 → 5.5.0-beta.2

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 (79) hide show
  1. package/lib/agent-behavior-gate.js +223 -0
  2. package/lib/agent-behavior-schema.js +184 -0
  3. package/lib/daemon.js +15 -0
  4. package/lib/issues-mcp-server.js +16 -3
  5. package/lib/migrate-single-user.js +2 -0
  6. package/lib/project-issues.js +9 -2
  7. package/lib/project-log-feedback-delivery.js +3 -0
  8. package/lib/project-logs-mcp-server.js +23 -2
  9. package/lib/project-logs.js +40 -6
  10. package/lib/project-session-document.js +9 -1
  11. package/lib/project-session-notes.js +19 -8
  12. package/lib/project-session-pair-prompt.js +5 -4
  13. package/lib/project-user-knowledge.js +21 -3
  14. package/lib/project-worker-proposal.js +8 -2
  15. package/lib/project.js +108 -61
  16. package/lib/public/app.js +1 -0
  17. package/lib/public/css/agent-behavior.css +101 -0
  18. package/lib/public/modules/agent-behavior-nav.js +90 -0
  19. package/lib/public/modules/agent-behavior-view.js +235 -0
  20. package/lib/public/modules/agent-behavior.js +200 -0
  21. package/lib/public/modules/settings-shell-registry.js +1 -1
  22. package/lib/public/modules/settings-shell-view.js +18 -0
  23. package/lib/public/modules/sidebar-sessions.js +3 -0
  24. package/lib/public/modules/split-view-arrange-menu.js +38 -0
  25. package/lib/public/modules/split-view-create-menu.js +9 -5
  26. package/lib/public/modules/split-view-member-actions.js +42 -0
  27. package/lib/public/modules/split-view-picker-actions.js +2 -2
  28. package/lib/public/modules/split-view-presentation-renderer.js +10 -35
  29. package/lib/public/modules/split-view-presentations.js +9 -8
  30. package/lib/public/modules/split-view-sidebar-menu.js +2 -1
  31. package/lib/public/modules/user-settings-layout.js +8 -1
  32. package/lib/public/modules/user-settings.js +5 -2
  33. package/lib/public/modules/view-workspace-state.js +1 -1
  34. package/lib/public/pane-assets/{app-BDYH6Y7R.js → app-SEMVZ4AY.js} +1 -1
  35. package/lib/public/pane-assets/{chunk-U5GH2UJM.js → chunk-4YE4RU27.js} +1 -1
  36. package/lib/public/pane-assets/{chunk-E6NRPGVH.js → chunk-5UBN6GR7.js} +1 -1
  37. package/lib/public/pane-assets/chunk-6AGSOAOD.js +1 -0
  38. package/lib/public/pane-assets/chunk-AATAIAXL.js +1 -0
  39. package/lib/public/pane-assets/{chunk-42C2CZEP.js → chunk-BNNPJJVG.js} +1 -1
  40. package/lib/public/pane-assets/{chunk-HWLBH6AL.js → chunk-C6P7PICY.js} +1 -1
  41. package/lib/public/pane-assets/{chunk-KB4T5B2R.js → chunk-CQNPEH2M.js} +1 -1
  42. package/lib/public/pane-assets/chunk-D3CE5FZL.js +1 -0
  43. package/lib/public/pane-assets/{chunk-N5SIEIOG.js → chunk-E4VM2IDB.js} +1 -1
  44. package/lib/public/pane-assets/{chunk-VCNLKOVT.js → chunk-FK3YLGAP.js} +1 -1
  45. package/lib/public/pane-assets/chunk-GKSLBGVE.js +168 -0
  46. package/lib/public/pane-assets/chunk-J5PTSKJ6.js +1 -0
  47. package/lib/public/pane-assets/{chunk-XI2DJM4H.js → chunk-K4PAN77B.js} +1 -1
  48. package/lib/public/pane-assets/{chunk-UMIYQYGM.js → chunk-M5CWIIIT.js} +1 -1
  49. package/lib/public/pane-assets/{chunk-KF3LZRJK.js → chunk-QBCM7QEA.js} +1 -1
  50. package/lib/public/pane-assets/chunk-QPBO6ID4.js +1 -0
  51. package/lib/public/pane-assets/{chunk-TMUCEZ7C.js → chunk-QWQB2YRF.js} +1 -1
  52. package/lib/public/pane-assets/chunk-RA6VUJTY.js +1 -0
  53. package/lib/public/pane-assets/{chunk-TT3NECKK.js → chunk-U7EHYRJZ.js} +1 -1
  54. package/lib/public/pane-assets/chunk-XOZUM5ET.js +1 -0
  55. package/lib/public/pane-assets/{chunk-X452YCE5.js → chunk-XYZGVYWW.js} +1 -1
  56. package/lib/public/pane-assets/{chunk-QRVKAVH7.js → chunk-YR5GH3GI.js} +1 -1
  57. package/lib/public/pane-assets/{chunk-YE45RR7W.js → chunk-YVVT3CTV.js} +1 -1
  58. package/lib/public/pane-assets/manifest.json +394 -388
  59. package/lib/public/pane-assets/{style-VDIGFFLR.css → style-DZX6JAMF.css} +1 -1
  60. package/lib/public/style.css +1 -0
  61. package/lib/sdk-bridge.js +3 -2
  62. package/lib/server-agent-behavior-settings.js +72 -0
  63. package/lib/server-settings.js +2 -0
  64. package/lib/server.js +9 -0
  65. package/lib/session-notes-mcp-server.js +27 -7
  66. package/lib/session-skill-scope.js +2 -2
  67. package/lib/users-agent-behavior-preferences.js +38 -0
  68. package/lib/users.js +6 -0
  69. package/lib/yoke/adapters/claude.js +8 -4
  70. package/lib/yoke/skill-discovery.js +46 -1
  71. package/package.json +1 -1
  72. package/lib/public/pane-assets/chunk-46OOGVHU.js +0 -1
  73. package/lib/public/pane-assets/chunk-4OJW4FF4.js +0 -1
  74. package/lib/public/pane-assets/chunk-F7VIE6FJ.js +0 -1
  75. package/lib/public/pane-assets/chunk-FCJYJLVZ.js +0 -1
  76. package/lib/public/pane-assets/chunk-FEJDEXOW.js +0 -1
  77. package/lib/public/pane-assets/chunk-FQQMXMH7.js +0 -168
  78. package/lib/public/pane-assets/chunk-HEMCIILG.js +0 -1
  79. package/lib/public/pane-assets/chunk-RV5O66TO.js +0 -1
@@ -93,6 +93,7 @@
93
93
 
94
94
  @import url("css/project-settings-modal.css");
95
95
  @import url("css/settings-shell.css");
96
+ @import url("css/agent-behavior.css");
96
97
 
97
98
  @import url("css/clay-tool-activity.css");
98
99
 
package/lib/sdk-bridge.js CHANGED
@@ -1721,7 +1721,8 @@ function createSDKBridge(opts) {
1721
1721
  function needsCatalogRecovery(family, version) {
1722
1722
  return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
1723
1723
  }
1724
- var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge"; });
1724
+ // Any User Knowledge tool counts: the Me read and report settings are independent.
1725
+ var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge" || tool.name === "read_user_knowledge" || tool.name === "report_user_observation"; });
1725
1726
  var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
1726
1727
  var hasLinearCatalog = sessionToolDefs.some(function (tool) { return tool.name === "link_session_linear"; });
1727
1728
  var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
@@ -1906,7 +1907,7 @@ function createSDKBridge(opts) {
1906
1907
  clayBridgeToken: clayBridgeToken,
1907
1908
  linuxUser: linuxUser || undefined,
1908
1909
  env: sessionRuntimeEnv,
1909
- skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
1910
+ skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser(), typeof opts.getExcludedSkills === "function" ? opts.getExcludedSkills(session) : undefined),
1910
1911
  // appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
1911
1912
  // REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
1912
1913
  // would strip the driver session down to the two-sentence pair preset.
@@ -0,0 +1,72 @@
1
+ // Authenticated Personal "Agent behavior" settings: GET returns the account's
2
+ // policy with the catalog the page renders; PUT merges validated changes and
3
+ // answers with the saved policy only after it is persisted.
4
+ var schema = require("./agent-behavior-schema");
5
+ var ROUTE = "/api/user/agent-behavior";
6
+ var MAX_BODY = 8192;
7
+
8
+ function attachAgentBehaviorSettings(ctx) {
9
+ var users = ctx.users;
10
+ var getMultiUserFromReq = ctx.getMultiUserFromReq;
11
+ var opts = ctx.opts || {};
12
+
13
+ function send(res, status, body) {
14
+ res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
15
+ res.end(JSON.stringify(body));
16
+ }
17
+
18
+ function owner(req) {
19
+ if (!users.isMultiUser()) return { id: "default" };
20
+ var mu = getMultiUserFromReq(req);
21
+ return mu ? { id: mu.id } : null;
22
+ }
23
+
24
+ function read(user) {
25
+ if (users.isMultiUser()) return users.getAgentBehavior(user.id);
26
+ var config = typeof opts.onGetDaemonConfig === "function" ? opts.onGetDaemonConfig() : {};
27
+ return schema.normalize(config && config.agentBehavior);
28
+ }
29
+
30
+ function reply(res, policy, extra) {
31
+ send(res, 200, Object.assign({ ok: true, policy: policy, effective: schema.effective(policy), catalog: schema.catalog() }, extra || {}));
32
+ }
33
+
34
+ function handleRequest(req, res, fullUrl) {
35
+ if (fullUrl !== ROUTE || (req.method !== "GET" && req.method !== "PUT")) return false;
36
+ var user = owner(req);
37
+ if (!user) { send(res, 401, { error: "unauthorized" }); return true; }
38
+ if (req.method === "GET") { reply(res, read(user)); return true; }
39
+ var startedMulti = users.isMultiUser();
40
+ var body = "";
41
+ var tooLarge = false;
42
+ req.on("data", function (chunk) {
43
+ if (tooLarge) return;
44
+ body += chunk;
45
+ if (body.length > MAX_BODY) tooLarge = true;
46
+ });
47
+ req.on("end", function () {
48
+ if (tooLarge) { send(res, 413, { error: "Request too large" }); return; }
49
+ // The body arrives asynchronously: authentication may have been revoked,
50
+ // replaced or the account mode changed since the request started.
51
+ var current = owner(req);
52
+ if (!current || current.id !== user.id || users.isMultiUser() !== startedMulti) { send(res, 401, { error: "unauthorized" }); return; }
53
+ var data;
54
+ try { data = JSON.parse(body); } catch (error) { send(res, 400, { error: "Invalid request" }); return; }
55
+ if (!data || typeof data !== "object") { send(res, 400, { error: "Invalid request" }); return; }
56
+ var result;
57
+ if (users.isMultiUser()) result = users.updateAgentBehavior(user.id, data.changes);
58
+ else if (typeof opts.onUpdateAgentBehavior === "function") result = opts.onUpdateAgentBehavior(data.changes);
59
+ else result = { error: "Agent behavior cannot be saved on this server" };
60
+ if (result.error) { send(res, /^Could not save/.test(result.error) ? 500 : 400, { error: result.error }); return; }
61
+ if (typeof ctx.onAgentBehaviorChanged === "function") {
62
+ try { ctx.onAgentBehaviorChanged(user.id, result.policy); } catch (error) { /* The setting is already saved. */ }
63
+ }
64
+ reply(res, result.policy, data.requestId ? { requestId: String(data.requestId).slice(0, 80) } : null);
65
+ });
66
+ return true;
67
+ }
68
+
69
+ return { handleRequest: handleRequest, ROUTE: ROUTE };
70
+ }
71
+
72
+ module.exports = { attachAgentBehaviorSettings: attachAgentBehaviorSettings };
@@ -17,6 +17,7 @@ function attachSettings(ctx) {
17
17
  var appearanceSettings = require('./server-appearance-settings').attachAppearanceSettings(ctx);
18
18
  var notificationSettings = require('./server-notification-settings').attachNotificationSettings(ctx);
19
19
  var experimentalSettings = attachExperimentalSettings(ctx);
20
+ var agentBehaviorSettings = require("./server-agent-behavior-settings").attachAgentBehaviorSettings(ctx);
20
21
 
21
22
  function handleRequest(req, res, fullUrl) {
22
23
  if (contextualClaySettings.handleRequest(req, res, fullUrl)) return true;
@@ -24,6 +25,7 @@ function attachSettings(ctx) {
24
25
  if (appearanceSettings.handleRequest(req, res, fullUrl)) return true;
25
26
  if (notificationSettings.handleRequest(req, res, fullUrl)) return true;
26
27
  if (experimentalSettings.handleRequest(req, res, fullUrl)) return true;
28
+ if (agentBehaviorSettings.handleRequest(req, res, fullUrl)) return true;
27
29
  // Render historical avatar styles locally so profile seeds never leave Clay.
28
30
  if (req.method === "GET" && fullUrl === "/api/generated-avatar") {
29
31
  var avatarParams = new URL(req.url || fullUrl, "http://localhost").searchParams;
package/lib/server.js CHANGED
@@ -503,6 +503,15 @@ function createServer(opts) {
503
503
  onCapsulesPreferenceChanged: function (userId) {
504
504
  if (toolsHandler) toolsHandler.refreshMateCatalogs(userId);
505
505
  },
506
+ onAgentBehaviorChanged: function (userId) {
507
+ projects.forEach(function (projectContext) {
508
+ try {
509
+ if (projectContext && typeof projectContext.refreshAgentBehavior === "function") projectContext.refreshAgentBehavior(userId);
510
+ } catch (error) {
511
+ console.error("[agent-behavior] Could not refresh a project's sessions:", error && error.message ? error.message : error);
512
+ }
513
+ });
514
+ },
506
515
  });
507
516
 
508
517
  function injectAppearanceSeed(html, appearance) {
@@ -4,17 +4,34 @@ var buildShape = require("./session-spawn-mcp-server").buildShape;
4
4
  var z;
5
5
  try { z = require("zod"); } catch (e) { z = null; }
6
6
 
7
- var MEMORY_CONTRACT =
8
- "The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. " +
9
- "Except for confirmed Issue link notes described below, default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository, an Issue, or another note, and the user would likely be glad to find it on the board a week later. " +
10
- "Use Sticky Notes for preferences, reminders, undeveloped ideas, or an explicit request to remember something; confirmed Issues also get short link notes as described below. For concrete actionable bugs, improvements, or deferred implementation, an authorized Project Driver should use Project Issues. If you lack Issues authority, route the work through an eligible Project Driver. Check active notes first when a duplicate is plausible. " +
11
- require("./issues-filing-guidance").NOTE_GUIDANCE +
7
+ var BOARD_INTRO = "The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. ";
8
+ var NEVER_CREATE =
12
9
  "Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. " +
13
10
  "One exception: when the user explicitly asks for a working checklist, keep that one native checklist note and check its items off as they are done; never start a checklist the user did not ask for. " +
14
11
  "Updates are visible too: update only when durable state materially changes. " +
15
12
  "A note is an attention item, so finishing it means closing it, never erasing it: call close_note once the thing it is asking for is done, and the note leaves the active board while the record stays and can be reopened. Do not turn a note into a completion log instead of closing it. " +
16
13
  "Put a concise plain-text title on the first line, stay focused on one topic, and include only the context needed for future action.";
17
14
 
15
+ // options.linked: keep the confirmed-Issue link note exception (default true).
16
+ // options.mode "request": write only when the user's message asks.
17
+ function memoryContract(options) {
18
+ var linked = !options || options.linked !== false;
19
+ if (options && options.mode === "request") {
20
+ return BOARD_INTRO +
21
+ "The user has set Sticky Notes to On request: create, update or close a note only when the user's message asks you to remember, track, change or close something on the board. Do not add notes on your own initiative, including notes that link Issues. Check active notes first when a duplicate is plausible. " +
22
+ NEVER_CREATE;
23
+ }
24
+ return BOARD_INTRO +
25
+ (linked ? "Except for confirmed Issue link notes described below, default to not writing. " : "Default to not writing. ") +
26
+ "Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository, an Issue, or another note, and the user would likely be glad to find it on the board a week later. " +
27
+ "Use Sticky Notes for preferences, reminders, undeveloped ideas, or an explicit request to remember something; " + (linked ? "confirmed Issues also get short link notes as described below. " : "") +
28
+ "For concrete actionable bugs, improvements, or deferred implementation, an authorized Project Driver should use Project Issues. If you lack Issues authority, route the work through an eligible Project Driver. Check active notes first when a duplicate is plausible. " +
29
+ (linked ? require("./issues-filing-guidance").NOTE_GUIDANCE : "") +
30
+ NEVER_CREATE;
31
+ }
32
+
33
+ var MEMORY_CONTRACT = memoryContract();
34
+
18
35
  var CHECKLIST_USAGE =
19
36
  " Native checklists: pass checklist: true to create one. The first line is the title and every later non-empty, non-heading line is an item; write items as '- [ ] task' and '- [x] task' for items already done. " +
20
37
  "To check or uncheck items, call list_notes, then pass id, that note's current version and checks with the exact item keys it returned. " +
@@ -27,7 +44,9 @@ var CHECK_ENTRY = z ? z.object({
27
44
  checked: z.boolean().describe("true marks the item done; false marks it not done."),
28
45
  }) : null;
29
46
 
30
- function getToolDefs(handlers) {
47
+ function getToolDefs(handlers, options) {
48
+ var MEMORY_CONTRACT = memoryContract(options);
49
+ var linked = !options || options.linked !== false;
31
50
  return [
32
51
  {
33
52
  name: "list_notes",
@@ -39,7 +58,7 @@ function getToolDefs(handlers) {
39
58
  },
40
59
  {
41
60
  name: "write_note",
42
- description: MEMORY_CONTRACT + " Create a new note, or update an existing note by id. For notes other than confirmed Issue link notes and a working checklist the user explicitly asked for, apply this test: would the user likely choose to keep this visible on their board next week? If the answer is unclear, do not call this tool. The 20000-character limit is an abuse guard, not a target." + CHECKLIST_USAGE,
61
+ description: MEMORY_CONTRACT + " Create a new note, or update an existing note by id. For notes other than " + (linked ? "confirmed Issue link notes and " : "") + "a working checklist the user explicitly asked for, apply this test: would the user likely choose to keep this visible on their board next week? If the answer is unclear, do not call this tool. The 20000-character limit is an abuse guard, not a target." + CHECKLIST_USAGE,
43
62
  inputSchema: buildShape({
44
63
  id: { type: "string", description: "Existing note id to update. Omit to create a note." },
45
64
  text: { type: "string", description: "User-facing sticky-note text with a concise title on the first line and only durable, action-relevant context below it. Required when creating; omit on update to keep the current text." },
@@ -83,5 +102,6 @@ module.exports = {
83
102
  // receive the current schema through exact-query recovery.
84
103
  CATALOG_VERSION: 2,
85
104
  MEMORY_CONTRACT: MEMORY_CONTRACT,
105
+ memoryContract: memoryContract,
86
106
  getToolDefs: getToolDefs,
87
107
  };
@@ -1,7 +1,7 @@
1
1
  // Bind personal instruction reads to the existing live session/query authority.
2
- function bind(session, sessions, options, valid, multiUser) {
2
+ function bind(session, sessions, options, valid, multiUser, excludeSkills) {
3
3
  var owner = session.ownerId || null;
4
- return Object.assign({}, options, { isAvailable: function() {
4
+ return Object.assign({}, options, excludeSkills && excludeSkills.length ? { excludeSkills: excludeSkills } : {}, { isAvailable: function() {
5
5
  if (!sessions || typeof sessions.get !== "function" || sessions.get(session.localId) !== session || !valid() || (session.ownerId || null) !== owner || session.youCurator || session.sessionVisibility === "shared" || session._mcpForeignInput) return false;
6
6
  if (owner) return require("./mcp-session-access").canUsePersonalConnections(session, sessions);
7
7
  return !multiUser && !session.delegated && !session._delegatedBy && !session._pairDelegation
@@ -0,0 +1,38 @@
1
+ // Per-account storage for the Personal "Agent behavior" policy. Only explicit
2
+ // choices are stored, so an account with no record keeps today's behavior.
3
+ var schema = require("./agent-behavior-schema");
4
+
5
+ function attachAgentBehaviorPreferences(deps) {
6
+ var loadUsers = deps.loadUsers;
7
+ var saveUsers = deps.saveUsers;
8
+
9
+ function find(data, userId) {
10
+ for (var i = 0; i < data.users.length; i++) {
11
+ if (data.users[i].id === userId) return data.users[i];
12
+ }
13
+ return null;
14
+ }
15
+
16
+ function getAgentBehavior(userId) {
17
+ var user = find(loadUsers(), userId);
18
+ return schema.normalize(user && user.agentBehavior);
19
+ }
20
+
21
+ // Merge into the stored explicit choices so two tabs changing different
22
+ // settings never overwrite each other.
23
+ function updateAgentBehavior(userId, changes) {
24
+ var checked = schema.validateChanges(changes);
25
+ if (checked.error) return { error: checked.error };
26
+ var data = loadUsers();
27
+ var user = find(data, userId);
28
+ if (!user) return { error: "User not found" };
29
+ var explicit = Object.assign({}, schema.explicit(user.agentBehavior), checked.value);
30
+ user.agentBehavior = explicit;
31
+ try { saveUsers(data); } catch (error) { return { error: "Could not save agent behavior" }; }
32
+ return { ok: true, policy: schema.normalize(explicit) };
33
+ }
34
+
35
+ return { getAgentBehavior: getAgentBehavior, updateAgentBehavior: updateAgentBehavior };
36
+ }
37
+
38
+ module.exports = { attachAgentBehaviorPreferences: attachAgentBehaviorPreferences };
package/lib/users.js CHANGED
@@ -11,6 +11,7 @@ var { attachHomeDockPreferences } = require("./users-home-dock-preferences");
11
11
  var { attachHomeSurfacePreferences } = require("./users-home-surface-preferences");
12
12
  var { attachSessionFolderPreferences } = require("./users-session-folder-preferences");
13
13
  var { attachExperimentalPreferences } = require("./users-experimental-preferences");
14
+ var { attachAgentBehaviorPreferences } = require("./users-agent-behavior-preferences");
14
15
  var { attachScheduledTaskPreferences } = require("./users-scheduled-task-preferences");
15
16
  var { attachDefaultAiPreferences } = require("./users-default-ai-preferences");
16
17
  var { attachDefaultVendorPreferences } = require("./users-default-vendor-preferences");
@@ -340,6 +341,7 @@ var homeDockPreferences = attachHomeDockPreferences({ loadUsers: loadUsers, save
340
341
  var homeSurfacePreferences = attachHomeSurfacePreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
341
342
  var sessionFolderPreferences = attachSessionFolderPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
342
343
  var experimentalPreferences = attachExperimentalPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
344
+ var agentBehaviorPreferences = attachAgentBehaviorPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
343
345
  var scheduledTaskPreferences = attachScheduledTaskPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
344
346
  var defaultAiPreferences = attachDefaultAiPreferences({ loadUsers: loadUsers, saveUsers: saveUsers, isMultiUser: auth.isMultiUser });
345
347
  var defaultVendorPreferences = attachDefaultVendorPreferences({ loadUsers: loadUsers, saveUsers: saveUsers, isMultiUser: auth.isMultiUser });
@@ -394,6 +396,8 @@ var setTerminalFont = preferences.setTerminalFont;
394
396
  var setMateOnboarded = preferences.setMateOnboarded;
395
397
  var getCapsulesEnabled = experimentalPreferences.getCapsulesEnabled;
396
398
  var setCapsulesEnabled = experimentalPreferences.setCapsulesEnabled;
399
+ var getAgentBehavior = agentBehaviorPreferences.getAgentBehavior;
400
+ var updateAgentBehavior = agentBehaviorPreferences.updateAgentBehavior;
397
401
  var getHomeDockPreference = homeDockPreferences.getHomeDockPreference;
398
402
  var setHomeDockPreference = homeDockPreferences.setHomeDockPreference;
399
403
  var getHomeSurfacePreference = homeSurfacePreferences.getHomeSurfacePreference;
@@ -476,6 +480,8 @@ module.exports = {
476
480
  setMatesEnabled: setMatesEnabled,
477
481
  getCapsulesEnabled: getCapsulesEnabled,
478
482
  setCapsulesEnabled: setCapsulesEnabled,
483
+ getAgentBehavior: getAgentBehavior,
484
+ updateAgentBehavior: updateAgentBehavior,
479
485
  getToolPalettes: getToolPalettes,
480
486
  setToolPalette: setToolPalette,
481
487
  getTerminalFont: getTerminalFont,
@@ -93,7 +93,7 @@ function attachPreToolUsePolicy(options, policy) {
93
93
  }]);
94
94
  }
95
95
 
96
- function getSharedSkillPlugins(homeDir, existingPlugins, cwd, env) {
96
+ function getSharedSkillPlugins(homeDir, existingPlugins, cwd, env, excludeSkills) {
97
97
  var plugins = Array.isArray(existingPlugins) ? existingPlugins.slice() : [];
98
98
  // undefined means normal in-process Claude and may use the configured
99
99
  // daemon home; null means a failed mapped-user resolution and must fail
@@ -112,7 +112,11 @@ function getSharedSkillPlugins(homeDir, existingPlugins, cwd, env) {
112
112
  for (var i = 0; i < roots.length; i++) {
113
113
  if (roots[i].source === "codex-user" || roots[i].source === "claude-user" || roots[i].source === "claude-project") continue;
114
114
  var rootId = crypto.createHash("sha256").update(roots[i].source + "\0" + path.resolve(roots[i].path)).digest("hex").slice(0, 12);
115
- var sharedPath = skillDiscovery.ensureClaudeCodexPlugin({ homeDir: effectiveHome, source: roots[i].path, name: "shared-" + roots[i].source + "-" + rootId });
115
+ var bridgeOpts = { homeDir: effectiveHome, source: roots[i].path, name: "shared-" + roots[i].source + "-" + rootId };
116
+ // Per-query exclusions (Personal Agent behavior) apply only to bundled skills.
117
+ var sharedPath = roots[i].source === "clay-builtin" && Array.isArray(excludeSkills) && excludeSkills.length
118
+ ? skillDiscovery.ensureFilteredClaudePlugin(Object.assign({ exclude: excludeSkills }, bridgeOpts))
119
+ : skillDiscovery.ensureClaudeCodexPlugin(bridgeOpts);
116
120
  if (sharedPath) plugins.push({ type: "local", path: sharedPath, skipMcpDiscovery: true });
117
121
  }
118
122
  return plugins;
@@ -143,7 +147,7 @@ function buildWorkerQueryOptions(queryOpts, claudeOpts, workerCwd) {
143
147
  queryOpts.systemPromptSnapshot
144
148
  );
145
149
  }
146
- queryOptions.plugins = queryOpts.skipSkills ? (claudeOpts.plugins || []) : getSharedSkillPlugins(claudeOpts.originalHome, claudeOpts.plugins, workerCwd, queryOpts.env);
150
+ queryOptions.plugins = queryOpts.skipSkills ? (claudeOpts.plugins || []) : getSharedSkillPlugins(claudeOpts.originalHome, claudeOpts.plugins, workerCwd, queryOpts.env, queryOpts.skillOptions && queryOpts.skillOptions.excludeSkills);
147
151
  if (queryOpts.toolServerDescriptors) queryOptions.mcpServerDescriptors = queryOpts.toolServerDescriptors;
148
152
  if (queryOpts.onUserDialog) queryOptions.supportedDialogKinds = SUPPORTED_DIALOG_KINDS;
149
153
  queryOptions.perTaskStopAffordance = PER_TASK_STOP_AFFORDANCE;
@@ -1832,7 +1836,7 @@ function createClaudeAdapter(opts) {
1832
1836
  if (co.allowDangerouslySkipPermissions) sdkOptions.allowDangerouslySkipPermissions = true;
1833
1837
  if (co.resumeSessionAt) sdkOptions.resumeSessionAt = co.resumeSessionAt;
1834
1838
  if (co.settings) sdkOptions.settings = co.settings;
1835
- sdkOptions.plugins = queryOpts.skipSkills ? (co.plugins || []) : getSharedSkillPlugins(co.originalHome, co.plugins, queryOpts.cwd || _cwd, queryOpts.env);
1839
+ sdkOptions.plugins = queryOpts.skipSkills ? (co.plugins || []) : getSharedSkillPlugins(co.originalHome, co.plugins, queryOpts.cwd || _cwd, queryOpts.env, queryOpts.skillOptions && queryOpts.skillOptions.excludeSkills);
1836
1840
 
1837
1841
  claudeModelSwitch.attach(sdkOptions, queryOpts.onModelSwitch);
1838
1842
  var rawQuery = sdk.query({ prompt: mq, options: sdkOptions });
@@ -129,7 +129,10 @@ function discoverSkills(cwd, opts) {
129
129
  } catch (error) { console.error("[skills] Clay-only skill migration failed: " + error.message); }
130
130
  }
131
131
  for (var i = 0; i < roots.length; i++) scanRoot(roots[i].path, roots[i].source, merged, opts);
132
- return Object.keys(merged).sort().map(function(name) { return merged[name]; });
132
+ // Per-session exclusions (Personal Agent behavior). Only names a caller lists
133
+ // are removed; nothing is excluded by default.
134
+ var excluded = opts && Array.isArray(opts.excludeSkills) ? opts.excludeSkills : [];
135
+ return Object.keys(merged).sort().filter(function(name) { return excluded.indexOf(name) === -1; }).map(function(name) { return merged[name]; });
133
136
  }
134
137
 
135
138
  function indexSkills(skills) {
@@ -244,7 +247,49 @@ function ensureClaudeCodexPlugin(opts) {
244
247
  return pluginDir;
245
248
  }
246
249
 
250
+ // A per-exclusion plugin for the Claude provider: its skills directory is a real
251
+ // directory of symlinks to every skill in source except the excluded names. The
252
+ // unfiltered bridge is never modified; each distinct exclusion set gets its own
253
+ // deterministic directory under the same bridge root.
254
+ function ensureFilteredClaudePlugin(opts) {
255
+ var homeDir = opts.homeDir || defaultHomeDir();
256
+ var excluded = (opts.exclude || []).slice().sort();
257
+ var tag = require("crypto").createHash("sha256").update(excluded.join("\0")).digest("hex").slice(0, 10);
258
+ var bridgeName = (opts.name || "shared") + "-without-" + tag;
259
+ var entries;
260
+ try { entries = fs.readdirSync(opts.source, { withFileTypes: true }); } catch (e) { return null; }
261
+ var pluginDir = path.join(homeDir, ".clay", "skill-bridge", bridgeName);
262
+ var skillsDir = path.join(pluginDir, "skills");
263
+ try {
264
+ fs.mkdirSync(path.join(pluginDir, ".claude-plugin"), { recursive: true });
265
+ fs.writeFileSync(path.join(pluginDir, ".claude-plugin", "plugin.json"), JSON.stringify({ name: bridgeName, version: "1.0.0", description: "Shared skills provided by Clay (filtered)" }, null, 2) + "\n", { mode: 0o600 });
266
+ var skillsStat = lstatOrNull(skillsDir);
267
+ if (skillsStat && (!skillsStat.isDirectory() || skillsStat.isSymbolicLink())) return null;
268
+ if (!skillsStat) fs.mkdirSync(skillsDir);
269
+ var wanted = {};
270
+ entries.forEach(function (entry) {
271
+ if (excluded.indexOf(entry.name) !== -1 || !(entry.isDirectory() || entry.isSymbolicLink())) return;
272
+ wanted[entry.name] = path.join(opts.source, entry.name);
273
+ });
274
+ fs.readdirSync(skillsDir).forEach(function (name) {
275
+ var stale = lstatOrNull(path.join(skillsDir, name));
276
+ if (!wanted[name] && stale && stale.isSymbolicLink()) fs.unlinkSync(path.join(skillsDir, name));
277
+ });
278
+ Object.keys(wanted).forEach(function (name) {
279
+ var link = path.join(skillsDir, name);
280
+ var existing = lstatOrNull(link);
281
+ if (existing) { if (existing.isSymbolicLink() && path.resolve(skillsDir, fs.readlinkSync(link)) === path.resolve(wanted[name])) return; return; }
282
+ fs.symlinkSync(wanted[name], link, "dir");
283
+ });
284
+ } catch (e) {
285
+ console.error("[yoke/skills] Failed to create filtered Claude skill bridge:", e.message);
286
+ return null;
287
+ }
288
+ return pluginDir;
289
+ }
290
+
247
291
  module.exports = {
292
+ ensureFilteredClaudePlugin: ensureFilteredClaudePlugin,
248
293
  buildSkillIndex: buildSkillIndex,
249
294
  discoverSkills: discoverSkills,
250
295
  ensureClaudeCodexPlugin: ensureClaudeCodexPlugin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "5.4.0",
3
+ "version": "5.5.0-beta.2",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js"
@@ -1 +0,0 @@
1
- import{hc as a,ic as b,jc as c,kc as d}from"./chunk-FQQMXMH7.js";import"./chunk-U5GH2UJM.js";import"./chunk-JIV7O5GN.js";import"./chunk-YE45RR7W.js";import"./chunk-42C2CZEP.js";import"./chunk-XI2DJM4H.js";import"./chunk-R7QVVFUG.js";import"./chunk-E6NRPGVH.js";import"./chunk-VZ2OKDAO.js";export{c as closeDebatesWorkbench,a as handleDebatesWorkbenchState,d as initDebatesWorkbench,b as openDebatesWorkbench};
@@ -1 +0,0 @@
1
- import{l as a,m as b,n as c,o as d}from"./chunk-FQQMXMH7.js";import"./chunk-U5GH2UJM.js";import"./chunk-JIV7O5GN.js";import"./chunk-YE45RR7W.js";import"./chunk-42C2CZEP.js";import"./chunk-XI2DJM4H.js";import"./chunk-R7QVVFUG.js";import"./chunk-E6NRPGVH.js";import"./chunk-VZ2OKDAO.js";export{a as closeLinear,c as handleLinearResult,d as initLinearPanel,b as openLinear};
@@ -1 +0,0 @@
1
- import{g as a,h as b,i as c}from"./chunk-42C2CZEP.js";import"./chunk-XI2DJM4H.js";import"./chunk-R7QVVFUG.js";import"./chunk-E6NRPGVH.js";import"./chunk-VZ2OKDAO.js";export{a as closeWireframe,b as openWireframe,c as restoreWireframeWorkbench};
@@ -1 +0,0 @@
1
- import{Ra as a,Sa as b,Ta as c,Ua as d,Va as e}from"./chunk-FQQMXMH7.js";import"./chunk-U5GH2UJM.js";import"./chunk-JIV7O5GN.js";import"./chunk-YE45RR7W.js";import"./chunk-42C2CZEP.js";import"./chunk-XI2DJM4H.js";import"./chunk-R7QVVFUG.js";import"./chunk-E6NRPGVH.js";import"./chunk-VZ2OKDAO.js";export{c as closeYou,d as handleYouMessage,e as initYou,b as openYou,a as sendYou};
@@ -1 +0,0 @@
1
- import{p as a,q as b,r as c,s as d,t as e,u as f}from"./chunk-FQQMXMH7.js";import"./chunk-U5GH2UJM.js";import"./chunk-JIV7O5GN.js";import"./chunk-YE45RR7W.js";import"./chunk-42C2CZEP.js";import"./chunk-XI2DJM4H.js";import"./chunk-R7QVVFUG.js";import"./chunk-E6NRPGVH.js";import"./chunk-VZ2OKDAO.js";export{c as closeIssues,f as handleIssuesMessage,e as initIssues,d as openIssues,b as refreshGithubIssue,a as requestIssue};