@somacheck/vibecheck 0.6.12 → 0.6.14
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/README.md +73 -41
- package/SKILL.md +11 -7
- package/dist/claude-plugin-setup.js +183 -78
- package/dist/cli.js +63 -20
- package/dist/client-setup.js +38 -22
- package/dist/constants.js +1 -1
- package/dist/readiness.js +36 -31
- package/dist/recipes.js +7 -7
- package/dist/server.js +65 -17
- package/package.json +3 -4
- package/recipes/chattermill-research-reflection.md +11 -10
- package/recipes/dovetail-research-reflection.md +2 -2
- package/recipes/great-question-research-reflection.md +5 -4
- package/recipes/maze-research-reflection.md +9 -9
- package/recipes/prolific-research-reflection.md +2 -2
- package/recipes/questionpro-research-reflection.md +6 -3
- package/recipes/typeform-research-reflection.md +3 -3
- package/recipes/user-interviews-research-reflection.md +9 -7
- package/claude-marketplace/.claude-plugin/marketplace.json +0 -22
- package/claude-marketplace/plugins/vibecheck/.claude-plugin/plugin.json +0 -13
- package/claude-marketplace/plugins/vibecheck/hooks/hooks.json +0 -18
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { SupabaseAgentApi } from "./api.js";
|
|
6
|
-
import { claudeChannelLaunchCommand, clientDisplayName, detectInstalledClients, detectLegacyHostedRegistration, LocalCommandRunner, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, parseClientChoice, preflightClientPersistence, registerClient, singleNonInteractiveClientSelection, } from "./client-setup.js";
|
|
6
|
+
import { claudeChannelLaunchCommand, clientDisplayName, detectInstalledClients, detectLegacyHostedRegistration, isClientInstalled, LocalCommandRunner, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, migrateClaudeDirectRegistration, parseClientChoice, preflightClientPersistence, registerClient, singleNonInteractiveClientSelection, } from "./client-setup.js";
|
|
7
7
|
import { configureClaudeContinuationPlugin } from "./claude-plugin-setup.js";
|
|
8
8
|
import { readConfig } from "./config.js";
|
|
9
9
|
import { awaitClaudeVibecheckResult } from "./claude-hook.js";
|
|
@@ -76,6 +76,32 @@ async function promptForClients(installed) {
|
|
|
76
76
|
}
|
|
77
77
|
async function configureClients(clients) {
|
|
78
78
|
for (const client of clients) {
|
|
79
|
+
if (client === "claude") {
|
|
80
|
+
if (!(await isClientInstalled(client, runner))) {
|
|
81
|
+
output(`○ ${clientDisplayName(client)} is not installed; skipped.`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
|
|
85
|
+
if (plugin.status === "failed") {
|
|
86
|
+
throw new ClientPreflightError("Could not configure the SomaCheck Channel plugin for Claude Code.");
|
|
87
|
+
}
|
|
88
|
+
const migration = await migrateClaudeDirectRegistration(runner);
|
|
89
|
+
if (migration === "needs_update") {
|
|
90
|
+
throw new ClientPreflightError([
|
|
91
|
+
'Claude Code has a user-owned MCP server named "vibecheck"; it was not changed.',
|
|
92
|
+
`Remove it only if it is obsolete: ${manualRemoveCommand(client)}`,
|
|
93
|
+
].join("\n"));
|
|
94
|
+
}
|
|
95
|
+
if (migration === "failed" || migration === "not_installed") {
|
|
96
|
+
throw new ClientPreflightError("Could not remove the superseded direct SomaCheck MCP registration.");
|
|
97
|
+
}
|
|
98
|
+
output(`✓ SomaCheck Channel plugin ${plugin.status === "installed" ? "was configured" : "is current"} for Claude Code.`);
|
|
99
|
+
output("✓ Automatic SomaCheck marketplace updates are enabled for future reviewed releases.");
|
|
100
|
+
if (migration === "removed")
|
|
101
|
+
output("✓ Removed the superseded direct MCP registration.");
|
|
102
|
+
output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
79
105
|
const result = await registerClient(client, runner);
|
|
80
106
|
if (result.status === "registered") {
|
|
81
107
|
output(`✓ Configured ${clientDisplayName(client)}.`);
|
|
@@ -96,14 +122,6 @@ async function configureClients(clients) {
|
|
|
96
122
|
else {
|
|
97
123
|
output(`✗ Could not configure ${clientDisplayName(client)} automatically.`);
|
|
98
124
|
}
|
|
99
|
-
if (client === "claude" && ["registered", "upgraded", "already_registered"].includes(result.status)) {
|
|
100
|
-
const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
|
|
101
|
-
if (plugin.status === "failed") {
|
|
102
|
-
throw new ClientPreflightError("Could not configure automatic SomaCheck results for Claude Code. No pairing code was used.");
|
|
103
|
-
}
|
|
104
|
-
output(`✓ Automatic Claude result continuation ${plugin.status === "installed" ? "was configured" : "is ready"}.`);
|
|
105
|
-
output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
|
|
106
|
-
}
|
|
107
125
|
}
|
|
108
126
|
}
|
|
109
127
|
async function preflightLinkClients(clients, interactive) {
|
|
@@ -144,6 +162,42 @@ async function preflightLinkClients(clients, interactive) {
|
|
|
144
162
|
].join("\n"));
|
|
145
163
|
}
|
|
146
164
|
output(`Checking ${clientDisplayName(client)} before redeeming the pairing code...`);
|
|
165
|
+
if (client === "claude") {
|
|
166
|
+
if (!(await isClientInstalled(client, runner))) {
|
|
167
|
+
throw new ClientPreflightError([
|
|
168
|
+
"Not linking here: Claude Code is not installed or its CLI is unavailable.",
|
|
169
|
+
"The pairing code has NOT been used.",
|
|
170
|
+
].join("\n"));
|
|
171
|
+
}
|
|
172
|
+
const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
|
|
173
|
+
if (plugin.status === "failed") {
|
|
174
|
+
throw new ClientPreflightError([
|
|
175
|
+
"Not linking here: the SomaCheck Channel plugin could not be installed and verified for Claude Code.",
|
|
176
|
+
"The pairing code has NOT been used. Repair Claude's plugin setup, then run the link command again.",
|
|
177
|
+
].join("\n"));
|
|
178
|
+
}
|
|
179
|
+
const migration = await migrateClaudeDirectRegistration(runner);
|
|
180
|
+
if (migration === "needs_update") {
|
|
181
|
+
throw new ClientPreflightError([
|
|
182
|
+
'Not linking here: Claude Code has a user-owned MCP entry named "vibecheck".',
|
|
183
|
+
"It was not changed because its command does not match a SomaCheck-managed registration.",
|
|
184
|
+
`Remove it only if it is obsolete: ${manualRemoveCommand(client)}`,
|
|
185
|
+
"The pairing code has NOT been used.",
|
|
186
|
+
].join("\n"));
|
|
187
|
+
}
|
|
188
|
+
if (migration === "failed" || migration === "not_installed") {
|
|
189
|
+
throw new ClientPreflightError([
|
|
190
|
+
"Not linking here: the superseded direct SomaCheck MCP registration could not be removed safely.",
|
|
191
|
+
"The pairing code has NOT been used.",
|
|
192
|
+
].join("\n"));
|
|
193
|
+
}
|
|
194
|
+
output("✓ Claude Code has one exact, plugin-scoped SomaCheck Channel server.");
|
|
195
|
+
output("✓ Automatic SomaCheck marketplace updates are enabled for future reviewed releases.");
|
|
196
|
+
if (migration === "removed")
|
|
197
|
+
output("✓ Removed the superseded direct MCP registration before redeeming the pairing code.");
|
|
198
|
+
output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
147
201
|
const preflight = await preflightClientPersistence(client, runner);
|
|
148
202
|
if (preflight.status === "ready") {
|
|
149
203
|
output(`✓ ${clientDisplayName(client)} already has the exact SomaCheck MCP registration.`);
|
|
@@ -169,17 +223,6 @@ async function preflightLinkClients(clients, interactive) {
|
|
|
169
223
|
"run the link command again.",
|
|
170
224
|
].join("\n"));
|
|
171
225
|
}
|
|
172
|
-
if (client === "claude") {
|
|
173
|
-
const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
|
|
174
|
-
if (plugin.status === "failed") {
|
|
175
|
-
throw new ClientPreflightError([
|
|
176
|
-
"Not linking here: automatic SomaCheck result continuation could not be configured for Claude Code.",
|
|
177
|
-
"The pairing code has NOT been used. Repair Claude's plugin setup, then run the link command again.",
|
|
178
|
-
].join("\n"));
|
|
179
|
-
}
|
|
180
|
-
output("✓ Automatic Claude result continuation is ready before redeeming the pairing code.");
|
|
181
|
-
output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
|
|
182
|
-
}
|
|
183
226
|
}
|
|
184
227
|
return selectedClients;
|
|
185
228
|
}
|
package/dist/client-setup.js
CHANGED
|
@@ -30,13 +30,13 @@ function mcpServerArgs(client) {
|
|
|
30
30
|
return client === "claude" ? [...args, "--channel"] : args;
|
|
31
31
|
}
|
|
32
32
|
export function claudeChannelLaunchCommand() {
|
|
33
|
-
return
|
|
33
|
+
return "claude --channels plugin:vibecheck@somacheck";
|
|
34
34
|
}
|
|
35
35
|
export function manualSetupCommand(client) {
|
|
36
36
|
if (client === "codex") {
|
|
37
37
|
return `codex mcp add ${MCP_SERVER_NAME} -- npx ${mcpServerArgs(client).join(" ")}`;
|
|
38
38
|
}
|
|
39
|
-
return `
|
|
39
|
+
return `npx -y ${PACKAGE_SPEC} setup claude`;
|
|
40
40
|
}
|
|
41
41
|
export function manualRemoveCommand(client) {
|
|
42
42
|
return client === "codex"
|
|
@@ -143,30 +143,46 @@ function isManagedSomaCheckRegistration(client, stdout) {
|
|
|
143
143
|
// earlier exact 0.6 patch release. Custom wrappers, extra arguments,
|
|
144
144
|
// disabled entries, later versions, and registrations naming the other
|
|
145
145
|
// client remain user-owned.
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
||
|
|
150
|
-
||
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
146
|
+
const version = parseExactSemver(packageArg.slice("@somacheck/vibecheck@".length));
|
|
147
|
+
const currentVersion = parseExactSemver(PACKAGE_SPEC.slice("@somacheck/vibecheck@".length));
|
|
148
|
+
if (version === null || currentVersion === null
|
|
149
|
+
|| compareSemver(version, [0, 5, 0]) < 0
|
|
150
|
+
|| compareSemver(version, currentVersion) >= 0)
|
|
151
|
+
return false;
|
|
152
|
+
const expected = ["-y", packageArg, "serve", "--client", client];
|
|
153
|
+
if (client === "claude" && compareSemver(version, [0, 6, 12]) >= 0) {
|
|
154
|
+
expected.push("--channel");
|
|
155
|
+
}
|
|
156
|
+
return args.length === expected.length
|
|
157
|
+
&& args.every((arg, index) => arg === expected[index]);
|
|
158
|
+
}
|
|
159
|
+
function parseExactSemver(value) {
|
|
160
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(value);
|
|
161
|
+
return match === null ? null : [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
162
|
+
}
|
|
163
|
+
function compareSemver(left, right) {
|
|
164
|
+
for (let index = 0; index < 3; index += 1) {
|
|
165
|
+
if (left[index] !== right[index])
|
|
166
|
+
return left[index] - right[index];
|
|
167
|
+
}
|
|
168
|
+
return 0;
|
|
166
169
|
}
|
|
167
170
|
export async function isClientRegistered(client, runner) {
|
|
168
171
|
return (await clientRegistrationState(client, runner)) === "current";
|
|
169
172
|
}
|
|
173
|
+
export async function migrateClaudeDirectRegistration(runner) {
|
|
174
|
+
if (!(await isClientInstalled("claude", runner)))
|
|
175
|
+
return "not_installed";
|
|
176
|
+
const state = await clientRegistrationState("claude", runner);
|
|
177
|
+
if (state === "missing")
|
|
178
|
+
return "absent";
|
|
179
|
+
if (state === "needs_update")
|
|
180
|
+
return "needs_update";
|
|
181
|
+
const removed = await runner.run("claude", ["mcp", "remove", "--scope", "user", MCP_SERVER_NAME]);
|
|
182
|
+
if (removed.exitCode !== 0)
|
|
183
|
+
return "failed";
|
|
184
|
+
return (await clientRegistrationState("claude", runner)) === "missing" ? "removed" : "failed";
|
|
185
|
+
}
|
|
170
186
|
export async function detectInstalledClients(runner) {
|
|
171
187
|
const clients = ["codex", "claude"];
|
|
172
188
|
const installed = await Promise.all(clients.map(async (client) => ({
|
package/dist/constants.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export const SUPABASE_URL = "https://pbldcmniommltbdwuykk.supabase.co";
|
|
3
3
|
export const SUPABASE_PUBLISHABLE_KEY = "sb_publishable_af-lUNI2FqEcb-oGy-4uxQ_cnm6kY85";
|
|
4
4
|
export const PACKAGE_NAME = "@somacheck/vibecheck";
|
|
5
|
-
export const PACKAGE_VERSION = "0.6.
|
|
5
|
+
export const PACKAGE_VERSION = "0.6.14";
|
|
6
6
|
export const PACKAGE_SPEC = `${PACKAGE_NAME}@${PACKAGE_VERSION}`;
|
|
7
7
|
export const SOMACHECK_SETUP_URL = "https://testflight.apple.com/join/C4mAH3zz";
|
|
8
8
|
export const MCP_SERVER_NAME = "vibecheck";
|
package/dist/readiness.js
CHANGED
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { readConfig } from "./config.js";
|
|
2
|
+
import { isClaudeChannelPluginConfigured, isClaudeMarketplaceAutoUpdateConfigured, } from "./claude-plugin-setup.js";
|
|
4
3
|
import { SomaCheckCompatibilityError, SomaCheckHttpError } from "./api.js";
|
|
5
|
-
import { clientDisplayName, clientRegistrationState, detectLegacyHostedRegistration, isClientInstalled, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, } from "./client-setup.js";
|
|
4
|
+
import { claudeChannelLaunchCommand, clientDisplayName, clientRegistrationState, detectLegacyHostedRegistration, isClientInstalled, manualLegacyHostedRemoveCommand, manualRemoveCommand, manualSetupCommand, } from "./client-setup.js";
|
|
6
5
|
import { BACKEND_PROTOCOL_VERSION, PACKAGE_VERSION, SOMACHECK_SETUP_URL } from "./constants.js";
|
|
7
|
-
function vibecheckRegistrationCount(value) {
|
|
8
|
-
if (!value || typeof value !== "object")
|
|
9
|
-
return 0;
|
|
10
|
-
let count = 0;
|
|
11
|
-
for (const [key, child] of Object.entries(value)) {
|
|
12
|
-
if (key === "mcpServers" && child && typeof child === "object" && !Array.isArray(child)) {
|
|
13
|
-
count += Object.keys(child).filter((name) => name.startsWith("vibecheck")).length;
|
|
14
|
-
}
|
|
15
|
-
count += vibecheckRegistrationCount(child);
|
|
16
|
-
}
|
|
17
|
-
return count;
|
|
18
|
-
}
|
|
19
|
-
async function claudeVibecheckRegistrationCount(home) {
|
|
20
|
-
try {
|
|
21
|
-
return vibecheckRegistrationCount(JSON.parse(await readFile(join(home, ".claude.json"), "utf8")));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
6
|
export async function checkReadiness(dependencies) {
|
|
28
7
|
let token;
|
|
29
8
|
try {
|
|
@@ -71,11 +50,6 @@ export async function checkReadiness(dependencies) {
|
|
|
71
50
|
}
|
|
72
51
|
}
|
|
73
52
|
const backendReachable = statusProbeOk && contextProbeOk;
|
|
74
|
-
const claudeRegistrationCount = await claudeVibecheckRegistrationCount(dependencies.home);
|
|
75
|
-
if (claudeRegistrationCount > 1) {
|
|
76
|
-
dependencies.output(`○ Multiple Claude Code registrations whose names start with "vibecheck" were found (${claudeRegistrationCount}).`);
|
|
77
|
-
dependencies.output(" Remove stale entries from ~/.claude.json so only the current user-scope vibecheck registration remains.");
|
|
78
|
-
}
|
|
79
53
|
const clients = ["codex", "claude"];
|
|
80
54
|
let installedClientCount = 0;
|
|
81
55
|
let hasLegacyHostedRegistration = false;
|
|
@@ -95,6 +69,29 @@ export async function checkReadiness(dependencies) {
|
|
|
95
69
|
dependencies.output(` Optional manual cleanup: ${manualLegacyHostedRemoveCommand(client)}`);
|
|
96
70
|
}
|
|
97
71
|
const registration = await clientRegistrationState(client, dependencies.runner);
|
|
72
|
+
if (client === "claude") {
|
|
73
|
+
const pluginConfigured = await isClaudeChannelPluginConfigured(dependencies.runner);
|
|
74
|
+
if (!pluginConfigured) {
|
|
75
|
+
dependencies.output("✗ Claude Code does not have the exact SomaCheck Channel plugin.");
|
|
76
|
+
dependencies.output(` Run: ${manualSetupCommand(client)}`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!(await isClaudeMarketplaceAutoUpdateConfigured(dependencies.home))) {
|
|
80
|
+
dependencies.output("✗ Automatic SomaCheck plugin updates are not enabled for Claude Code.");
|
|
81
|
+
dependencies.output(` Run: ${manualSetupCommand(client)}`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (registration !== "missing") {
|
|
85
|
+
dependencies.output('✗ Claude Code also has a direct MCP server named "vibecheck".');
|
|
86
|
+
dependencies.output(" The plugin is installed, but the duplicate direct tool surface must be removed.");
|
|
87
|
+
dependencies.output(` Run: ${manualRemoveCommand(client)}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
registeredClients.push(client);
|
|
91
|
+
dependencies.output("✓ Claude Code has one exact, plugin-scoped SomaCheck Channel server.");
|
|
92
|
+
dependencies.output("✓ Automatic SomaCheck marketplace updates are enabled.");
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
98
95
|
if (registration === "current") {
|
|
99
96
|
registeredClients.push(client);
|
|
100
97
|
dependencies.output(`✓ ${clientDisplayName(client)} is configured.`);
|
|
@@ -156,9 +153,17 @@ export async function checkReadiness(dependencies) {
|
|
|
156
153
|
dependencies.output('✗ Remove the legacy "somacheck" connector before this setup can be Ready.');
|
|
157
154
|
}
|
|
158
155
|
const ready = readyClients.length > 0;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
if (ready) {
|
|
157
|
+
dependencies.output("Setup checks pass. Restart your agent client, then ask it to check your SomaCheck status.");
|
|
158
|
+
if (readyClients.includes("claude")) {
|
|
159
|
+
dependencies.output("○ Doctor verifies Claude's installed plugin and backend compatibility, not a running Channel session.");
|
|
160
|
+
dependencies.output(` Start Claude with: ${claudeChannelLaunchCommand()}`);
|
|
161
|
+
dependencies.output(" Team/Enterprise use also requires the administrator to allowlist this plugin.");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
dependencies.output("Setup is not complete yet. Fix the items marked ✗, then run: vibecheck doctor");
|
|
166
|
+
}
|
|
162
167
|
return { linked: true, backendReachable, backendCompatible, registeredClients, readyClients, ready };
|
|
163
168
|
}
|
|
164
169
|
//# sourceMappingURL=readiness.js.map
|
package/dist/recipes.js
CHANGED
|
@@ -52,8 +52,8 @@ export const RECIPE_CATALOG = [
|
|
|
52
52
|
"The product or integration owner has provisioned an organization-authorized Maze connection or partner sandbox at https://connect.maze.co/mcp; running this recipe does not ask the researcher to sign up, upgrade, administer access, or populate Maze data.",
|
|
53
53
|
"The provisioned workspace supplies one named existing study with safe metadata or an aggregate summary; if it does not, the Maze half remains untested and the researcher still receives a copy-ready plan.",
|
|
54
54
|
],
|
|
55
|
-
starterPrompt: "Use Maze and SomaCheck to help me reflect on my own interpretation plan for one named Maze study. I am the researcher and I am holding the phone. The study is [STUDY NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Maze MCP at https://connect.maze.co/mcp. Read only that study's safe metadata or one aggregate summary. Do not retrieve transcripts, recordings, heatmaps, click maps, mission answers, participant identifiers, contact fields, demographic exports, free text, or row-level data. Label what Maze literally reports separately from your interpretation, then offer two defensible framings. Offer one short first-person proposition about my own interpretation plan and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about a participant. Ask what I choose in
|
|
56
|
-
successCondition: "The researcher receives at most one optional signal about their own proposition and
|
|
55
|
+
starterPrompt: "Use Maze and SomaCheck to help me reflect on my own interpretation plan for one named Maze study. I am the researcher and I am holding the phone. The study is [STUDY NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Maze MCP at https://connect.maze.co/mcp. Read only that study's safe metadata or one aggregate summary. Do not retrieve transcripts, recordings, heatmaps, click maps, mission answers, participant identifiers, contact fields, demographic exports, free text, or row-level data. Label what Maze literally reports separately from your interpretation, then offer two defensible framings. Offer one short first-person proposition about my own interpretation plan and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about a participant. Ask what I choose and accept any explicit response available in this interface. Keep the proposition, reading, confidence, and confirmation out of Maze. Do not create, edit, publish, delete, archive, export, share, tag, comment, or otherwise write to Maze. If the preconfigured connection or a safe runtime read is absent, label the Maze half untested, return a copy-ready interpretation plan, and tell the integration owner what capability is missing. Do not ask me to sign up, upgrade, administer access, or populate Maze data.",
|
|
56
|
+
successCondition: "The researcher receives at most one optional signal about their own proposition and explicitly indicates their choice after a real metadata or aggregate read from a preconfigured workspace; no participant data or SomaCheck output enters Maze, and the Maze workspace remains unchanged.",
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
id: "chattermill-research-reflection-v1",
|
|
@@ -66,8 +66,8 @@ export const RECIPE_CATALOG = [
|
|
|
66
66
|
"The product or integration owner has provisioned an organization-authorized Chattermill connection or partner sandbox at https://app.chattermill.com/mcp with mcp:read; running this recipe does not ask the researcher to sign up, purchase access, administer access, or populate Chattermill data.",
|
|
67
67
|
"The provisioned workspace supplies one named existing project for which get_metrics or generate_highlights is sufficient; get_feedback, search_observations, identifier-bearing attributes, and free text remain prohibited.",
|
|
68
68
|
],
|
|
69
|
-
starterPrompt: "Use Chattermill and SomaCheck to help me reflect on my own interpretation of one named project's customer-feedback insights. I am the researcher and I am holding the phone. The project is [PROJECT NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Chattermill MCP at https://app.chattermill.com/mcp. Read only aggregate metrics with get_metrics or generated highlights with generate_highlights, plus the minimum discovery metadata needed to build that query. Never call get_feedback or search_observations, enumerate identifier-bearing attributes, or surface individual feedback, source quotes, respondent identifiers, contact fields, or free text. Label what the aggregate output literally reports separately from your interpretation, then offer two defensible interpretations. Offer one short first-person proposition about my own interpretation or next step and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about an individual customer. Ask what I choose in
|
|
70
|
-
successCondition: "The researcher receives at most one optional signal about their own proposition and
|
|
69
|
+
starterPrompt: "Use Chattermill and SomaCheck to help me reflect on my own interpretation of one named project's customer-feedback insights. I am the researcher and I am holding the phone. The project is [PROJECT NAME OR ID PROVIDED BY THE WORKSPACE OWNER]. Use only the preconfigured official hosted read-only Chattermill MCP at https://app.chattermill.com/mcp. Read only aggregate metrics with get_metrics or generated highlights with generate_highlights, plus the minimum discovery metadata needed to build that query. Never call get_feedback or search_observations, enumerate identifier-bearing attributes, or surface individual feedback, source quotes, respondent identifiers, contact fields, or free text. Label what the aggregate output literally reports separately from your interpretation, then offer two defensible interpretations. Offer one short first-person proposition about my own interpretation or next step and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about an individual customer. Ask what I choose and accept any explicit response available in this interface. Keep the proposition, reading, confidence, and confirmation out of Chattermill. Do not create, edit, publish, delete, share, export, tag, comment, or otherwise write to Chattermill. If the preconfigured connection, mcp:read, or a safe aggregate runtime tool is absent, label the Chattermill half untested, return a copy-ready interpretation, and tell the integration owner what capability is missing. Do not ask me to sign up, purchase access, administer access, or populate Chattermill data.",
|
|
70
|
+
successCondition: "The researcher receives at most one optional signal about their own proposition and explicitly indicates their choice after a real aggregate or generated-summary read from a preconfigured workspace; no individual feedback or SomaCheck output enters the cross-MCP flow, and Chattermill remains unchanged.",
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
id: "user-interviews-research-reflection-v1",
|
|
@@ -80,8 +80,8 @@ export const RECIPE_CATALOG = [
|
|
|
80
80
|
"The planning workflow requires no User Interviews account or connector because it calls no platform data or action tool.",
|
|
81
81
|
"For partner integration validation only, the product or integration owner may supply a preconfigured partner sandbox whose runtime capability list can be inspected without invoking a project, recruitment, participant-data, contact, publication, or spend tool; the researcher is never asked to request access or populate platform data.",
|
|
82
82
|
],
|
|
83
|
-
starterPrompt: "Use User Interviews and SomaCheck to help me prepare one clearly named, participant-free test-project plan without changing User Interviews. I am the researcher and I am holding the phone. The study idea is [STUDY IDEA]. Work only from what I provide here. If a product or integration owner has supplied a preconfigured official User Interviews partner sandbox, you may inspect its capability names and schemas, but do not call any platform data or action tool. The planning workflow does not require me to sign up, request access, purchase a plan, or populate User Interviews data. Do not retrieve candidates, participants, screeners, responses, profiles, identifiers, messages, recordings, transcripts, session data, or other workspace data. Propose two neutral study framings and show the exact signal-free project copy. Then offer one short first-person proposition about my own preferred direction and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about another person. Ask what I choose in
|
|
84
|
-
successCondition: "The researcher receives at most one optional signal about their own proposition and
|
|
83
|
+
starterPrompt: "Use User Interviews and SomaCheck to help me prepare one clearly named, participant-free test-project plan without changing User Interviews. I am the researcher and I am holding the phone. The study idea is [STUDY IDEA]. Work only from what I provide here. If a product or integration owner has supplied a preconfigured official User Interviews partner sandbox, you may inspect its capability names and schemas, but do not call any platform data or action tool. The planning workflow does not require me to sign up, request access, purchase a plan, or populate User Interviews data. Do not retrieve candidates, participants, screeners, responses, profiles, identifiers, messages, recordings, transcripts, session data, or other workspace data. Propose two neutral study framings and show the exact signal-free project copy. Then offer one short first-person proposition about my own preferred direction and wait for my acceptance before any proactive SomaCheck ask. Treat Aligned or Unaligned plus confidence as context, not truth, diagnosis, authorization, or evidence about another person. Ask what I choose and accept any explicit response available in this interface. Keep the proposition, reading, confidence, and confirmation out of User Interviews. Return a copy-ready project payload only. Do not create, edit, recruit, invite, message, schedule, screen, launch, publish, attach incentives, spend, read participant data, or call a broader or undocumented tool.",
|
|
84
|
+
successCondition: "The researcher receives at most one optional signal about their own proposition and explicitly indicates their choice; the agent returns a copy-ready signal-free test-project plan, invokes no User Interviews data or action tool, and performs no recruitment, participant read, contact, mutation, launch, publication, incentive, payment, or spend.",
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
id: "sprig-research-reflection-v1",
|
|
@@ -108,7 +108,7 @@ export const RECIPE_CATALOG = [
|
|
|
108
108
|
"The product or integration owner has provisioned an organization-authorized Great Question connection or partner sandbox at https://greatquestion.co/api/mcp/v1 with PII hiding enabled; running this recipe does not ask the researcher to sign up, upgrade, purchase, administer access, or populate Great Question data.",
|
|
109
109
|
"The owner supplies one participant-free test study, and the client restricts the platform half to get_survey_study for that exact artifact; no discovery or mutation tool is approved.",
|
|
110
110
|
],
|
|
111
|
-
starterPrompt: "Use Great Question and SomaCheck to help me reflect on a five-question test survey plan without changing Great Question. I am the researcher and I am holding the phone. The product or integration owner has provisioned a partner sandbox and named this participant-free test study: [TEST STUDY NAME OR ID]. Read only that study's safe title, purpose, and question structure with get_survey_study. Do not search or list the workspace, and do not read candidates, participants, screeners, responses, sessions, transcripts, recordings, highlights, insights, reels, or other workspace data. Propose two neutral study framings and show their exact questions. Then offer one short first-person proposition about my own preferred framing and wait for my acceptance before any proactive SomaCheck ask. Give the agent the Aligned or Unaligned result plus confidence as context and let it use that context with its judgment. Ask what I choose in
|
|
111
|
+
starterPrompt: "Use Great Question and SomaCheck to help me reflect on a five-question test survey plan without changing Great Question. I am the researcher and I am holding the phone. The product or integration owner has provisioned a partner sandbox and named this participant-free test study: [TEST STUDY NAME OR ID]. Read only that study's safe title, purpose, and question structure with get_survey_study. Do not search or list the workspace, and do not read candidates, participants, screeners, responses, sessions, transcripts, recordings, highlights, insights, reels, or other workspace data. Propose two neutral study framings and show their exact questions. Then offer one short first-person proposition about my own preferred framing and wait for my acceptance before any proactive SomaCheck ask. Give the agent the Aligned or Unaligned result plus confidence as context and let it use that context with its judgment. Ask what I choose and accept any explicit response available in this interface. Keep the proposition, reading, confidence, and my confirmation out of Great Question. Return a signal-free, copy-ready research plan only. Do not create, update, delete, recruit, invite, message, schedule, incentivize, launch, publish, or otherwise write to Great Question. Do not ask me to sign up, upgrade, purchase, administer access, or populate Great Question data.",
|
|
112
112
|
successCondition: "The researcher receives at most one optional signal, states a choice, and receives a copy-ready five-question plan; only get_survey_study reads the exact owner-supplied test artifact, Great Question remains unchanged, and no recruitment or participant data is accessed.",
|
|
113
113
|
},
|
|
114
114
|
{
|
package/dist/server.js
CHANGED
|
@@ -20,6 +20,11 @@ const LIVE_ASK_POLL_DELAYS_MS = [
|
|
|
20
20
|
4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000, 4_000,
|
|
21
21
|
];
|
|
22
22
|
const CLAUDE_CHANNEL_POLL_INTERVAL_MS = 2_000;
|
|
23
|
+
const CLAUDE_CHANNEL_ACTIVE_POLL_WINDOW_MS = 30 * 60 * 1_000;
|
|
24
|
+
const CLAUDE_CHANNEL_EXTENDED_POLL_WINDOW_MS = 2 * 60 * 60 * 1_000;
|
|
25
|
+
const CLAUDE_CHANNEL_ACTIVE_MAX_POLL_INTERVAL_MS = 15 * 1_000;
|
|
26
|
+
const CLAUDE_CHANNEL_EXTENDED_MAX_POLL_INTERVAL_MS = 30 * 1_000;
|
|
27
|
+
const CLAUDE_CHANNEL_LONG_MAX_POLL_INTERVAL_MS = 60 * 1_000;
|
|
23
28
|
const CLAUDE_CHANNEL_MAX_WATCH_MS = 15 * 60 * 1_000;
|
|
24
29
|
const CLAUDE_CHANNEL_RESULT_TIMEOUT_MS = 10_000;
|
|
25
30
|
function abortAwareSleep(milliseconds, signal) {
|
|
@@ -126,11 +131,10 @@ const contextShareSchema = {
|
|
|
126
131
|
const SERVER_INSTRUCTIONS = [
|
|
127
132
|
"SomaCheck lets you ask your person for a vibecheck.",
|
|
128
133
|
"Offer one when useful or when asked.",
|
|
129
|
-
"When asked, choose and send a useful first-person statement from available context
|
|
130
|
-
"Only request_vibecheck creates an immediate phone ask; post_vibecheck_statement
|
|
131
|
-
"Help them gain insight from the context you have.",
|
|
134
|
+
"When asked, choose and send a useful first-person statement from available context.",
|
|
135
|
+
"Only request_vibecheck creates an immediate phone ask, one at a time; use post_vibecheck_statement only when the person asks to stock reflections for later.",
|
|
132
136
|
"Gesture and optional feedback are context, not authorization.",
|
|
133
|
-
"
|
|
137
|
+
"Never infer the SomaCheck identity from the agent login.",
|
|
134
138
|
"Use your judgment.",
|
|
135
139
|
"Never include secrets, raw private content, diagnostic claims, or assess anyone else.",
|
|
136
140
|
].join(" ");
|
|
@@ -193,7 +197,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
193
197
|
});
|
|
194
198
|
server.registerTool("get_vibecheck_status", {
|
|
195
199
|
title: "Vibecheck Status",
|
|
196
|
-
description: "Read the
|
|
200
|
+
description: "Read the SomaCheck database feed before posting. Reports available proposition capacity, when routine replenishment is due, and whether this is the agent's first contact. Database state does not verify phone display.",
|
|
197
201
|
inputSchema: {},
|
|
198
202
|
outputSchema: statusSchema,
|
|
199
203
|
annotations: {
|
|
@@ -251,7 +255,7 @@ export function createVibecheckServer(dependencies) {
|
|
|
251
255
|
});
|
|
252
256
|
server.registerTool("post_vibecheck_statement", {
|
|
253
257
|
title: "Post Vibecheck Statement",
|
|
254
|
-
description: "
|
|
258
|
+
description: "When the person asks to stock reflections for later, or has explicitly authorized scheduled stocking, add one to three optional personalized reflections under Settings → Vibe Checks and return immediately. One can become database-current for Home; the rest stay Up next without extra pushes. This does not verify phone display or delivery. Never call this to add follow-up asks after request_vibecheck. Call get_vibecheck_status first; propositions_needed is the available maximum, not a quota. Submit only genuinely useful statements and never invent extras to fill capacity. A still-active scheduled authorization does not require repeated consent each run.",
|
|
255
259
|
inputSchema: {
|
|
256
260
|
statements: z.array(z.string().min(1)).min(1).max(3)
|
|
257
261
|
.describe("Distinct personalized statements for this person to test, ordered most useful first."),
|
|
@@ -272,14 +276,14 @@ export function createVibecheckServer(dependencies) {
|
|
|
272
276
|
return {
|
|
273
277
|
content: [{
|
|
274
278
|
type: "text",
|
|
275
|
-
text: `Added ${created.length} under Settings → Vibe Checks: ${presented}
|
|
279
|
+
text: `Added ${created.length} under Settings → Vibe Checks: ${presented} database-current, ${queued} queued. This is asynchronous feed inventory, not verified phone display or an immediate request.`,
|
|
276
280
|
}],
|
|
277
281
|
structuredContent: { propositions: created },
|
|
278
282
|
};
|
|
279
283
|
}
|
|
280
284
|
catch (error) {
|
|
281
285
|
return failure(error instanceof StatementPendingError
|
|
282
|
-
? "
|
|
286
|
+
? "The database feed cannot accept those propositions within its available capacity. Check status again after a check-in, refresh, or expiry."
|
|
283
287
|
: operationalFailureText("add SomaCheck propositions", error));
|
|
284
288
|
}
|
|
285
289
|
});
|
|
@@ -420,14 +424,34 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
420
424
|
const options = dependencies.claudeChannelWatch || undefined;
|
|
421
425
|
const now = options?.now ?? Date.now;
|
|
422
426
|
const sleep = options?.sleep ?? unrefSleep;
|
|
423
|
-
const
|
|
427
|
+
const initialInterval = Math.max(1, options?.pollIntervalMs ?? CLAUDE_CHANNEL_POLL_INTERVAL_MS);
|
|
428
|
+
const startedAt = now();
|
|
424
429
|
const maxWatchMs = options?.maxWatchMs ?? CLAUDE_CHANNEL_MAX_WATCH_MS;
|
|
425
430
|
const parsedExpiry = created.expires_at === null ? Number.NaN : Date.parse(created.expires_at);
|
|
426
|
-
const
|
|
431
|
+
const hasExpiry = Number.isFinite(parsedExpiry);
|
|
432
|
+
// A valid expiry is authoritative in production. The short grace window
|
|
433
|
+
// permits one result read after expiry, when the API transitions a still-
|
|
434
|
+
// pending request to its terminal state. maxWatchMs remains an explicit
|
|
435
|
+
// deterministic-test cap, but is never an implicit production cap.
|
|
436
|
+
const expiryDeadline = hasExpiry
|
|
437
|
+
? Math.max(startedAt, parsedExpiry) + CLAUDE_CHANNEL_RESULT_TIMEOUT_MS
|
|
438
|
+
: startedAt + maxWatchMs;
|
|
439
|
+
const deadline = options?.maxWatchMs === undefined
|
|
440
|
+
? expiryDeadline
|
|
441
|
+
: Math.min(expiryDeadline, startedAt + maxWatchMs);
|
|
442
|
+
let interval = initialInterval;
|
|
427
443
|
let consecutiveReadFailures = 0;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
444
|
+
// Preserve the explicit zero-duration test switch without issuing a
|
|
445
|
+
// surprising immediate read.
|
|
446
|
+
if (deadline <= startedAt)
|
|
447
|
+
return;
|
|
448
|
+
while (true) {
|
|
449
|
+
const beforeSleep = now();
|
|
450
|
+
if (beforeSleep > deadline)
|
|
451
|
+
return;
|
|
452
|
+
await sleep(Math.min(interval, Math.max(0, deadline - beforeSleep)));
|
|
453
|
+
const readAt = now();
|
|
454
|
+
if (readAt > deadline)
|
|
431
455
|
return;
|
|
432
456
|
const readAbort = new AbortController();
|
|
433
457
|
const readTimeout = setTimeout(() => readAbort.abort(new Error("Claude channel result read timed out.")), CLAUDE_CHANNEL_RESULT_TIMEOUT_MS);
|
|
@@ -441,13 +465,22 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
441
465
|
consecutiveReadFailures += 1;
|
|
442
466
|
if (consecutiveReadFailures >= 3)
|
|
443
467
|
return;
|
|
468
|
+
interval = Math.min(pollIntervalCeiling(options, now() - startedAt, initialInterval), interval * 2);
|
|
444
469
|
continue;
|
|
445
470
|
}
|
|
446
471
|
finally {
|
|
447
472
|
clearTimeout(readTimeout);
|
|
448
473
|
}
|
|
449
|
-
if (lifecycle.status === "pending")
|
|
474
|
+
if (lifecycle.status === "pending") {
|
|
475
|
+
// Keep a responsive first check, then avoid a long-lived hot poll while
|
|
476
|
+
// still checking often enough to deliver shortly after completion. The
|
|
477
|
+
// active window stays at <=15s so a result feels immediate in a normal
|
|
478
|
+
// gesture; only unusually long-lived requests relax their cadence.
|
|
479
|
+
interval = Math.min(pollIntervalCeiling(options, now() - startedAt, initialInterval), interval * 2);
|
|
480
|
+
if (readAt >= deadline)
|
|
481
|
+
return;
|
|
450
482
|
continue;
|
|
483
|
+
}
|
|
451
484
|
const handle = `live:${requestId}`;
|
|
452
485
|
const content = lifecycle.status === "answered"
|
|
453
486
|
? `SomaCheck vibecheck ${handle} completed: ${lifecycle.verdict}, confidence ${formatChannelConfidence(lifecycle.confidence)}${formatUserFeedback(lifecycle.user_feedback)}.`
|
|
@@ -472,6 +505,18 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
|
|
|
472
505
|
return;
|
|
473
506
|
}
|
|
474
507
|
}
|
|
508
|
+
function pollIntervalCeiling(options, elapsedMs, initialInterval) {
|
|
509
|
+
if (options?.maxPollIntervalMs !== undefined) {
|
|
510
|
+
return Math.max(initialInterval, options.maxPollIntervalMs);
|
|
511
|
+
}
|
|
512
|
+
if (elapsedMs < CLAUDE_CHANNEL_ACTIVE_POLL_WINDOW_MS) {
|
|
513
|
+
return Math.max(initialInterval, CLAUDE_CHANNEL_ACTIVE_MAX_POLL_INTERVAL_MS);
|
|
514
|
+
}
|
|
515
|
+
if (elapsedMs < CLAUDE_CHANNEL_EXTENDED_POLL_WINDOW_MS) {
|
|
516
|
+
return Math.max(initialInterval, CLAUDE_CHANNEL_EXTENDED_MAX_POLL_INTERVAL_MS);
|
|
517
|
+
}
|
|
518
|
+
return Math.max(initialInterval, CLAUDE_CHANNEL_LONG_MAX_POLL_INTERVAL_MS);
|
|
519
|
+
}
|
|
475
520
|
function unrefSleep(milliseconds) {
|
|
476
521
|
if (milliseconds <= 0)
|
|
477
522
|
return Promise.resolve();
|
|
@@ -570,16 +615,19 @@ async function waitForLiveVibecheck(created, token, identity, dependencies, sign
|
|
|
570
615
|
}
|
|
571
616
|
function summarise(status) {
|
|
572
617
|
const cadence = status.cadence;
|
|
618
|
+
if (status.propositions_needed <= 0) {
|
|
619
|
+
return `This connection's database feed has no available slots; ${status.queued_proposition_count} propositions are queued. Do not post another feed item. This status does not verify phone display. Routine replenishment uses a ${formatDuration(cadence.followup_within_seconds)} floor.`;
|
|
620
|
+
}
|
|
573
621
|
if (status.first_run_intro.should_offer_now) {
|
|
574
|
-
return `This is your first contact. Introduce the SomaCheck ritual,
|
|
622
|
+
return `This is your first contact. Introduce the SomaCheck ritual. The feed has up to ${status.propositions_needed} available slot${status.propositions_needed === 1 ? "" : "s"}; only if the person asks to stock reflections for later or has explicitly authorized scheduled stocking, add 1 to ${status.propositions_needed} genuinely useful proposition${status.propositions_needed === 1 ? "" : "s"}. Never invent extras to fill capacity.`;
|
|
575
623
|
}
|
|
576
624
|
if (status.propositions_needed > 0) {
|
|
577
625
|
const requested = status.replenishment_requested_at === null
|
|
578
626
|
? ""
|
|
579
627
|
: ` The person requested replenishment at ${status.replenishment_requested_at}.`;
|
|
580
|
-
return `The
|
|
628
|
+
return `The database feed has ${status.pending_proposition_count} of 3 propositions and up to ${status.propositions_needed} available slot${status.propositions_needed === 1 ? "" : "s"}. Only if the person asks to stock reflections for later or has explicitly authorized scheduled stocking, add 1 to ${status.propositions_needed} genuinely useful proposition${status.propositions_needed === 1 ? "" : "s"}; never invent extras to fill capacity. A still-active scheduled authorization does not require repeated consent each run. This status does not verify phone display.${requested}`;
|
|
581
629
|
}
|
|
582
|
-
return
|
|
630
|
+
return "Feed capacity is unavailable. Do not post another feed item until status can be confirmed.";
|
|
583
631
|
}
|
|
584
632
|
function formatDuration(seconds) {
|
|
585
633
|
const hours = seconds / 3600;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somacheck/vibecheck",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.14",
|
|
4
4
|
"mcpName": "io.github.Sensie-agents/vibecheck",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Body language for AI agents, on your terms. A consented SomaCheck signal for how a thought or choice lands.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/Sensie-agents/vibecheck.git"
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"dist/*.js",
|
|
19
19
|
"SKILL.md",
|
|
20
|
-
"recipes/**"
|
|
21
|
-
"claude-marketplace/**"
|
|
20
|
+
"recipes/**"
|
|
22
21
|
],
|
|
23
22
|
"scripts": {
|
|
24
23
|
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
@@ -77,8 +77,8 @@ or `search_observations` (observation identifiers and representative verbatim
|
|
|
77
77
|
snippets), and never surfaces individual feedback, source quotes, respondent
|
|
78
78
|
identifiers, or free text. The agent offers two defensible interpretations of
|
|
79
79
|
what the aggregate signal suggests, then one short first-person proposition
|
|
80
|
-
about the researcher's own interpretation or next step. After the researcher
|
|
81
|
-
|
|
80
|
+
about the researcher's own interpretation or next step. After the researcher
|
|
81
|
+
explicitly indicates a choice, the agent stops. There is no Chattermill write.
|
|
82
82
|
|
|
83
83
|
## Starter prompt
|
|
84
84
|
|
|
@@ -98,7 +98,8 @@ aggregate signal suggests, with the tradeoff for each. Then offer one short
|
|
|
98
98
|
first-person proposition about my own interpretation or next step and wait
|
|
99
99
|
for my acceptance before any proactive SomaCheck ask. Treat Aligned or
|
|
100
100
|
Unaligned plus confidence as context, not truth, diagnosis, authorization, or
|
|
101
|
-
evidence about any individual customer. Ask what I choose
|
|
101
|
+
evidence about any individual customer. Ask what I choose and accept any
|
|
102
|
+
explicit response available in this interface. Keep the
|
|
102
103
|
proposition, reading, confidence, and my confirmation out of Chattermill. Do
|
|
103
104
|
not write to Chattermill; the hosted MCP is read-only. If the preconfigured
|
|
104
105
|
connection, mcp:read, or a safe aggregate tool is absent, return the copy-ready
|
|
@@ -198,9 +199,9 @@ recipe work. If the runtime does not expose `get_metrics` or
|
|
|
198
199
|
researcher decides). `unaligned` may indicate possible inner conflict
|
|
199
200
|
relative to the proposition; it does not name a cause, choose an
|
|
200
201
|
interpretation, or justify a research claim.
|
|
201
|
-
11. **
|
|
202
|
-
whether they want a third option, or whether they want to stop.
|
|
203
|
-
their
|
|
202
|
+
11. **Return authority.** Ask the researcher which interpretation they choose,
|
|
203
|
+
whether they want a third option, or whether they want to stop. Accept any
|
|
204
|
+
explicit response available in the interface and follow their choice.
|
|
204
205
|
12. **Keep the signal out of Chattermill.** Do not call any write, mutation,
|
|
205
206
|
export, tag, theme, highlight, insight, or analytics tool. Do not store
|
|
206
207
|
the proposition, reading, confidence, confirmation, or gesture metadata
|
|
@@ -246,8 +247,8 @@ The test passes when:
|
|
|
246
247
|
- the researcher receives at most one optional SomaCheck check-in on their
|
|
247
248
|
phone on an exact first-person proposition about their own interpretation or
|
|
248
249
|
next step;
|
|
249
|
-
- the agent presents the result as context, asks the researcher to
|
|
250
|
-
choice
|
|
250
|
+
- the agent presents the result as context, asks the researcher to indicate
|
|
251
|
+
their choice explicitly, and follows that choice;
|
|
251
252
|
- the agent makes no Chattermill write, mutation, export, share, tag,
|
|
252
253
|
comment, or analytics call; and
|
|
253
254
|
- no individual customer, respondent, or employee data enters the cross-MCP
|
|
@@ -271,7 +272,7 @@ The test passes when:
|
|
|
271
272
|
then proceed without a reading if still unresolved.
|
|
272
273
|
- **Unreadable capture:** Offer a retry only if the researcher wants it;
|
|
273
274
|
unreadable is not a third interpretation.
|
|
274
|
-
- **Researcher disagrees with the reading:** Follow the researcher's
|
|
275
|
+
- **Researcher disagrees with the reading:** Follow the researcher's explicit
|
|
275
276
|
choice without reconciliation or repetition.
|
|
276
277
|
|
|
277
278
|
## Privacy boundary
|
|
@@ -306,7 +307,7 @@ The test passes when:
|
|
|
306
307
|
- [ ] The proposition is first-person and contains no Chattermill content.
|
|
307
308
|
- [ ] At most one SomaCheck request is created for the choice.
|
|
308
309
|
- [ ] Observation, interpretation, confirmation, and choice remain separate.
|
|
309
|
-
- [ ] The researcher
|
|
310
|
+
- [ ] The researcher explicitly indicates a choice and the agent follows it.
|
|
310
311
|
- [ ] The agent makes no Chattermill write, mutation, export, share, tag,
|
|
311
312
|
comment, or analytics call.
|
|
312
313
|
- [ ] No individual customer, respondent, or employee data enters the
|