clay-server 3.8.1 → 4.0.0-beta.1
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/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +329 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +78 -20
- package/lib/public/modules/app-notifications.js +4 -2
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +276 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +86 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +37 -6
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +2 -1
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -1,120 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
// AskUserQuestion UI and ask_user_response flow.
|
|
1
|
+
// Backward-compatible export for callers migrating to YOKE user input.
|
|
2
|
+
var userInput = require("./yoke/user-input");
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// Returns a Zod "shape" object (property -> zod field) matching what
|
|
9
|
-
// Claude SDK's `sdk.tool()` expects. Do NOT wrap in z.object() here —
|
|
10
|
-
// the SDK does that internally.
|
|
11
|
-
//
|
|
12
|
-
// The schema is deliberately tight: options are required (2-6), each with
|
|
13
|
-
// a label and a short description. This pushes Codex (which treats the
|
|
14
|
-
// schema as a loose hint) toward producing well-structured multiple-choice
|
|
15
|
-
// cards instead of a bare "Other..." text field.
|
|
16
|
-
function buildQuestionShape() {
|
|
17
|
-
if (!z) return {};
|
|
18
|
-
|
|
19
|
-
var optionSchema = z.object({
|
|
20
|
-
label: z.string().min(1).max(60)
|
|
21
|
-
.describe("Short button label, 1-6 words. Shown as the primary option text."),
|
|
22
|
-
description: z.string().min(1).max(160)
|
|
23
|
-
.describe("One-line clarifier shown under the label. Concrete example or scope."),
|
|
24
|
-
markdown: z.string().optional()
|
|
25
|
-
.describe("Optional longer markdown body shown on expand. Use sparingly."),
|
|
26
|
-
}).passthrough();
|
|
27
|
-
|
|
28
|
-
var questionSchema = z.object({
|
|
29
|
-
header: z.string().min(1).max(40)
|
|
30
|
-
.describe("Short ALL-CAPS-ish section header above the question (e.g. 'FIRST THINGS FIRST', 'SCOPE'). Gives the user context for this step."),
|
|
31
|
-
question: z.string().min(1)
|
|
32
|
-
.describe("The actual question in natural spoken tone. Be specific, not generic."),
|
|
33
|
-
multiSelect: z.boolean().optional()
|
|
34
|
-
.describe("Set true only when multiple answers genuinely make sense. Default false."),
|
|
35
|
-
options: z.array(optionSchema).min(2).max(6)
|
|
36
|
-
.describe("Concrete options. Preferred count is 4; acceptable range is 2-6. FOUR is the target. Do NOT default to 3 out of habit. If you can only think of 3, push yourself: add a scope variant, edge case, or 'combined' option as the 4th. Only drop below 4 when the answer space is genuinely narrower (e.g. yes/no/unsure). Never include an 'Other' option yourself (the UI renders one automatically)."),
|
|
37
|
-
}).passthrough();
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
questions: z.array(questionSchema).min(1).max(3)
|
|
41
|
-
.describe("One to three question objects to show together as a card. Prefer ONE focused question per call."),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
var TOOL_DESCRIPTION = [
|
|
46
|
-
"Ask the user a structured multiple-choice question. Renders as a card with a header, the question, 2-6 clickable option buttons, and an always-present 'Other...' free-text field.",
|
|
47
|
-
"",
|
|
48
|
-
"WHEN TO USE:",
|
|
49
|
-
"- Interviewing the user at mate setup, or whenever you need to narrow scope before acting.",
|
|
50
|
-
"- Any branching decision where 2-6 concrete choices cover most of the space.",
|
|
51
|
-
"",
|
|
52
|
-
"REQUIRED STRUCTURE (all fields matter, do not skip):",
|
|
53
|
-
"- header: short section label, like 'FIRST THINGS FIRST' or 'SCOPE'. Gives context.",
|
|
54
|
-
"- question: one specific question in natural tone. Avoid generic 'how can I help?'.",
|
|
55
|
-
"- options: ALWAYS provide FOUR options by default. Acceptable range is 4-6; only drop to 3 if the answer space is genuinely binary-plus-one (rare). Each option has a short label (1-6 words) AND a one-line description giving a concrete example or scope. The UI already renders an 'Other...' text field, so you never need to include an 'Other' option yourself.",
|
|
56
|
-
"- multiSelect: omit or false unless multiple answers clearly apply.",
|
|
57
|
-
"",
|
|
58
|
-
"ON OPTION COUNT (IMPORTANT):",
|
|
59
|
-
"- Default to 4 options. Not 3. Models tend to gravitate to 3 because it feels tidy; resist that.",
|
|
60
|
-
"- If you're about to produce 3 options, stop and think: is there a fourth axis you're missing? A scope variant? A 'both' or 'neither'? A more niche case? Add it.",
|
|
61
|
-
"- 3 is only acceptable when the fourth option would be truly degenerate (e.g. yes/no/unsure).",
|
|
62
|
-
"- 5 or 6 is fine when the space is wide; don't cram.",
|
|
63
|
-
"",
|
|
64
|
-
"GOOD EXAMPLE:",
|
|
65
|
-
' { header: "FIRST THINGS FIRST",',
|
|
66
|
-
' question: "\'Language\' is broad, what do you actually want help with?",',
|
|
67
|
-
' options: [',
|
|
68
|
-
' { label: "A new language from scratch", description: "Pick up a language you don\'t speak yet (e.g. Japanese, Spanish)" },',
|
|
69
|
-
' { label: "Sharpen English", description: "Level up your English, writing, speaking, nuance, executive communication" },',
|
|
70
|
-
' { label: "Sharpen Korean", description: "Polish your Korean, writing style, formal register, etc." },',
|
|
71
|
-
' { label: "Teach me how to teach", description: "Help you teach language to others, like Elyse or team members" }',
|
|
72
|
-
" ] }",
|
|
73
|
-
"",
|
|
74
|
-
"BAD EXAMPLES (do not do this):",
|
|
75
|
-
"- Asking a vague question with no options and relying on the Other field.",
|
|
76
|
-
"- Options with only labels and no descriptions.",
|
|
77
|
-
"- Defaulting to exactly 3 options out of habit. Aim for 4.",
|
|
78
|
-
"- More than one question per call unless they are tightly related.",
|
|
79
|
-
"- Single-option 'questions' (use a plain message instead).",
|
|
80
|
-
].join("\n");
|
|
81
|
-
|
|
82
|
-
function getToolDefs(onAsk) {
|
|
83
|
-
var tools = [];
|
|
84
|
-
|
|
85
|
-
tools.push({
|
|
86
|
-
name: "ask_user_questions",
|
|
87
|
-
description: TOOL_DESCRIPTION,
|
|
88
|
-
inputSchema: buildQuestionShape(),
|
|
89
|
-
handler: function (args) {
|
|
90
|
-
if (!args || !Array.isArray(args.questions) || args.questions.length === 0) {
|
|
91
|
-
return Promise.resolve({
|
|
92
|
-
content: [{ type: "text", text: "Error: questions must be a non-empty array." }],
|
|
93
|
-
isError: true,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
// Defensive structural check only: zod already enforces min 2.
|
|
97
|
-
// We do NOT reject on "only 3 options" — that's a soft preference
|
|
98
|
-
// expressed via the tool description, not a hard validation rule.
|
|
99
|
-
for (var i = 0; i < args.questions.length; i++) {
|
|
100
|
-
var q = args.questions[i];
|
|
101
|
-
if (!q || !Array.isArray(q.options) || q.options.length < 2) {
|
|
102
|
-
return Promise.resolve({
|
|
103
|
-
content: [{
|
|
104
|
-
type: "text",
|
|
105
|
-
text: "Error: question " + (i + 1) + " must include at least 2 options. "
|
|
106
|
-
+ "Provide concrete { label, description } choices (4 preferred). "
|
|
107
|
-
+ "The UI already shows an 'Other...' free-text field, so never add an 'Other' option yourself.",
|
|
108
|
-
}],
|
|
109
|
-
isError: true,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return onAsk(args);
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return tools;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
module.exports = { getToolDefs: getToolDefs };
|
|
4
|
+
module.exports = {
|
|
5
|
+
getToolDefs: userInput.fallbackToolDefs,
|
|
6
|
+
};
|
package/lib/builtin-mates.js
CHANGED
|
@@ -24,7 +24,7 @@ var BUILTIN_MATES = [
|
|
|
24
24
|
avatarLocked: true,
|
|
25
25
|
primary: true, // code-managed, auto-updated on startup
|
|
26
26
|
globalSearch: true, // searches all mates' sessions and projects
|
|
27
|
-
hostAgent: true, //
|
|
27
|
+
hostAgent: true, // enables owner-bound workspace-wide read tools (only this mate)
|
|
28
28
|
templateVersion: 1,
|
|
29
29
|
seedData: {
|
|
30
30
|
relationship: "assistant",
|
|
@@ -69,7 +69,7 @@ var BUILTIN_MATES = [
|
|
|
69
69
|
key: "arch",
|
|
70
70
|
displayName: "Arch",
|
|
71
71
|
bio: "Architect. Stubborn about structure, patient about everything else. Draws boxes and arrows before anyone writes code, and is annoyingly right about which ones will break.",
|
|
72
|
-
avatarColor: "#
|
|
72
|
+
avatarColor: "#8a806a",
|
|
73
73
|
avatarStyle: "imprint",
|
|
74
74
|
seedData: {
|
|
75
75
|
relationship: "colleague",
|
|
@@ -87,7 +87,7 @@ var BUILTIN_MATES = [
|
|
|
87
87
|
key: "rush",
|
|
88
88
|
displayName: "Rush",
|
|
89
89
|
bio: "Shipper. Impatient in the best way. Cuts your scope in half, ships it before lunch, and somehow it's better than the original plan.",
|
|
90
|
-
avatarColor: "#
|
|
90
|
+
avatarColor: "#887b91",
|
|
91
91
|
avatarStyle: "imprint",
|
|
92
92
|
seedData: {
|
|
93
93
|
relationship: "colleague",
|
|
@@ -105,7 +105,7 @@ var BUILTIN_MATES = [
|
|
|
105
105
|
key: "ward",
|
|
106
106
|
displayName: "Ward",
|
|
107
107
|
bio: "Guardian. Quietly anxious in a productive way. Reads error logs for fun and asks \"what happens when the input is null?\" before anyone else thinks of it.",
|
|
108
|
-
avatarColor: "#
|
|
108
|
+
avatarColor: "#6f8382",
|
|
109
109
|
avatarStyle: "imprint",
|
|
110
110
|
seedData: {
|
|
111
111
|
relationship: "reviewer",
|
|
@@ -123,7 +123,7 @@ var BUILTIN_MATES = [
|
|
|
123
123
|
key: "pixel",
|
|
124
124
|
displayName: "Pixel",
|
|
125
125
|
bio: "Designer. Obsessed with the details nobody else notices. Sees the 2px misalignment, the confusing label, the flow that makes sense to the builder but not the user.",
|
|
126
|
-
avatarColor: "#
|
|
126
|
+
avatarColor: "#92756d",
|
|
127
127
|
avatarStyle: "imprint",
|
|
128
128
|
seedData: {
|
|
129
129
|
relationship: "colleague",
|
|
@@ -141,7 +141,7 @@ var BUILTIN_MATES = [
|
|
|
141
141
|
key: "buzz",
|
|
142
142
|
displayName: "Buzz",
|
|
143
143
|
bio: "Marketer. Competitive, trend-obsessed, always watching what others are doing. Researches your market, tracks what people search for, and turns \"we built a thing\" into a story people care about.",
|
|
144
|
-
avatarColor: "#
|
|
144
|
+
avatarColor: "#6f7489",
|
|
145
145
|
avatarStyle: "imprint",
|
|
146
146
|
seedData: {
|
|
147
147
|
relationship: "colleague",
|
|
@@ -43,7 +43,22 @@ var CLAY_MANAGED_ALLOW = [
|
|
|
43
43
|
"mcp__clay-browser__browser_watch_tab",
|
|
44
44
|
"mcp__clay-browser__browser_unwatch_tab",
|
|
45
45
|
"mcp__clay-debate__propose_debate",
|
|
46
|
+
"mcp__clay-mate-creation__propose_mate",
|
|
46
47
|
"mcp__clay-history__*",
|
|
48
|
+
"mcp__clay-workspace__list_projects",
|
|
49
|
+
"mcp__clay-workspace__list_project_sessions",
|
|
50
|
+
"mcp__clay-workspace__search_project_history",
|
|
51
|
+
"mcp__clay-workspace__read_project_session",
|
|
52
|
+
"mcp__clay-workspace__search_workspace_history",
|
|
53
|
+
"mcp__clay-workspace__list_workspace_activity",
|
|
54
|
+
"mcp__clay-workspace__propose_project_assignment",
|
|
55
|
+
"mcp__clay-workspace__propose_project_follow_up",
|
|
56
|
+
"mcp__clay-workspace__get_assignment_status",
|
|
57
|
+
"mcp__clay-tools__clay_tool_list",
|
|
58
|
+
"mcp__clay-tools__clay_tool_snapshot",
|
|
59
|
+
"mcp__clay-tools__clay_tool_act",
|
|
60
|
+
"mcp__clay-tools__clay_tool_set",
|
|
61
|
+
"mcp__clay-tools__clay_tool_source",
|
|
47
62
|
// Email: read-side only. Send / reply / mark_read still prompt.
|
|
48
63
|
"mcp__clay-email__clay_read_email",
|
|
49
64
|
"mcp__clay-email__clay_read_email_body",
|
|
@@ -1,313 +1,78 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Tools that let the Clay host agent search and read across the user's
|
|
4
|
-
// entire workspace (sessions, project memory, decision history). Scoped
|
|
5
|
-
// strictly to the active user's data via the projectSessions accessor;
|
|
6
|
-
// the Clay session never reads other users' files.
|
|
7
|
-
//
|
|
8
|
-
// Mounted only on host-agent mate projects (def.hostAgent === true), so
|
|
9
|
-
// regular Mates and project sessions never see these tools.
|
|
10
|
-
//
|
|
11
|
-
// Usage:
|
|
12
|
-
// var clayHistoryMcp = require("./clay-history-mcp-server");
|
|
13
|
-
// var tools = clayHistoryMcp.getToolDefs({ getAllProjectsWithSessions: ..., readSessionRange: ... });
|
|
14
|
-
// var mcpConfig = adapter.createToolServer({ name: "clay-history", version: "1.0.0", tools: tools });
|
|
15
|
-
|
|
16
|
-
var fs = require("fs");
|
|
17
|
-
var path = require("path");
|
|
18
|
-
var sessionSearch = require("./session-search");
|
|
1
|
+
// Compatibility MCP surface for existing builtin Clay prompts. All reads
|
|
2
|
+
// delegate to the same exact-owner workspace query binding as clay-workspace.
|
|
19
3
|
|
|
20
4
|
var z;
|
|
21
5
|
try { z = require("zod"); } catch (e) { z = null; }
|
|
22
6
|
|
|
23
|
-
function
|
|
7
|
+
function field(type, description, optional) {
|
|
24
8
|
if (!z) return {};
|
|
25
|
-
var
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var k = keys[i];
|
|
29
|
-
var p = props[k];
|
|
30
|
-
var field;
|
|
31
|
-
if (p.type === "number") field = z.number();
|
|
32
|
-
else if (p.type === "boolean") field = z.boolean();
|
|
33
|
-
else if (p.enum) field = z.enum(p.enum);
|
|
34
|
-
else field = z.string();
|
|
35
|
-
if (p.description) field = field.describe(p.description);
|
|
36
|
-
if (!required || required.indexOf(k) === -1) field = field.optional();
|
|
37
|
-
shape[k] = field;
|
|
38
|
-
}
|
|
39
|
-
return shape;
|
|
9
|
+
var value = type === "number" ? z.number() : z.string();
|
|
10
|
+
if (description) value = value.describe(description);
|
|
11
|
+
return optional ? value.optional() : value;
|
|
40
12
|
}
|
|
41
13
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/\bdecided\s+to\b/i,
|
|
46
|
-
/\bdecision\b/i,
|
|
47
|
-
/\bgoing\s+with\b/i,
|
|
48
|
-
/\bsettled\s+on\b/i,
|
|
49
|
-
/\bchose\s+to\b/i,
|
|
50
|
-
/\bwill\s+go\s+with\b/i,
|
|
51
|
-
/\blet'?s\s+go\s+with\b/i,
|
|
52
|
-
/\b결정\b/, // Korean "decision"
|
|
53
|
-
/\b정했\b/, // Korean "settled/chose"
|
|
54
|
-
/\b이걸로\s+가/, // Korean "going with this"
|
|
55
|
-
];
|
|
14
|
+
function result(value) {
|
|
15
|
+
return Promise.resolve({ content: [{ type: "text", text: JSON.stringify(value) }] });
|
|
16
|
+
}
|
|
56
17
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new Error("clay-history-mcp-server requires getAllProjectsWithSessions");
|
|
61
|
-
}
|
|
18
|
+
function error(value) {
|
|
19
|
+
return Promise.resolve({ content: [{ type: "text", text: "Error: " + value }], isError: true });
|
|
20
|
+
}
|
|
62
21
|
|
|
63
|
-
|
|
22
|
+
function guarded(workspace, method, mapArgs) {
|
|
23
|
+
return function (args) {
|
|
24
|
+
if (!workspace || workspace.isClay !== true || typeof workspace[method] !== "function") {
|
|
25
|
+
return error("Clay history requires an exact session-bound builtin Clay query.");
|
|
26
|
+
}
|
|
27
|
+
try { return result(workspace[method](mapArgs ? mapArgs(args || {}) : (args || {}))); }
|
|
28
|
+
catch (e) { return error(e && e.message ? e.message : String(e)); }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
64
31
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
handler: function (args) {
|
|
81
|
-
try {
|
|
82
|
-
var query = (args.query || "").trim();
|
|
83
|
-
if (!query) {
|
|
84
|
-
return Promise.resolve({
|
|
85
|
-
content: [{ type: "text", text: "Empty query." }],
|
|
86
|
-
isError: true,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
var maxResults = Math.min(50, Math.max(1, args.maxResults || 20));
|
|
90
|
-
var projectSessions = getAllProjectsWithSessions();
|
|
91
|
-
if (args.projectSlug) {
|
|
92
|
-
projectSessions = projectSessions.filter(function (p) {
|
|
93
|
-
return p.projectSlug === args.projectSlug;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
var sinceMs = parseTime(args.since);
|
|
97
|
-
var untilMs = parseTime(args.until);
|
|
98
|
-
if (sinceMs != null || untilMs != null) {
|
|
99
|
-
projectSessions = projectSessions.map(function (p) {
|
|
100
|
-
var filtered = (p.sessions || []).filter(function (s) {
|
|
101
|
-
var t = s.lastActivity || s.createdAt || 0;
|
|
102
|
-
if (sinceMs != null && t < sinceMs) return false;
|
|
103
|
-
if (untilMs != null && t > untilMs) return false;
|
|
104
|
-
return true;
|
|
105
|
-
});
|
|
106
|
-
return Object.assign({}, p, { sessions: filtered });
|
|
107
|
-
}).filter(function (p) { return p.sessions.length > 0; });
|
|
108
|
-
}
|
|
109
|
-
var results = sessionSearch.searchPalette(projectSessions, query, { maxResults: maxResults });
|
|
110
|
-
if (results.length === 0) {
|
|
111
|
-
return Promise.resolve({
|
|
112
|
-
content: [{ type: "text", text: "No matches for: " + query }],
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
var lines = results.map(function (r) {
|
|
116
|
-
var when = r.lastActivity ? new Date(r.lastActivity).toISOString().slice(0, 10) : "";
|
|
117
|
-
var ref = "[" + r.projectSlug + "/" + r.sessionId + " — " + when + "]";
|
|
118
|
-
var head = r.sessionTitle || "(untitled)";
|
|
119
|
-
var body = r.snippet ? r.snippet.replace(/\s+/g, " ").trim() : "";
|
|
120
|
-
if (body.length > 220) body = body.substring(0, 220) + "...";
|
|
121
|
-
return ref + " " + head + (body ? " — " + body : "");
|
|
122
|
-
});
|
|
123
|
-
return Promise.resolve({
|
|
124
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
125
|
-
});
|
|
126
|
-
} catch (e) {
|
|
127
|
-
return Promise.resolve({
|
|
128
|
-
content: [{ type: "text", text: "Search failed: " + (e.message || String(e)) }],
|
|
129
|
-
isError: true,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
32
|
+
function getToolDefs(deps) {
|
|
33
|
+
var workspace = deps && deps.workspace;
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
name: "search_clay_history",
|
|
37
|
+
description: "Search canonical user and assistant text across projects owned by the current user. Returns sanitized snippets and opaque session references.",
|
|
38
|
+
inputSchema: z ? {
|
|
39
|
+
query: field("string", "Search query."),
|
|
40
|
+
projectSlug: field("string", "Optional project slug.", true),
|
|
41
|
+
maxResults: field("number", "Maximum results, from 1 to 50.", true),
|
|
42
|
+
} : {},
|
|
43
|
+
handler: guarded(workspace, "searchWorkspaceHistory", function (args) {
|
|
44
|
+
if (args.projectSlug) return { projectSlug: args.projectSlug, query: args.query, limit: args.maxResults };
|
|
45
|
+
return { query: args.query, limit: args.maxResults };
|
|
46
|
+
}),
|
|
132
47
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}, ["projectSlug", "sessionId"]),
|
|
147
|
-
handler: function (args) {
|
|
148
|
-
try {
|
|
149
|
-
var projectSlug = args.projectSlug;
|
|
150
|
-
var sessionId = args.sessionId;
|
|
151
|
-
var offset = Math.max(0, args.offset || 0);
|
|
152
|
-
var limit = Math.min(100, Math.max(1, args.limit || 30));
|
|
153
|
-
|
|
154
|
-
var projectSessions = getAllProjectsWithSessions();
|
|
155
|
-
var found = null;
|
|
156
|
-
for (var p = 0; p < projectSessions.length; p++) {
|
|
157
|
-
if (projectSessions[p].projectSlug !== projectSlug) continue;
|
|
158
|
-
var sessions = projectSessions[p].sessions || [];
|
|
159
|
-
for (var s = 0; s < sessions.length; s++) {
|
|
160
|
-
if (sessions[s].localId === sessionId) {
|
|
161
|
-
found = { project: projectSessions[p], session: sessions[s] };
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (found) break;
|
|
166
|
-
}
|
|
167
|
-
if (!found) {
|
|
168
|
-
return Promise.resolve({
|
|
169
|
-
content: [{ type: "text", text: "Session not found: " + projectSlug + "/" + sessionId }],
|
|
170
|
-
isError: true,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
var history = found.session.history || [];
|
|
174
|
-
var slice = history.slice(offset, offset + limit);
|
|
175
|
-
if (slice.length === 0) {
|
|
176
|
-
return Promise.resolve({
|
|
177
|
-
content: [{ type: "text", text: "No turns in window (offset=" + offset + ", limit=" + limit + ", total=" + history.length + ")." }],
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
var out = [];
|
|
181
|
-
out.push("# " + (found.session.title || "untitled") + " — " + projectSlug + "/" + sessionId);
|
|
182
|
-
out.push("Showing turns " + (offset + 1) + "-" + (offset + slice.length) + " of " + history.length + "\n");
|
|
183
|
-
for (var i = 0; i < slice.length; i++) {
|
|
184
|
-
var entry = slice[i];
|
|
185
|
-
var label;
|
|
186
|
-
var text = "";
|
|
187
|
-
if (entry.type === "user_message") {
|
|
188
|
-
label = "USER";
|
|
189
|
-
text = entry.text || "";
|
|
190
|
-
} else if (entry.type === "delta") {
|
|
191
|
-
label = "ASSISTANT";
|
|
192
|
-
text = entry.text || "";
|
|
193
|
-
} else if (entry.type === "tool_executing" || entry.type === "tool_result") {
|
|
194
|
-
label = "TOOL";
|
|
195
|
-
text = (entry.name || "") + (entry.input ? " " + JSON.stringify(entry.input).substring(0, 120) : "");
|
|
196
|
-
} else {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (text.length > 800) text = text.substring(0, 800) + "...";
|
|
200
|
-
out.push("[" + label + "] " + text);
|
|
201
|
-
}
|
|
202
|
-
return Promise.resolve({
|
|
203
|
-
content: [{ type: "text", text: out.join("\n") }],
|
|
204
|
-
});
|
|
205
|
-
} catch (e) {
|
|
206
|
-
return Promise.resolve({
|
|
207
|
-
content: [{ type: "text", text: "read_session failed: " + (e.message || String(e)) }],
|
|
208
|
-
isError: true,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
48
|
+
{
|
|
49
|
+
name: "read_session",
|
|
50
|
+
description: "Read canonical user and assistant turns from an owned session. Raw tool payloads and internal prompts are excluded.",
|
|
51
|
+
inputSchema: z ? {
|
|
52
|
+
projectSlug: field("string", "Exact project slug."),
|
|
53
|
+
sessionId: field("string", "Opaque session reference returned by search_clay_history."),
|
|
54
|
+
offset: field("number", "Legacy numeric turn offset.", true),
|
|
55
|
+
limit: field("number", "Maximum turns, from 1 to 50.", true),
|
|
56
|
+
} : {},
|
|
57
|
+
handler: guarded(workspace, "readProjectSession", function (args) {
|
|
58
|
+
var cursor = args.offset > 0 ? Buffer.from(String(Math.floor(args.offset)), "utf8").toString("base64url") : null;
|
|
59
|
+
return { projectSlug: args.projectSlug, sessionRef: args.sessionId, cursor: cursor, limit: args.limit };
|
|
60
|
+
}),
|
|
211
61
|
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
until: { type: "string", description: "Optional. Latest activity date." },
|
|
225
|
-
maxResults: { type: "number", description: "Optional. Default 15, max 30." },
|
|
226
|
-
}),
|
|
227
|
-
handler: function (args) {
|
|
228
|
-
try {
|
|
229
|
-
var maxResults = Math.min(30, Math.max(1, args.maxResults || 15));
|
|
230
|
-
var sinceMs = parseTime(args.since);
|
|
231
|
-
var untilMs = parseTime(args.until);
|
|
232
|
-
var projectSessions = getAllProjectsWithSessions();
|
|
233
|
-
if (args.projectSlug) {
|
|
234
|
-
projectSessions = projectSessions.filter(function (p) {
|
|
235
|
-
return p.projectSlug === args.projectSlug;
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
var hits = [];
|
|
239
|
-
for (var p = 0; p < projectSessions.length; p++) {
|
|
240
|
-
var proj = projectSessions[p];
|
|
241
|
-
var sessions = proj.sessions || [];
|
|
242
|
-
for (var s = 0; s < sessions.length; s++) {
|
|
243
|
-
var session = sessions[s];
|
|
244
|
-
var t = session.lastActivity || session.createdAt || 0;
|
|
245
|
-
if (sinceMs != null && t < sinceMs) continue;
|
|
246
|
-
if (untilMs != null && t > untilMs) continue;
|
|
247
|
-
var history = session.history || [];
|
|
248
|
-
for (var h = 0; h < history.length; h++) {
|
|
249
|
-
var entry = history[h];
|
|
250
|
-
if (entry.type !== "user_message" && entry.type !== "delta") continue;
|
|
251
|
-
var text = entry.text || "";
|
|
252
|
-
if (!text) continue;
|
|
253
|
-
if (!matchesDecisionPattern(text)) continue;
|
|
254
|
-
hits.push({
|
|
255
|
-
projectSlug: proj.projectSlug,
|
|
256
|
-
projectTitle: proj.projectTitle,
|
|
257
|
-
sessionId: session.localId,
|
|
258
|
-
sessionTitle: session.title || "(untitled)",
|
|
259
|
-
lastActivity: t,
|
|
260
|
-
turnIdx: h,
|
|
261
|
-
turnType: entry.type === "user_message" ? "user" : "assistant",
|
|
262
|
-
text: text,
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
hits.sort(function (a, b) { return b.lastActivity - a.lastActivity; });
|
|
268
|
-
if (hits.length > maxResults) hits = hits.slice(0, maxResults);
|
|
269
|
-
if (hits.length === 0) {
|
|
270
|
-
return Promise.resolve({
|
|
271
|
-
content: [{ type: "text", text: "No decision-pattern matches in scope." }],
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
var lines = hits.map(function (h) {
|
|
275
|
-
var when = h.lastActivity ? new Date(h.lastActivity).toISOString().slice(0, 10) : "";
|
|
276
|
-
var ref = "[" + h.projectSlug + "/" + h.sessionId + " — " + when + "]";
|
|
277
|
-
var snippet = h.text.replace(/\s+/g, " ").trim();
|
|
278
|
-
if (snippet.length > 220) snippet = snippet.substring(0, 220) + "...";
|
|
279
|
-
return ref + " (" + h.turnType + ") " + snippet;
|
|
280
|
-
});
|
|
281
|
-
return Promise.resolve({
|
|
282
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
283
|
-
});
|
|
284
|
-
} catch (e) {
|
|
285
|
-
return Promise.resolve({
|
|
286
|
-
content: [{ type: "text", text: "list_recent_decisions failed: " + (e.message || String(e)) }],
|
|
287
|
-
isError: true,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
62
|
+
{
|
|
63
|
+
name: "list_recent_decisions",
|
|
64
|
+
description: "List recent canonical user or assistant turns containing explicit decision language in the owned workspace.",
|
|
65
|
+
inputSchema: z ? {
|
|
66
|
+
projectSlug: field("string", "Optional project slug.", true),
|
|
67
|
+
since: field("string", "Optional earliest ISO date.", true),
|
|
68
|
+
until: field("string", "Optional latest ISO date.", true),
|
|
69
|
+
maxResults: field("number", "Maximum results, from 1 to 50.", true),
|
|
70
|
+
} : {},
|
|
71
|
+
handler: guarded(workspace, "listRecentDecisions", function (args) {
|
|
72
|
+
return { projectSlug: args.projectSlug, since: args.since, until: args.until, limit: args.maxResults };
|
|
73
|
+
}),
|
|
290
74
|
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return tools;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function parseTime(input) {
|
|
297
|
-
if (input == null || input === "") return null;
|
|
298
|
-
if (typeof input === "number") return input;
|
|
299
|
-
var s = String(input).trim();
|
|
300
|
-
if (/^\d{10,}$/.test(s)) return parseInt(s, 10); // unix ms
|
|
301
|
-
var d = new Date(s);
|
|
302
|
-
var n = d.getTime();
|
|
303
|
-
return isNaN(n) ? null : n;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function matchesDecisionPattern(text) {
|
|
307
|
-
for (var i = 0; i < DECISION_PATTERNS.length; i++) {
|
|
308
|
-
if (DECISION_PATTERNS[i].test(text)) return true;
|
|
309
|
-
}
|
|
310
|
-
return false;
|
|
75
|
+
];
|
|
311
76
|
}
|
|
312
77
|
|
|
313
78
|
module.exports = { getToolDefs: getToolDefs };
|
package/lib/daemon.js
CHANGED
|
@@ -814,7 +814,7 @@ var relay = createServer({
|
|
|
814
814
|
var want = !!value;
|
|
815
815
|
config.matesEnabled = want;
|
|
816
816
|
saveConfig(config);
|
|
817
|
-
console.log("[daemon]
|
|
817
|
+
console.log("[daemon] Project Mate DMs:", want ? "shown" : "hidden", "(web)");
|
|
818
818
|
return { ok: true, matesEnabled: want };
|
|
819
819
|
},
|
|
820
820
|
onGetToolPalettes: function () {
|