@somacheck/vibecheck 0.6.13 → 0.6.15

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 CHANGED
@@ -11,7 +11,7 @@ Your agent has your context. It does not have your gut. This is the bridge.
11
11
 
12
12
  Fair. Here is what you need, without executing anything.
13
13
 
14
- **What the `link` command does.** `npx -y @somacheck/vibecheck@0.6.13 link <CODE> --client <CLIENT>`
14
+ **What the `link` command does.** `npx -y @somacheck/vibecheck@0.6.15 link <CODE> --client <CLIENT>`
15
15
  exchanges a short-lived pairing code, generated inside the SomaCheck iOS app, for
16
16
  a bearer token. It writes that token to `~/.sensie/config.json` with mode `0600`,
17
17
  offers to configure supported agent clients found on the computer, and checks the
@@ -42,8 +42,8 @@ code is not used. Non-interactive implicit selection,
42
42
 
43
43
  **What the server does once running.** It exposes MCP tools that share bounded,
44
44
  user-authorized context summaries, create a statement for the user to test,
45
- read its outcome, recover recent completed gesture context, and keep a
46
- three-statement feed stocked. Version 0.6 adds `request_vibecheck` for a
45
+ read its outcome, recover recent completed gesture context, and optionally stock
46
+ up to three feed items when the person asks. Version 0.6 adds `request_vibecheck` for a
47
47
  person-approved, immediate phone check-in. That path is deliberately separate
48
48
  from the reflection feed.
49
49
 
@@ -102,20 +102,20 @@ before the pairing code is redeemed.
102
102
  To configure or repair a client later:
103
103
 
104
104
  ```text
105
- npx -y @somacheck/vibecheck@0.6.13 setup codex
106
- npx -y @somacheck/vibecheck@0.6.13 setup claude
107
- npx -y @somacheck/vibecheck@0.6.13 doctor
105
+ npx -y @somacheck/vibecheck@0.6.15 setup codex
106
+ npx -y @somacheck/vibecheck@0.6.15 setup claude
107
+ npx -y @somacheck/vibecheck@0.6.15 doctor
108
108
  ```
109
109
 
110
110
  Manual Codex registration remains available. Its stdio runtime command is
111
- `npx -y @somacheck/vibecheck@0.6.13 serve --client codex`:
111
+ `npx -y @somacheck/vibecheck@0.6.15 serve --client codex`:
112
112
 
