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.
- package/lib/agent-behavior-gate.js +223 -0
- package/lib/agent-behavior-schema.js +184 -0
- package/lib/daemon.js +15 -0
- package/lib/issues-mcp-server.js +16 -3
- package/lib/migrate-single-user.js +2 -0
- package/lib/project-issues.js +9 -2
- package/lib/project-log-feedback-delivery.js +3 -0
- package/lib/project-logs-mcp-server.js +23 -2
- package/lib/project-logs.js +40 -6
- package/lib/project-session-document.js +9 -1
- package/lib/project-session-notes.js +19 -8
- package/lib/project-session-pair-prompt.js +5 -4
- package/lib/project-user-knowledge.js +21 -3
- package/lib/project-worker-proposal.js +8 -2
- package/lib/project.js +108 -61
- package/lib/public/app.js +1 -0
- package/lib/public/css/agent-behavior.css +101 -0
- package/lib/public/modules/agent-behavior-nav.js +90 -0
- package/lib/public/modules/agent-behavior-view.js +235 -0
- package/lib/public/modules/agent-behavior.js +200 -0
- package/lib/public/modules/settings-shell-registry.js +1 -1
- package/lib/public/modules/settings-shell-view.js +18 -0
- package/lib/public/modules/sidebar-sessions.js +3 -0
- package/lib/public/modules/split-view-arrange-menu.js +38 -0
- package/lib/public/modules/split-view-create-menu.js +9 -5
- package/lib/public/modules/split-view-member-actions.js +42 -0
- package/lib/public/modules/split-view-picker-actions.js +2 -2
- package/lib/public/modules/split-view-presentation-renderer.js +10 -35
- package/lib/public/modules/split-view-presentations.js +9 -8
- package/lib/public/modules/split-view-sidebar-menu.js +2 -1
- package/lib/public/modules/user-settings-layout.js +8 -1
- package/lib/public/modules/user-settings.js +5 -2
- package/lib/public/modules/view-workspace-state.js +1 -1
- package/lib/public/pane-assets/{app-BDYH6Y7R.js → app-SEMVZ4AY.js} +1 -1
- package/lib/public/pane-assets/{chunk-U5GH2UJM.js → chunk-4YE4RU27.js} +1 -1
- package/lib/public/pane-assets/{chunk-E6NRPGVH.js → chunk-5UBN6GR7.js} +1 -1
- package/lib/public/pane-assets/chunk-6AGSOAOD.js +1 -0
- package/lib/public/pane-assets/chunk-AATAIAXL.js +1 -0
- package/lib/public/pane-assets/{chunk-42C2CZEP.js → chunk-BNNPJJVG.js} +1 -1
- package/lib/public/pane-assets/{chunk-HWLBH6AL.js → chunk-C6P7PICY.js} +1 -1
- package/lib/public/pane-assets/{chunk-KB4T5B2R.js → chunk-CQNPEH2M.js} +1 -1
- package/lib/public/pane-assets/chunk-D3CE5FZL.js +1 -0
- package/lib/public/pane-assets/{chunk-N5SIEIOG.js → chunk-E4VM2IDB.js} +1 -1
- package/lib/public/pane-assets/{chunk-VCNLKOVT.js → chunk-FK3YLGAP.js} +1 -1
- package/lib/public/pane-assets/chunk-GKSLBGVE.js +168 -0
- package/lib/public/pane-assets/chunk-J5PTSKJ6.js +1 -0
- package/lib/public/pane-assets/{chunk-XI2DJM4H.js → chunk-K4PAN77B.js} +1 -1
- package/lib/public/pane-assets/{chunk-UMIYQYGM.js → chunk-M5CWIIIT.js} +1 -1
- package/lib/public/pane-assets/{chunk-KF3LZRJK.js → chunk-QBCM7QEA.js} +1 -1
- package/lib/public/pane-assets/chunk-QPBO6ID4.js +1 -0
- package/lib/public/pane-assets/{chunk-TMUCEZ7C.js → chunk-QWQB2YRF.js} +1 -1
- package/lib/public/pane-assets/chunk-RA6VUJTY.js +1 -0
- package/lib/public/pane-assets/{chunk-TT3NECKK.js → chunk-U7EHYRJZ.js} +1 -1
- package/lib/public/pane-assets/chunk-XOZUM5ET.js +1 -0
- package/lib/public/pane-assets/{chunk-X452YCE5.js → chunk-XYZGVYWW.js} +1 -1
- package/lib/public/pane-assets/{chunk-QRVKAVH7.js → chunk-YR5GH3GI.js} +1 -1
- package/lib/public/pane-assets/{chunk-YE45RR7W.js → chunk-YVVT3CTV.js} +1 -1
- package/lib/public/pane-assets/manifest.json +394 -388
- package/lib/public/pane-assets/{style-VDIGFFLR.css → style-DZX6JAMF.css} +1 -1
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +3 -2
- package/lib/server-agent-behavior-settings.js +72 -0
- package/lib/server-settings.js +2 -0
- package/lib/server.js +9 -0
- package/lib/session-notes-mcp-server.js +27 -7
- package/lib/session-skill-scope.js +2 -2
- package/lib/users-agent-behavior-preferences.js +38 -0
- package/lib/users.js +6 -0
- package/lib/yoke/adapters/claude.js +8 -4
- package/lib/yoke/skill-discovery.js +46 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/chunk-46OOGVHU.js +0 -1
- package/lib/public/pane-assets/chunk-4OJW4FF4.js +0 -1
- package/lib/public/pane-assets/chunk-F7VIE6FJ.js +0 -1
- package/lib/public/pane-assets/chunk-FCJYJLVZ.js +0 -1
- package/lib/public/pane-assets/chunk-FEJDEXOW.js +0 -1
- package/lib/public/pane-assets/chunk-FQQMXMH7.js +0 -168
- package/lib/public/pane-assets/chunk-HEMCIILG.js +0 -1
- package/lib/public/pane-assets/chunk-RV5O66TO.js +0 -1
package/lib/public/style.css
CHANGED
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
|
-
|
|
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 };
|
package/lib/server-settings.js
CHANGED
|
@@ -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
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
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 +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};
|