@somacheck/vibecheck 0.6.14 → 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 +30 -20
- package/SKILL.md +21 -2
- package/dist/cli.js +59 -26
- package/dist/constants.js +1 -1
- package/dist/readiness.js +4 -2
- package/dist/server.js +45 -22
- package/package.json +1 -1
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.
|
|
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
|
|
@@ -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.
|
|
106
|
-
npx -y @somacheck/vibecheck@0.6.
|
|
107
|
-
npx -y @somacheck/vibecheck@0.6.
|
|
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.
|
|
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.
|
|
118
|
+
"args": ["-y", "@somacheck/vibecheck@0.6.15", "serve", "--client", "codex"]
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -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
|
|
319
|
-
answer, expiry, or cancellation arrives.
|
|
320
|
-
result as conversation input and
|
|
321
|
-
|
|
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
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
session may silently drop
|
|
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
|
|
336
|
-
|
|
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,
|
|
339
|
-
|
|
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
|
|
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,
|
|
21
|
-
person
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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.
|
|
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(
|
|
161
|
-
dependencies.output("
|
|
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/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,
|
|
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
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
|
|
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
|
-
|
|
166
|
-
|
|
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(() =>
|
|
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",
|
|
@@ -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
|
|
335
|
-
+ "This call waits up to 45 seconds. If the result is still pending, keep
|
|
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
|
|
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). `
|
|
538
|
-
* server when
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
|
552
|
-
+ `Do not end your turn or
|
|
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) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somacheck/vibecheck",
|
|
3
|
-
"version": "0.6.
|
|
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": {
|