113
113
  ```json
114
114
  {
115
115
  "mcpServers": {
116
116
  "vibecheck": {
117
117
  "command": "npx",
118
- "args": ["-y", "@somacheck/vibecheck@0.6.13", "serve", "--client", "codex"]
118
+ "args": ["-y", "@somacheck/vibecheck@0.6.15", "serve", "--client", "codex"]
119
119
  }
120
120
  }
121
121
  }
@@ -287,9 +287,9 @@ blocked until the capability and revocation probe in the safety review passes.
287
287
  | Tool | What it does |
288
288
  |---|---|
289
289
  | `get_vibecheck_context` | Read recent completed gestures for this exact agent link without retaining every `request_id`. |
290
- | `get_vibecheck_status` | Read the live three-proposition cache and how many distinct insights it needs. |
290
+ | `get_vibecheck_status` | Read the three-proposition database cache and its available capacity; this does not verify phone display. |
291
291
  | `share_somacheck_context` | Share 1–20 bounded, derived, user-authorized observations; raw conversations and secrets are rejected. |
292
- | `post_vibecheck_statement` | Add one to three personalized statements without blocking; one may become the current Home vibe check and the rest wait under Vibe Checks without extra pushes. Returns a stable `request_id` for each. |
292
+ | `post_vibecheck_statement` | When the person asks to stock reflections for later or has explicitly authorized scheduled stocking, add one to the reported available maximum without blocking. One may become database-current and the rest queue under Vibe Checks without extra pushes. Returns a stable `request_id` for each created row; it does not verify phone display. |
293
293
  | `get_vibecheck_result` | Read that exact proposition once by `request_id`; returns queued, pending, or its terminal result. |
294
294
  | `request_vibecheck` | With per-ask consent, send one immediate phone vibecheck, wait boundedly for its exact answer, and retain an opaque `live:<uuid>` fallback handle. |
295
295
 
@@ -315,28 +315,38 @@ response.
315
315
  Claude Code is the primary local experience. Its registered stdio server
316
316
  advertises Anthropic's experimental Channel capability. When
317
317
  `request_vibecheck` returns pending, the same local server follows only that
318
- exact request and emits one `notifications/claude/channel` event when the
319
- answer, expiry, or cancellation arrives. Claude receives the constrained
320
- result as conversation input and can continue without another user message.
321
- The event contains the binary reading, confidence, lifecycle, and opaque
318
+ exact request and attempts one `notifications/claude/channel` event when the
319
+ answer, expiry, or cancellation arrives. An enabled, listening Channel host
320
+ can receive the constrained result as conversation input and continue without
321
+ another user message.
322
+ The server's Channel attempt does not prove that the host enabled or received
323
+ the experimental event. A pending response therefore also tells the agent to
324
+ keep the current turn active and poll only the same stable request until it is
325
+ answered, expired, or cancelled. It must not create a duplicate request. The
326
+ event contains the binary reading, confidence, lifecycle, and opaque
322
327
  handle—never the statement, link token, raw motion, or an instruction that
323
328
  turns the result into authorization.
324
329
 
325
- The installed `PostToolUse` hook remains a compatibility fallback for an
326
- ordinary non-channel registration. A channel-armed response marks its
327
- continuation transport explicitly, and the hook abstains, so two watchers
328
- cannot wake the same turn. During Anthropic's research preview, the channel
329
- session must be launched with the command above; a closed or non-enabled
330
- session may silently drop the event.
330
+ The legacy `PostToolUse` parser is not installed by the current Channel plugin,
331
+ so it is not a continuation guarantee or a substitute listener. During
332
+ Anthropic's research preview, Channel delivery requires a Channels-capable
333
+ session and SomaCheck on the effective plugin allowlist: Anthropic's default
334
+ allowlist or an organization's replacement `allowedChannelPlugins` policy. A
335
+ closed, non-enabled, non-allowed, or non-listening session may silently drop
336
+ the event while same-request polling remains available.
331
337
 
332
338
  Other standard MCP hosts retain the bounded tool response and stable-handle
333
339
  fallback until they provide an equivalent supported wake mechanism. This
334
340
  Claude-specific continuation must never be presented as provider-neutral. A
335
- pending response without that hook (including the hosted connector, which is
336
- stateless per request and never arms the watch) tells the agent the exact
341
+ pending response (including the hosted connector, which is stateless per
342
+ request and never arms the watch) tells the agent the exact
337
343
  `request_id` and the person's expiry, and to call `get_vibecheck_result` again
338
- in about 15 seconds, repeating at that cadence until the state is answered or
339
- expired without ending the turn or asking the person whether to check. A
344
+ in about 15 seconds, keeping the turn active and polling only that handle until
345
+ the state is answered, expired, or cancelled. A terminal tool read suppresses
346
+ any not-yet-emitted Channel event for that request. An event already handed to
347
+ the host cannot be retracted, and the server receives no host acknowledgement;
348
+ the stable handle and idempotency key prevent another phone request but do not
349
+ claim exactly-once host delivery. A
340
350
  still-pending read of the same Live Ask repeats that instruction; the read does
341
351
  not invent an expiry because the result endpoint does not return one.
342
352
 
@@ -346,20 +356,26 @@ statement with the same `idempotency_key`.
346
356
 
347
357
  An immediate vibecheck is not proposition caching. It sends one person-approved statement
348
358
  for the current choice, expires, and remains bound to its originating client.
359
+ Handle multiple immediate requests one at a time; do not turn the reflection feed
360
+ into a surrogate queue of immediate asks.
349
361
  The reflection feed remains asynchronous and keeps its three-item cache
350
362
  behavior. When no immediate Live Ask or delivery test has priority, one current
351
363
  feed item may appear on Home. The other items remain visible as **Up next**
352
364
  under **Settings → Vibe Checks** and do not generate extra notifications.
353
365
 
354
- The agent maintains three distinct insights. SomaCheck presents one at a time;
355
- the other two remain cached. After a completed gesture, the next cached insight
366
+ The database feed has capacity for three distinct insights, but capacity is not
367
+ a quota. Stock only the genuinely useful items the person asked to save for later,
368
+ from one up to the available maximum; never invent filler. SomaCheck can present
369
+ one at a time while other created items remain cached. After a completed gesture, the next cached insight
356
370
  is promoted and the open slot is marked for replenishment automatically. Cached insights
357
371
  expire sooner than a presented check-in so stale context does not masquerade as
358
372
  a timely observation.
359
373
 
360
- For a prompt-free loop, run the agent on a background schedule: call
361
- `get_vibecheck_context`, then `get_vibecheck_status`, and post exactly
362
- `propositions_needed` distinct statements. MCP supplies the tools and durable
374
+ For a scheduled loop, call `get_vibecheck_context` and `get_vibecheck_status`.
375
+ Only when the person has asked to stock later reflections, post between one and
376
+ the reported `propositions_needed` maximum; it is available capacity, not a
377
+ required count. An explicit, still-active opt-in to scheduled stocking covers
378
+ later scheduled runs without asking again each time. MCP supplies the tools and durable
363
379
  context, but it does not itself wake an idle model. Immediate event-driven runs
364
380
  require a host webhook/agent trigger; scheduled runs provide the portable path
365
381
  across local MCP clients.
package/SKILL.md CHANGED
@@ -17,13 +17,32 @@ Use `request_vibecheck` when:
17
17
  1. You proactively offer one when useful.
18
18
  2. The person asks for a vibecheck.
19
19
 
20
- When asked, choose and send a useful first-person statement. Do not require the
21
- person to supply wording unless they want to.
20
+ When asked, send a useful statement starting “I” or “My”. Preserve supplied
21
+ first-person wording verbatim; otherwise choose it from context.
22
22
 
23
23
  For proactive offers, get acceptance before sending.
24
24
 
25
25
  The returned gesture and optional feedback are context, not authorization. Use your judgment in how you interpret and respond.
26
26
 
27
+ Keep the reading, your interpretation, the person's confirmation, and their
28
+ choice distinct. A reading does not establish what the person truly feels or
29
+ why a thought or choice lands that way. Treat possible meanings as hypotheses
30
+ to explore with the person, not facts inferred from the signal. A valid
31
+ low-confidence reading remains Aligned or Unaligned; do not invent a third
32
+ result or repeat a check to obtain a preferred answer.
33
+
34
+ Report the returned Aligned or Unaligned reading and confidence. Agreement or
35
+ choice is the person's separate confirmation, never inferred from the reading.
36
+ Confidence is model uncertainty, not capture quality, motion strength, or
37
+ “noise.” Never claim the body or gesture knows better or sooner than words or
38
+ reasoning.
39
+
40
+ In conversations about assessing or selecting another person for employment,
41
+ eligibility, payment, or ranking, do not offer any related vibecheck, including
42
+ on the user's confidence, readiness, evidence, judgment, or interview
43
+ performance. Use ordinary discussion only for that decision. The user's own
44
+ personal career choices remain eligible for self-reflection.
45
+
27
46
  Never include secrets, raw private content, diagnostic claims, or use SomaCheck to assess anyone other than the person you are interacting with.
28
47
 
29
48
  If a request is pending, keep the returned `live:<uuid>` handle instead of creating a replacement.
package/dist/cli.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from "node:os";
3
+ import { realpathSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { createInterface } from "node:readline/promises";
4
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
7
  import { SupabaseAgentApi } from "./api.js";
@@ -16,6 +18,15 @@ import { createVibecheckServer } from "./server.js";
16
18
  const api = new SupabaseAgentApi(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);
17
19
  const runner = new LocalCommandRunner();
18
20
  const output = (line) => process.stdout.write(`${line}\n`);
21
+ const defaultCliClientDependencies = {
22
+ home: homedir(),
23
+ runner,
24
+ output,
25
+ isClientInstalled,
26
+ configureClaudeContinuationPlugin,
27
+ migrateClaudeDirectRegistration,
28
+ detectLegacyHostedRegistration,
29
+ };
19
30
  class ClientPreflightError extends Error {
20
31
  constructor(message) {
21
32
  super(message);
@@ -74,18 +85,19 @@ async function promptForClients(installed) {
74
85
  terminal.close();
75
86
  }
76
87
  }
77
- async function configureClients(clients) {
88
+ export async function configureClients(clients, dependencies = defaultCliClientDependencies) {
89
+ const { runner, output } = dependencies;
78
90
  for (const client of clients) {
79
91
  if (client === "claude") {
80
- if (!(await isClientInstalled(client, runner))) {
92
+ if (!(await dependencies.isClientInstalled(client, runner))) {
81
93
  output(`○ ${clientDisplayName(client)} is not installed; skipped.`);
82
94
  continue;
83
95
  }
84
- const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
96
+ const plugin = await dependencies.configureClaudeContinuationPlugin(dependencies.home, runner);
85
97
  if (plugin.status === "failed") {
86
98
  throw new ClientPreflightError("Could not configure the SomaCheck Channel plugin for Claude Code.");
87
99
  }
88
- const migration = await migrateClaudeDirectRegistration(runner);
100
+ const migration = await dependencies.migrateClaudeDirectRegistration(runner);
89
101
  if (migration === "needs_update") {
90
102
  throw new ClientPreflightError([
91
103
  'Claude Code has a user-owned MCP server named "vibecheck"; it was not changed.',
@@ -99,7 +111,9 @@ async function configureClients(clients) {
99
111
  output("✓ Automatic SomaCheck marketplace updates are enabled for future reviewed releases.");
100
112
  if (migration === "removed")
101
113
  output("✓ Removed the superseded direct MCP registration.");
102
- output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
114
+ output(" Standard Claude Code can use SomaCheck with the same-request polling fallback.");
115
+ output(" Channel delivery requires a Channels-capable session and SomaCheck on the effective plugin allowlist.");
116
+ output(` If Anthropic's allowlist or your organization's allowedChannelPlugins permits SomaCheck, start with: ${claudeChannelLaunchCommand()}`);
103
117
  continue;
104
118
  }
105
119
  const result = await registerClient(client, runner);
@@ -124,7 +138,8 @@ async function configureClients(clients) {
124
138
  }
125
139
  }
126
140
  }
127
- async function preflightLinkClients(clients, interactive) {
141
+ export async function preflightLinkClients(clients, interactive, dependencies = defaultCliClientDependencies) {
142
+ const { runner, output } = dependencies;
128
143
  const nonInteractiveClient = interactive ? null : singleNonInteractiveClientSelection(clients);
129
144
  const selectedClients = interactive
130
145
  ? clients === "prompt" ? [] : clients
@@ -151,7 +166,7 @@ async function preflightLinkClients(clients, interactive) {
151
166
  ].join("\n"));
152
167
  }
153
168
  for (const client of selectedClients) {
154
- const legacyHosted = await detectLegacyHostedRegistration(client, runner);
169
+ const legacyHosted = await dependencies.detectLegacyHostedRegistration(client, runner);
155
170
  if (legacyHosted !== null) {
156
171
  throw new ClientPreflightError([
157
172
  `Not linking yet: ${clientDisplayName(client)} still has the separate legacy "somacheck" connector.`,
@@ -163,20 +178,20 @@ async function preflightLinkClients(clients, interactive) {
163
178
  }
164
179
  output(`Checking ${clientDisplayName(client)} before redeeming the pairing code...`);
165
180
  if (client === "claude") {
166
- if (!(await isClientInstalled(client, runner))) {
181
+ if (!(await dependencies.isClientInstalled(client, runner))) {
167
182
  throw new ClientPreflightError([
168
183
  "Not linking here: Claude Code is not installed or its CLI is unavailable.",
169
184
  "The pairing code has NOT been used.",
170
185
  ].join("\n"));
171
186
  }
172
- const plugin = await configureClaudeContinuationPlugin(homedir(), runner);
187
+ const plugin = await dependencies.configureClaudeContinuationPlugin(dependencies.home, runner);
173
188
  if (plugin.status === "failed") {
174
189
  throw new ClientPreflightError([
175
190
  "Not linking here: the SomaCheck Channel plugin could not be installed and verified for Claude Code.",
176
191
  "The pairing code has NOT been used. Repair Claude's plugin setup, then run the link command again.",
177
192
  ].join("\n"));
178
193
  }
179
- const migration = await migrateClaudeDirectRegistration(runner);
194
+ const migration = await dependencies.migrateClaudeDirectRegistration(runner);
180
195
  if (migration === "needs_update") {
181
196
  throw new ClientPreflightError([
182
197
  'Not linking here: Claude Code has a user-owned MCP entry named "vibecheck".',
@@ -195,7 +210,9 @@ async function preflightLinkClients(clients, interactive) {
195
210
  output("✓ Automatic SomaCheck marketplace updates are enabled for future reviewed releases.");
196
211
  if (migration === "removed")
197
212
  output("✓ Removed the superseded direct MCP registration before redeeming the pairing code.");
198
- output(` Start Claude Code with SomaCheck Channels: ${claudeChannelLaunchCommand()}`);
213
+ output(" Standard Claude Code can use SomaCheck with the same-request polling fallback.");
214
+ output(" Channel delivery requires a Channels-capable session and SomaCheck on the effective plugin allowlist.");
215
+ output(` If Anthropic's allowlist or your organization's allowedChannelPlugins permits SomaCheck, start with: ${claudeChannelLaunchCommand()}`);
199
216
  continue;
200
217
  }
201
218
  const preflight = await preflightClientPersistence(client, runner);
@@ -353,21 +370,37 @@ async function main() {
353
370
  await startServer(null);
354
371
  return 0;
355
372
  }
356
- main().then((exitCode) => {
357
- process.exitCode = exitCode;
358
- }).catch((error) => {
359
- if (error instanceof NonInteractiveLinkError || error instanceof LinkPersistenceError || error instanceof ClientPreflightError) {
360
- process.stderr.write(`${error.message}\n`);
373
+ async function runMain() {
374
+ try {
375
+ process.exitCode = await main();
376
+ }
377
+ catch (error) {
378
+ if (error instanceof NonInteractiveLinkError || error instanceof LinkPersistenceError || error instanceof ClientPreflightError) {
379
+ process.stderr.write(`${error.message}\n`);
380
+ process.exitCode = 1;
381
+ return;
382
+ }
383
+ const isUsageError = error instanceof Error && error.message.startsWith("Usage:");
384
+ const message = isUsageError
385
+ ? error.message
386
+ : process.argv[2] === "link"
387
+ ? "Could not link SomaCheck. Check the pairing code and try again."
388
+ : "Vibecheck server could not start.";
389
+ process.stderr.write(`${message}\n`);
361
390
  process.exitCode = 1;
362
- return;
363
391
  }
364
- const isUsageError = error instanceof Error && error.message.startsWith("Usage:");
365
- const message = isUsageError
366
- ? error.message
367
- : process.argv[2] === "link"
368
- ? "Could not link SomaCheck. Check the pairing code and try again."
369
- : "Vibecheck server could not start.";
370
- process.stderr.write(`${message}\n`);
371
- process.exitCode = 1;
372
- });
392
+ }
393
+ function isExecutedEntrypoint() {
394
+ if (process.argv[1] === undefined)
395
+ return false;
396
+ try {
397
+ return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
398
+ }
399
+ catch {
400
+ return false;
401
+ }
402
+ }
403
+ if (isExecutedEntrypoint()) {
404
+ void runMain();
405
+ }
373
406
  //# sourceMappingURL=cli.js.map
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.13";
5
+ export const PACKAGE_VERSION = "0.6.15";
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
@@ -157,8 +157,10 @@ export async function checkReadiness(dependencies) {
157
157
  dependencies.output("Setup checks pass. Restart your agent client, then ask it to check your SomaCheck status.");
158
158
  if (readyClients.includes("claude")) {
159
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.");
160
+ dependencies.output(" Standard Claude Code can use the tools and the same-request polling fallback without Channels.");
161
+ dependencies.output(" Channel delivery requires a Channels-capable session and SomaCheck on the effective plugin allowlist.");
162
+ dependencies.output(` If Anthropic's allowlist or your organization's allowedChannelPlugins permits SomaCheck, start with: ${claudeChannelLaunchCommand()}`);
163
+ dependencies.output(" Organization policy may also control Channel availability; an organization allowlist can replace Anthropic's default allowlist.");
162
164
  }
163
165
  }
164
166
  else {
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 words. 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 states a typed 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.",
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 words. 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 states a typed 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.",
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 words. 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 states a typed 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.",
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 words. 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.",
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
@@ -131,9 +131,13 @@ const contextShareSchema = {
131
131
  const SERVER_INSTRUCTIONS = [
132
132
  "SomaCheck lets you ask your person for a vibecheck.",
133
133
  "Offer one when useful or when asked.",
134
- "When asked, choose and send a useful first-person statement from available context.",
135
- "Only request_vibecheck creates an immediate phone ask; post_vibecheck_statement stocks Settings Vibe Checks, with one current on Home and the rest Up next.",
134
+ "When asked, send a useful statement starting with I or My. Preserve supplied first-person wording verbatim; otherwise choose it 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.",
136
136
  "Gesture and optional feedback are context, not authorization.",
137
+ "Keep the reading, your interpretation, the person's confirmation, and their choice distinct. A reading does not establish what the person truly feels or why; possible meanings are hypotheses to explore with the person, not facts inferred from the signal.",
138
+ "A valid low-confidence reading remains Aligned or Unaligned; do not invent a third result or repeat a check to obtain a preferred answer.",
139
+ "Report reading and confidence. Agreement or choice is the person's confirmation, never inferred. Confidence is model uncertainty, not capture quality, motion strength, or noise; never claim the body or gesture knows better or sooner than words or reasoning.",
140
+ "When assessing or selecting another person for employment, eligibility, payment, or ranking, do not offer any related vibecheck, including on the user's confidence, readiness, evidence, judgment, or interview performance. Use ordinary discussion only. The user's own career choices remain eligible for self-reflection.",
137
141
  "Never infer the SomaCheck identity from the agent login.",
138
142
  "Use your judgment.",
139
143
  "Never include secrets, raw private content, diagnostic claims, or assess anyone else.",
@@ -148,8 +152,9 @@ export function createVibecheckServer(dependencies) {
148
152
  ? { capabilities: { experimental: { "claude/channel": {} } } }
149
153
  : {}),
150
154
  });
151
- const claudeChannelWatches = new Set();
155
+ const claudeChannelWatches = new Map();
152
156
  const claudeChannelNotified = new Set();
157
+ const terminalResultsReadByTool = new Set();
153
158
  const startClaudeChannelWatch = (created, token, identity) => {
154
159
  if (!supportsClaudeChannel || dependencies.claudeChannelWatch === false)
155
160
  return;
@@ -162,11 +167,17 @@ export function createVibecheckServer(dependencies) {
162
167
  || claudeChannelWatches.has(requestId)
163
168
  || claudeChannelNotified.has(requestId))
164
169
  return;
165
- claudeChannelWatches.add(requestId);
166
- void watchForClaudeChannelResult(created, token, identity, dependencies, async (notification) => {
170
+ const watchAbort = new AbortController();
171
+ claudeChannelWatches.set(requestId, watchAbort);
172
+ void watchForClaudeChannelResult(created, token, identity, dependencies, watchAbort.signal, async (notification) => {
173
+ if (terminalResultsReadByTool.has(requestId) || watchAbort.signal.aborted)
174
+ return;
167
175
  claudeChannelNotified.add(requestId);
168
176
  await server.server.notification(notification);
169
- }).finally(() => claudeChannelWatches.delete(requestId));
177
+ }).finally(() => {
178
+ claudeChannelWatches.delete(requestId);
179
+ terminalResultsReadByTool.delete(requestId);
180
+ });
170
181
  };
171
182
  server.registerTool("get_vibecheck_context", {
172
183
  title: "Vibecheck Context",
@@ -197,7 +208,7 @@ export function createVibecheckServer(dependencies) {
197
208
  });
198
209
  server.registerTool("get_vibecheck_status", {
199
210
  title: "Vibecheck Status",
200
- description: "Read the live SomaCheck cache before posting. Reports how many personalized propositions are needed, when routine replenishment is due, and whether this is the agent's first contact.",
211
+ 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.",
201
212
  inputSchema: {},
202
213
  outputSchema: statusSchema,
203
214
  annotations: {
@@ -255,7 +266,7 @@ export function createVibecheckServer(dependencies) {
255
266
  });
256
267
  server.registerTool("post_vibecheck_statement", {
257
268
  title: "Post Vibecheck Statement",
258
- description: "Stock Settings Vibe Checks with one to three optional personalized reflections and return immediately. One can become the current Home proposition; the rest stay Up next without extra pushes. Never call this to add follow-up asks after request_vibecheck. Call get_vibecheck_status first and submit exactly propositions_needed statements.",
269
+ 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.",
259
270
  inputSchema: {
260
271
  statements: z.array(z.string().min(1)).min(1).max(3)
261
272
  .describe("Distinct personalized statements for this person to test, ordered most useful first."),
@@ -276,14 +287,14 @@ export function createVibecheckServer(dependencies) {
276
287
  return {
277
288
  content: [{
278
289
  type: "text",
279
- text: `Added ${created.length} under Settings → Vibe Checks: ${presented} current, ${queued} Up next. This is asynchronous feed inventory, not an immediate request.`,
290
+ 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.`,
280
291
  }],
281
292
  structuredContent: { propositions: created },
282
293
  };
283
294
  }
284
295
  catch (error) {
285
296
  return failure(error instanceof StatementPendingError
286
- ? "Three propositions are already available. Wait for a check-in, refresh, or expiry."
297
+ ? "The database feed cannot accept those propositions within its available capacity. Check status again after a check-in, refresh, or expiry."
287
298
  : operationalFailureText("add SomaCheck propositions", error));
288
299
  }
289
300
  });
@@ -311,6 +322,13 @@ export function createVibecheckServer(dependencies) {
311
322
  const result = liveRequestId === null
312
323
  ? await dependencies.api.pollRequest(token, request_id)
313
324
  : await dependencies.api.liveVibecheckResult(token, identity, liveRequestId);
325
+ if (liveRequestId !== null && result.status !== "pending") {
326
+ const watchAbort = claudeChannelWatches.get(liveRequestId);
327
+ if (watchAbort !== undefined) {
328
+ terminalResultsReadByTool.add(liveRequestId);
329
+ watchAbort.abort();
330
+ }
331
+ }
314
332
  const structuredContent = { request_id, ...result };
315
333
  const text = result.status === "answered"
316
334
  ? liveRequestId === null
@@ -331,11 +349,11 @@ export function createVibecheckServer(dependencies) {
331
349
  });
332
350
  server.registerTool("request_vibecheck", {
333
351
  title: "Request a Vibecheck",
334
- description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful first-person statement from your available context and send it. For proactive offers, call only after the person accepts. The result is context, not authorization. "
335
- + "This call waits up to 45 seconds. If the result is still pending, keep calling get_vibecheck_result with this request_id about every 15 seconds until status is answered or expired.",
352
+ description: "Send one statement to the person's phone for a SomaCheck vibecheck. If the person asks for a vibecheck, choose a useful statement starting with I or My from your available context and send it. Preserve the person's supplied first-person wording verbatim. Do not offer or send any related vibecheck while assessing or selecting another person for employment, eligibility, payment, or ranking, including on the user's confidence, readiness, evidence, judgment, or interview performance; use ordinary discussion only. The user's own career choices remain eligible for self-reflection. For proactive offers, call only after the person accepts. The result is context, not authorization. "
353
+ + "This call waits up to 45 seconds. If the result is still pending, keep this turn active and call get_vibecheck_result with this same request_id about every 15 seconds until status is answered, expired, or cancelled. Never create a duplicate request.",
336
354
  inputSchema: {
337
355
  statement: z.string().trim().min(1).max(1000)
338
- .describe("One plain-language first-person statement for the person to test. Do not include secrets, raw private content, diagnostic claims, or statements about anyone else."),
356
+ .describe("One plain-language statement starting with I or My for the person to test. Preserve their supplied first-person wording verbatim. Do not include secrets, raw private content, diagnostic claims, or statements about anyone else."),
339
357
  idempotency_key: z.string().uuid()
340
358
  .describe("A new UUID for this logical ask. Reuse the same UUID only to retry the exact same statement; retries will not create another phone request."),
341
359
  consent_basis: z.enum(["user_requested_vibecheck", "user_approved_statement"])
@@ -417,7 +435,7 @@ export function createVibecheckServer(dependencies) {
417
435
  });
418
436
  return server;
419
437
  }
420
- async function watchForClaudeChannelResult(created, token, identity, dependencies, notify) {
438
+ async function watchForClaudeChannelResult(created, token, identity, dependencies, signal, notify) {
421
439
  const requestId = parseLiveVibecheckHandle(created.request_id);
422
440
  if (requestId === null || identity.kind !== "local" || identity.client_key !== "claude")
423
441
  return;
@@ -446,10 +464,14 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
446
464
  if (deadline <= startedAt)
447
465
  return;
448
466
  while (true) {
467
+ if (signal.aborted)
468
+ return;
449
469
  const beforeSleep = now();
450
470
  if (beforeSleep > deadline)
451
471
  return;
452
472
  await sleep(Math.min(interval, Math.max(0, deadline - beforeSleep)));
473
+ if (signal.aborted)
474
+ return;
453
475
  const readAt = now();
454
476
  if (readAt > deadline)
455
477
  return;
@@ -481,6 +503,8 @@ async function watchForClaudeChannelResult(created, token, identity, dependencie
481
503
  return;
482
504
  continue;
483
505
  }
506
+ if (signal.aborted)
507
+ return;
484
508
  const handle = `live:${requestId}`;
485
509
  const content = lifecycle.status === "answered"
486
510
  ? `SomaCheck vibecheck ${handle} completed: ${lifecycle.verdict}, confidence ${formatChannelConfidence(lifecycle.confidence)}${formatUserFeedback(lifecycle.user_feedback)}.`
@@ -534,22 +558,21 @@ function formatUserFeedback(feedback) {
534
558
  /** Text for a still-pending Live Ask, whether just created (delivery not
535
559
  * failed/skipped) or re-read via get_vibecheck_result. `expiresAt` is null
536
560
  * when the caller has no expiry to report (get_vibecheck_result's live read
537
- * does not receive one). `hasLocalWatch` is true only for the local stdio
538
- * server when the Claude PostToolUse hook's background watch is armed for
539
- * this request (see startClaudeChannelWatch) that watch notifies the same
540
- * session automatically, so the agent should not be told to poll. The hosted
541
- * edge function is stateless per request and never has a watch, so it must
542
- * tell the agent exactly what to do next instead of inviting it to ask the
543
- * human. */
544
- function pendingWaitText(requestId, expiresAt, hasLocalWatch) {
545
- if (hasLocalWatch) {
546
- return `Vibecheck ${requestId} is pending. It expires at ${expiresAt}. This session will be notified automatically when the person responds.`;
547
- }
561
+ * does not receive one). `hasChannelWatch` is true only for the local stdio
562
+ * server when its Claude Channel watcher is running. A running watcher proves
563
+ * only that the server will attempt a notification; it cannot prove that the
564
+ * host enabled or is listening to the experimental Channel. The stable-handle
565
+ * polling fallback therefore remains explicit in both modes. */
566
+ function pendingWaitText(requestId, expiresAt, hasChannelWatch) {
567
+ const channelNotice = hasChannelWatch
568
+ ? "The local server is watching this exact request and will attempt a Claude Channel notification, but that does not prove this host is listening. "
569
+ : "";
548
570
  return `Vibecheck ${requestId} is pending. SomaCheck has not received a response yet. `
549
571
  + `If no phone prompt appears, open SomaCheck on the same account; phone setup is at ${SOMACHECK_SETUP_URL}. `
572
+ + channelNotice
550
573
  + `Call get_vibecheck_result with request_id ${requestId} again in about 15 seconds, `
551
- + `and keep polling at that cadence until the state is answered or expired. `
552
- + `Do not end your turn or ask the person whether to check; they are answering on their phone.`
574
+ + `and keep this turn active while polling only that request until the state is answered, expired, or cancelled. `
575
+ + `Do not call request_vibecheck again, end your turn, or treat a gesture as authorization.`
553
576
  + (expiresAt === null ? "" : ` It expires at ${expiresAt}.`);
554
577
  }
555
578
  async function waitForLiveVibecheck(created, token, identity, dependencies, signal, deadline, progress) {
@@ -615,16 +638,19 @@ async function waitForLiveVibecheck(created, token, identity, dependencies, sign
615
638
  }
616
639
  function summarise(status) {
617
640
  const cadence = status.cadence;
641
+ if (status.propositions_needed <= 0) {
642
+ 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.`;
643
+ }
618
644
  if (status.first_run_intro.should_offer_now) {
619
- return `This is your first contact. Introduce the SomaCheck ritual, then create exactly ${status.propositions_needed} distinct personalized propositions.`;
645
+ 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.`;
620
646
  }
621
647
  if (status.propositions_needed > 0) {
622
648
  const requested = status.replenishment_requested_at === null
623
649
  ? ""
624
650
  : ` The person requested replenishment at ${status.replenishment_requested_at}.`;
625
- return `The cache has ${status.pending_proposition_count} of 3 propositions. Create exactly ${status.propositions_needed} distinct personalized proposition${status.propositions_needed === 1 ? "" : "s"} now.${requested}`;
651
+ 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}`;
626
652
  }
627
- return `The cache is full: one proposition is presented and ${status.queued_proposition_count} are queued. Routine replenishment uses a ${formatDuration(cadence.followup_within_seconds)} floor.`;
653
+ return "Feed capacity is unavailable. Do not post another feed item until status can be confirmed.";
628
654
  }
629
655
  function formatDuration(seconds) {
630
656
  const hours = seconds / 3600;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@somacheck/vibecheck",
3
- "version": "0.6.13",
3
+ "version": "0.6.15",
4
4
  "mcpName": "io.github.Sensie-agents/vibecheck",
5
5
  "description": "Body language for AI agents, on your terms. A consented SomaCheck signal for how a thought or choice lands.",
6
6
  "repository": {
@@ -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's
81
- typed choice, the agent stops. There is no Chattermill write.
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 in words. Keep the
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. **Ask for words.** Ask the researcher which interpretation they choose,
202
- whether they want a third option, or whether they want to stop. Follow
203
- their typed choice.
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 state their
250
- choice in words, and follows the typed 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 typed
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 states a choice in words and the agent follows the typed choice.
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
@@ -115,7 +115,7 @@ makes the reading an automatic write condition.
115
115
  `pending`, is `expired` or `cancelled`, or errors, continue without a result;
116
116
  do not infer one or start another request.
117
117
  8. **Return authority.** Offer the options to revise, investigate, proceed,
118
- pause, or leave the artifact unchanged. A typed researcher choice overrides
118
+ pause, or leave the artifact unchanged. An explicit researcher choice overrides
119
119
  the signal.
120
120
  9. **Keep the signal private.** Do not write the verdict, confidence, gesture,
121
121
  or confirmation to Dovetail. If the researcher asks to record a next step,
@@ -153,7 +153,7 @@ Do not trigger when:
153
153
  - the task is ordinary Dovetail retrieval, summarization, tagging, or export;
154
154
  - the proposed subject is anyone other than the researcher;
155
155
  - the source contains raw or identifiable participant material;
156
- - the researcher has already made a clear typed choice;
156
+ - the researcher has already made a clear choice;
157
157
  - the result would determine inclusion, quality, compensation, performance, or
158
158
  another consequential outcome; or
159
159
  - a vibecheck was already used for the same decision.
@@ -48,7 +48,8 @@ neutral study framings and show their exact questions. Then offer one short
48
48
  first-person proposition about my own preferred framing and wait for my
49
49
  acceptance before any proactive SomaCheck ask. Give the agent the Aligned or
50
50
  Unaligned result plus confidence as context and let it use that context with
51
- its judgment. Ask what I choose in words. Keep the proposition, reading,
51
+ its judgment. Ask what I choose and accept any explicit response available in
52
+ this interface. Keep the proposition, reading,
52
53
  confidence, and my confirmation out of Great Question. Return a signal-free,
53
54
  copy-ready research plan only. Do not create, update, delete, recruit, invite,
54
55
  message, schedule, incentivize, launch, publish, or otherwise write to Great
@@ -119,8 +120,8 @@ owner-supplied artifact.
119
120
  or Unaligned reading, and confidence. Let it combine that context with the
120
121
  conversation and its judgment. Keep observation, interpretation,
121
122
  confirmation, and choice distinct.
122
- 9. **Ask the researcher to choose in words.** Their typed answer controls what
123
- follows and may differ from the signal.
123
+ 9. **Return authority.** Ask the researcher to indicate their choice explicitly.
124
+ Their choice controls what follows and may differ from the signal.
124
125
  10. **Return a copy-ready plan.** Include only a test title, goal/purpose,
125
126
  language, and the five questions. Do not attach an incentive, consent form,
126
127
  screener, candidates, recruitment settings, or send settings.
@@ -169,7 +170,7 @@ The end-to-end test passes only when:
169
170
  - **SomaCheck unresolved:** Use the same handle once later, then proceed without
170
171
  a reading if unresolved.
171
172
  - **Unreadable capture:** Offer a retry only if the researcher wants it.
172
- - **The researcher's words differ from the reading:** Follow their typed choice
173
+ - **The researcher's choice differs from the reading:** Follow their explicit choice
173
174
  without reconciliation or repetition.
174
175
 
175
176
  ## Privacy boundary
@@ -73,7 +73,7 @@ summary (mission completion rates, time on task, success-rate aggregate,
73
73
  summary counts). It does not open transcripts, response rows, participant
74
74
  identifiers, recordings, or free-text fields. The agent offers two defensible
75
75
  interpretation framings and one short first-person proposition about the
76
- researcher's own interpretation plan. After the researcher's typed choice,
76
+ researcher's own interpretation plan. After the researcher explicitly indicates a choice,
77
77
  the agent stops. There is no Maze write.
78
78
 
79
79
  ## Starter prompt
@@ -94,7 +94,8 @@ defensible interpretation framings. Then offer one short first-person
94
94
  proposition about my own interpretation plan and wait for my acceptance
95
95
  before any proactive SomaCheck ask. Treat Aligned or Unaligned plus
96
96
  confidence as context, not truth, diagnosis, authorization, or evidence
97
- about a Maze participant. Ask what I choose in words. Keep the
97
+ about a Maze participant. Ask what I choose and accept any explicit response
98
+ available in this interface. Keep the
98
99
  proposition, reading, confidence, and my confirmation out of Maze. Do
99
100
  not create, edit, publish, delete, archive, export, share, tag, comment,
100
101
  or write to Maze, a hidden field, a webhook, or any analytics record.
@@ -192,8 +193,8 @@ read, label the Maze half untested and stop.
192
193
  decides). `unaligned` may indicate possible inner conflict relative
193
194
  to the proposition; it does not name a cause, choose an interpretation,
194
195
  or justify a research claim.
195
- 11. **Ask for words.** Ask the researcher which framing they choose, whether they
196
- want a third option, or whether they want to stop. Follow their typed
196
+ 11. **Return authority.** Ask the researcher which framing they choose, whether they
197
+ want a third option, or whether they want to stop. Follow their explicit
197
198
  choice.
198
199
  12. **Keep the signal out of Maze.** Do not call any Maze write, mutation,
199
200
  export, webhook, comment, tag, highlight, insight, analytics, or
@@ -241,8 +242,8 @@ The test passes when:
241
242
  theme, highlight, insight, automation, or write tool;
242
243
  - the researcher receives at most one optional SomaCheck check-in on their phone
243
244
  on an exact first-person proposition about their own interpretation plan;
244
- - the agent presents the result as context, asks the researcher to state their
245
- choice in words, and follows the typed choice;
245
+ - the agent presents the result as context, asks the researcher to indicate
246
+ their choice explicitly, and follows that choice;
246
247
  - the agent makes no Maze write, mutation, export, share, tag,
247
248
  comment, or analytics call; and
248
249
  - no Maze participant, respondent, or employee data enters the
@@ -271,7 +272,7 @@ The test passes when:
271
272
  later, 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 typed choice
275
+ - **Researcher disagrees with the reading:** Follow the researcher's explicit choice
275
276
  without reconciliation or repetition.
276
277
 
277
278
  ## Privacy boundary
@@ -308,8 +309,7 @@ The test passes when:
308
309
  - [ ] At most one SomaCheck request is created for the choice.
309
310
  - [ ] Observation, interpretation, confirmation, and choice remain
310
311
  separate.
311
- - [ ] The researcher states a choice in words and the agent follows the typed
312
- choice.
312
+ - [ ] The researcher explicitly indicates a choice and the agent follows it.
313
313
  - [ ] The agent makes no Maze mutation, write, export, share, tag,
314
314
  comment, or analytics call.
315
315
  - [ ] No Maze participant, respondent, or employee data enters the
@@ -124,7 +124,7 @@ automatic decision gate.
124
124
  proceed to separate review, pause, or leave unchanged. Do not call
125
125
  `create_filter_set`, `create_study`, or `publish_study`. If the researcher
126
126
  chooses a change, produce a reviewable draft outside Prolific; platform
127
- mutation is a new action requiring explicit instruction. A typed researcher
127
+ mutation is a new action requiring explicit instruction. An explicit researcher
128
128
  choice overrides the signal.
129
129
  10. **Keep the signal private.** Do not write the verdict, confidence, gesture,
130
130
  confirmation, or inferred meaning to Prolific or another research platform.
@@ -161,7 +161,7 @@ Do not trigger when:
161
161
  - the proposed subject is a participant, employee, cohort, or respondent;
162
162
  - the result would affect eligibility, filtering, payment, bonus, rejection,
163
163
  submission quality, authenticity, or performance;
164
- - the researcher has already made a clear typed choice;
164
+ - the researcher has already made a clear choice;
165
165
  - the agent is being asked to publish, gate, or automate a launch; or
166
166
  - a vibecheck was already used for the same decision.
167
167
 
@@ -26,7 +26,8 @@ about a respondent, or a research decision. The researcher remains the authority
26
26
 
27
27
  The researcher supplies a participant-free survey goal. The agent drafts two
28
28
  four-question versions and may offer one SomaCheck check-in on the researcher's
29
- own preferred direction. The researcher then chooses in words. The output is a
29
+ own preferred direction. The researcher then explicitly indicates a choice.
30
+ The output is a
30
31
  signal-free survey definition for a QuestionPro owner to review. This recipe
31
32
  does not create, publish, distribute, or populate a survey.
32
33
 
@@ -43,7 +44,8 @@ upgrade, buy a plan, administer access, or populate QuestionPro data. Propose tw
43
44
  questionnaire framings and show the exact questions. Then offer one short
44
45
  first-person proposition about my own preferred direction and wait for my
45
46
  acceptance before any proactive SomaCheck ask. Give the result and confidence to
46
- the agent as context and let it use its judgment. Ask what I choose in words.
47
+ the agent as context and let it use its judgment. Ask what I choose and accept
48
+ any explicit response available in this interface.
47
49
  Keep the proposition, reading, confidence, and confirmation out of QuestionPro.
48
50
  Return a copy-ready, signal-free survey definition only. Do not create, edit,
49
51
  send, distribute, publish, recruit, launch, access responses, or spend anything.
@@ -77,7 +79,8 @@ send, distribute, publish, recruit, launch, access responses, or spend anything.
77
79
  6. Give the proposition, Aligned or Unaligned result, and confidence to the
78
80
  agent. Let the agent use its judgment in context without treating the result
79
81
  as authorization or a claim about respondents.
80
- 7. Ask what the researcher chooses in words. Their choice controls the plan.
82
+ 7. Ask the researcher to indicate their choice explicitly. Their choice controls
83
+ the plan.
81
84
  8. Return a copy-ready payload with title, purpose, questions, answer choices,
82
85
  and optional logic. Exclude all SomaCheck data and platform identifiers.
83
86
  9. Stop before any QuestionPro data or action call. A partner owner may review
@@ -115,8 +115,8 @@ Typeform write or launch condition.
115
115
  `pending`, is `expired` or `cancelled`, or errors, continue without a result;
116
116
  do not infer one or start another request.
117
117
  8. **Return authority.** Offer revise, split a question, soften leading
118
- wording, add an answer option, pilot, proceed, pause, or leave unchanged. A
119
- typed researcher choice overrides the signal.
118
+ wording, add an answer option, pilot, proceed, pause, or leave unchanged. An
119
+ explicit researcher choice overrides the signal.
120
120
  9. **Keep the signal private.** Do not write the verdict, confidence, gesture,
121
121
  or confirmation to Typeform. If the researcher asks for a Typeform edit,
122
122
  show the exact signal-free edit first and call the Typeform write tool only
@@ -159,7 +159,7 @@ Do not trigger when:
159
159
  free-text answers;
160
160
  - the result would affect eligibility, payment, employment, grading, access,
161
161
  support priority, response quality, or survey validity;
162
- - the researcher has already made a clear typed choice;
162
+ - the researcher has already made a clear choice;
163
163
  - the researcher has declined a recent offer or opted out; or
164
164
  - a vibecheck was already used for the same decision.
165
165
 
@@ -57,7 +57,8 @@ gates.
57
57
 
58
58
  The researcher gives the agent a participant-free study idea. The agent drafts
59
59
  two neutral framings, offers one first-person SomaCheck check-in about the
60
- researcher's own direction, and asks what they choose in words. The agent
60
+ researcher's own direction, and accepts any explicit response available in the
61
+ interface. The agent
61
62
  returns a complete copy-ready payload and stops. User Interviews remains
62
63
  unchanged. Any later draft creation is a separate future recipe and review, not
63
64
  an optional branch of this one.
@@ -79,7 +80,8 @@ Propose two neutral study framings and show the exact
79
80
  signal-free project copy. Then offer one short first-person proposition about
80
81
  my own preferred direction and ask for one SomaCheck check-in on that exact
81
82
  wording. Keep the proposition, reading, confidence, and my confirmation out of
82
- User Interviews. Afterward ask what I choose in words. Return the complete
83
+ User Interviews. Afterward ask what I choose and accept any explicit response
84
+ available in this interface. Return the complete
83
85
  signal-free, copy-ready project payload only. Do not create, edit, recruit,
84
86
  invite, message, schedule, screen, launch, publish, attach incentives, spend,
85
87
  read participant data, or call a broader or undocumented tool.
@@ -152,14 +154,14 @@ tools, stop without calling it.
152
154
  8. **Return authority.** Separate observation, interpretation (`aligned` or
153
155
  `unaligned` plus confidence), confirmation, and choice. `unaligned` may
154
156
  indicate possible inner conflict relative to the proposition; it does not
155
- identify a cause or select a study design. The researcher's typed choice
157
+ identify a cause or select a study design. The researcher's explicit choice
156
158
  controls.
157
159
  9. **Prepare the exact payload.** Show title, purpose, study type, questions or
158
160
  tasks, and any required non-contact fields. Exclude the proposition, result,
159
161
  confidence, confirmation, SomaCheck wording, participant metadata, and
160
162
  hidden fields.
161
163
  10. **Stop before every platform call.** The SomaCheck reading and the
162
- researcher's typed choice are not approval to use User Interviews. Do not
164
+ researcher's explicit choice are not approval to use User Interviews. Do not
163
165
  call create, update,
164
166
  read-back, recruitment, messaging, scheduling, participant, incentive,
165
167
  launch, publish, or data tools.
@@ -188,8 +190,8 @@ Not allowed:
188
190
  The test passes when:
189
191
 
190
192
  - the researcher receives at most one live SomaCheck request on their phone;
191
- - the agent presents the result as context and asks the researcher for their
192
- typed choice;
193
+ - the agent presents the result as context and asks the researcher to indicate
194
+ their choice explicitly;
193
195
  - the agent returns the exact signal-free, copy-ready project payload;
194
196
  - no User Interviews data or action tool is invoked and the workspace remains
195
197
  unchanged; and
@@ -212,7 +214,7 @@ The test passes when:
212
214
  proceed without a reading if unresolved.
213
215
  - **Unreadable capture:** Offer a retry only if the researcher wants it;
214
216
  unreadable is not a third interpretation.
215
- - **Researcher disagrees with the reading:** Follow the researcher's typed
217
+ - **Researcher disagrees with the reading:** Follow the researcher's explicit
216
218
  choice without reconciliation or repetition.
217
219
 
218
220
  ## Privacy boundary