@paigy/mcp 0.38.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -8
- package/dist/{chunk-KE63GZEQ.js → chunk-3QUIB42M.js} +602 -580
- package/dist/{chunk-PPACGOTU.js → chunk-4UZAPBKK.js} +752 -872
- package/dist/{chunk-4UEL7Y5U.js → chunk-CXABA27N.js} +1 -1
- package/dist/chunk-TLASZUJD.js +224 -0
- package/dist/enable.js +3 -3
- package/dist/index.js +11 -152
- package/dist/listen.js +25 -5
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/statusline.js +4 -2
- package/package.json +2 -2
- package/dist/chunk-7IGGSB3H.js +0 -315
- package/dist/chunk-UG2FARVV.js +0 -54
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_TOOLS,
|
|
3
|
+
mcpInputSchema,
|
|
4
|
+
serverInstructions
|
|
5
|
+
} from "./chunk-3QUIB42M.js";
|
|
6
|
+
import {
|
|
7
|
+
AGENT_NAME
|
|
8
|
+
} from "./chunk-4UZAPBKK.js";
|
|
9
|
+
|
|
10
|
+
// src/toolset.ts
|
|
11
|
+
var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
|
|
12
|
+
var SERVER_INSTRUCTIONS = serverInstructions({ waits: true });
|
|
13
|
+
|
|
14
|
+
// src/tools.ts
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
var OnboardSchema = z.object({
|
|
17
|
+
name: z.string().max(60).optional(),
|
|
18
|
+
voice: z.string().max(40).optional(),
|
|
19
|
+
/** Continue a code ceremony already in flight — same meaning as `pair`'s. */
|
|
20
|
+
device_code: z.string().optional(),
|
|
21
|
+
/** Which allowlist to REPORT on (never written by `start`). */
|
|
22
|
+
scope: z.enum(["user", "project"]).optional()
|
|
23
|
+
});
|
|
24
|
+
var PairSchema = z.object({
|
|
25
|
+
device_code: z.string().optional().describe("Omit to start pairing (returns an approval link to show the user). Pass the device_code from that first call to finish, once the user has approved."),
|
|
26
|
+
name: z.string().min(1).max(60).optional().describe("Hatch path only: the name you choose for this identity. Pick your own \u2014 ONE or TWO words, the way you'd introduce yourself on a call (it is spoken aloud and shown in lists). 'Piper', 'Blue Heron' \u2014 never a sentence or a task description."),
|
|
27
|
+
voice: z.string().optional().describe("Hatch path only: your voice on calls \u2014 one of rachel, george, jessica, brian, lily.")
|
|
28
|
+
});
|
|
29
|
+
var IDENTITY_TOOLS = [
|
|
30
|
+
{
|
|
31
|
+
// ONE rail for starting a session (#875). `pair`, the `paigy-mcp-onboard` CLI and
|
|
32
|
+
// `enable_tools` were three doors into one flow, and "onboard" was the word people
|
|
33
|
+
// reached for attached to the tool that did the least. It's the door now.
|
|
34
|
+
name: "onboard",
|
|
35
|
+
description: ONBOARD_DESCRIPTION,
|
|
36
|
+
inputSchema: mcpInputSchema(OnboardSchema)
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "pair",
|
|
40
|
+
description: "Pair this agent with the user's Paigy account (one-time) \u2014 required before contact work. FAST PATH: if this machine already holds a device credential (the user ran the Paigy desktop harness or app), calling pair hatches a fresh identity INSTANTLY \u2014 no code, no approval. Pass { name, voice } to choose who you are (pick your own; voices: rachel, george, jessica, brian, lily). Only when no device credential exists does the code ceremony below run. It does NOT open a browser; the user enters the code in the Paigy app (or scans `qr`). Step 1: call with NO args \u2014 returns { user_code, device_code, qr, user_message } AND starts polling for approval in the background. REQUIRED: You MUST immediately print the `user_message` (the bare code) as a text message to the user, AND in that same turn call step 2 (pair with the device_code). This ensures the user sees the code in chat while the tool blocks/polls in the background for approval. Step 2: call with that device_code to collect the result. Because approval is already being polled in the background, this returns the moment the user approves; on { status:'pending' } just call again to keep waiting; on { status:'awaiting_confirmation' } (E2EE) show the bare `user_message` verify code and call again to finish. The leading text block of every result states the code plainly, so it shows even if you emit no prose. On { status:'paired' } ALWAYS follow the `enable_prompt` \u2014 PRINT the returned `enable_command` so the user can allowlist Paigy's tools and notify/await stop prompting each time. Printing is the whole job: that command writes your own permission allowlist, so you must not run it and a host will block you if you try.",
|
|
41
|
+
inputSchema: mcpInputSchema(PairSchema)
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "unpair",
|
|
45
|
+
description: "Log out / unpair this agent from the user's Paigy account: revokes the token server-side (it stops working everywhere) and deletes the local ~/.paigy/token.json. Takes no arguments. After this, contact won't work until the user pairs again with the pair tool.",
|
|
46
|
+
inputSchema: mcpInputSchema(z.object({}))
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
var TOOLS = [...AGENT_TOOLS, ...IDENTITY_TOOLS];
|
|
50
|
+
var TOOL_NAMES = TOOLS.map((t) => t.name);
|
|
51
|
+
var HIDDEN_ALIASES = [];
|
|
52
|
+
|
|
53
|
+
// src/clients.ts
|
|
54
|
+
import { execFile } from "child_process";
|
|
55
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
56
|
+
import { homedir, platform } from "os";
|
|
57
|
+
import { join, dirname } from "path";
|
|
58
|
+
function openBrowser(url) {
|
|
59
|
+
const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "cmd" : "xdg-open";
|
|
60
|
+
const args = platform() === "win32" ? ["/c", "start", url] : [url];
|
|
61
|
+
execFile(cmd, args, () => {
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function withPaigyMcpJson(existing, agent) {
|
|
65
|
+
let config = {};
|
|
66
|
+
if (existing) {
|
|
67
|
+
try {
|
|
68
|
+
config = JSON.parse(existing);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
config.mcpServers = {
|
|
74
|
+
...config.mcpServers ?? {},
|
|
75
|
+
paigy: { command: "npx", args: ["-y", "@paigy/mcp@latest"], env: { PAIGY_AGENT: agent } }
|
|
76
|
+
};
|
|
77
|
+
return JSON.stringify(config, null, 2);
|
|
78
|
+
}
|
|
79
|
+
function withPaigyMcpToml(existing, agent) {
|
|
80
|
+
if (existing.includes("[mcp_servers.paigy]")) return null;
|
|
81
|
+
const entry = [
|
|
82
|
+
"",
|
|
83
|
+
"[mcp_servers.paigy]",
|
|
84
|
+
'command = "npx"',
|
|
85
|
+
'args = ["-y", "@paigy/mcp@latest"]',
|
|
86
|
+
`env = { PAIGY_AGENT = "${agent}" }`,
|
|
87
|
+
""
|
|
88
|
+
].join("\n");
|
|
89
|
+
return existing + entry;
|
|
90
|
+
}
|
|
91
|
+
function autoConfigureClients(skip = AGENT_NAME) {
|
|
92
|
+
const home = homedir();
|
|
93
|
+
const registered = [];
|
|
94
|
+
if (skip !== "antigravity") {
|
|
95
|
+
const antigravityConfigs = [
|
|
96
|
+
join(home, ".gemini", "config", "mcp_config.json"),
|
|
97
|
+
join(home, ".gemini", "antigravity-cli", "mcp_config.json")
|
|
98
|
+
];
|
|
99
|
+
for (const path of antigravityConfigs) {
|
|
100
|
+
try {
|
|
101
|
+
if (!existsSync(dirname(path))) continue;
|
|
102
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
103
|
+
const updated = withPaigyMcpJson(existing, "antigravity");
|
|
104
|
+
if (updated === null) continue;
|
|
105
|
+
writeFileSync(path, updated);
|
|
106
|
+
registered.push(`Google Antigravity (${path})`);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (skip !== "codex") {
|
|
112
|
+
const codexConfig = join(home, ".codex", "config.toml");
|
|
113
|
+
try {
|
|
114
|
+
if (existsSync(dirname(codexConfig))) {
|
|
115
|
+
const existing = existsSync(codexConfig) ? readFileSync(codexConfig, "utf8") : "";
|
|
116
|
+
const updated = withPaigyMcpToml(existing, "codex");
|
|
117
|
+
if (updated !== null) {
|
|
118
|
+
writeFileSync(codexConfig, updated);
|
|
119
|
+
registered.push(`OpenAI Codex (${codexConfig})`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return registered;
|
|
126
|
+
}
|
|
127
|
+
function claudeInstallHint(skip = AGENT_NAME) {
|
|
128
|
+
if (skip.startsWith("claude")) return null;
|
|
129
|
+
const claudeDir = join(homedir(), ".claude");
|
|
130
|
+
if (!existsSync(claudeDir)) return null;
|
|
131
|
+
try {
|
|
132
|
+
const registry = join(claudeDir, "plugins", "installed_plugins.json");
|
|
133
|
+
if (existsSync(registry) && readFileSync(registry, "utf8").includes('"paigy@')) return null;
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
return "Claude Code detected \u2014 to add Paigy there, run /plugin marketplace add paigy-ai/mcp then /plugin install paigy (pairing carries over; no need to pair again). Note: If you run this inside a live session, type /reload-plugins afterward so the agent connects to the new tools.";
|
|
137
|
+
}
|
|
138
|
+
var ENABLE_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
|
|
139
|
+
var PROMPTS_ON_PURPOSE = /* @__PURE__ */ new Set(["pair", "unpair"]);
|
|
140
|
+
var PAIGY_TOOL_IDS = [
|
|
141
|
+
...TOOL_NAMES.filter((n) => !PROMPTS_ON_PURPOSE.has(n)),
|
|
142
|
+
...HIDDEN_ALIASES
|
|
143
|
+
].map((n) => `mcp__paigy__${n}`);
|
|
144
|
+
function withPaigyAllowlist(existing, tools) {
|
|
145
|
+
let config = {};
|
|
146
|
+
if (existing) {
|
|
147
|
+
try {
|
|
148
|
+
config = JSON.parse(existing);
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const permissions = config.permissions && typeof config.permissions === "object" ? config.permissions : {};
|
|
154
|
+
const allow = Array.isArray(permissions.allow) ? [...permissions.allow] : [];
|
|
155
|
+
for (const t of tools) if (!allow.includes(t)) allow.push(t);
|
|
156
|
+
config.permissions = { ...permissions, allow };
|
|
157
|
+
return JSON.stringify(config, null, 2);
|
|
158
|
+
}
|
|
159
|
+
function settingsPathFor(scope, cwd) {
|
|
160
|
+
return scope === "project" ? join(cwd, ".claude", "settings.json") : join(homedir(), ".claude", "settings.json");
|
|
161
|
+
}
|
|
162
|
+
function paigyToolsAllowlisted(scope = "user", cwd = process.cwd()) {
|
|
163
|
+
try {
|
|
164
|
+
const path = settingsPathFor(scope, cwd);
|
|
165
|
+
if (!path || !existsSync(path)) return false;
|
|
166
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
167
|
+
const allow = Array.isArray(config.permissions?.allow) ? config.permissions.allow : [];
|
|
168
|
+
return PAIGY_TOOL_IDS.every((t) => allow.includes(t));
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function enablePaigyTools(scope = "user", cwd = process.cwd()) {
|
|
174
|
+
if (scope === "user" && !existsSync(join(homedir(), ".claude"))) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
reason: `No ~/.claude on this machine \u2014 the hosting tool uses its own permission allowlist. Add these ids there by hand: ${PAIGY_TOOL_IDS.join(", ")}.`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const path = settingsPathFor(scope, cwd);
|
|
181
|
+
let existing = null;
|
|
182
|
+
try {
|
|
183
|
+
existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
184
|
+
} catch {
|
|
185
|
+
existing = null;
|
|
186
|
+
}
|
|
187
|
+
let prevAllow = [];
|
|
188
|
+
if (existing) {
|
|
189
|
+
try {
|
|
190
|
+
const parsed = JSON.parse(existing);
|
|
191
|
+
if (Array.isArray(parsed.permissions?.allow)) prevAllow = parsed.permissions.allow;
|
|
192
|
+
} catch {
|
|
193
|
+
return { ok: false, reason: `${path} didn't parse as JSON \u2014 not overwriting it. Add the ids by hand.` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const updated = withPaigyAllowlist(existing, PAIGY_TOOL_IDS);
|
|
197
|
+
if (updated === null) return { ok: false, reason: `${path} didn't parse as JSON \u2014 not overwriting it. Add the ids by hand.` };
|
|
198
|
+
try {
|
|
199
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
200
|
+
writeFileSync(path, updated + "\n");
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return { ok: false, reason: `Couldn't write ${path}: ${e.message}` };
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
path,
|
|
207
|
+
added: PAIGY_TOOL_IDS.filter((t) => !prevAllow.includes(t)),
|
|
208
|
+
already: PAIGY_TOOL_IDS.filter((t) => prevAllow.includes(t))
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export {
|
|
213
|
+
SERVER_INSTRUCTIONS,
|
|
214
|
+
OnboardSchema,
|
|
215
|
+
PairSchema,
|
|
216
|
+
TOOLS,
|
|
217
|
+
openBrowser,
|
|
218
|
+
autoConfigureClients,
|
|
219
|
+
claudeInstallHint,
|
|
220
|
+
ENABLE_COMMAND,
|
|
221
|
+
PAIGY_TOOL_IDS,
|
|
222
|
+
paigyToolsAllowlisted,
|
|
223
|
+
enablePaigyTools
|
|
224
|
+
};
|
package/dist/enable.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PAIGY_TOOL_IDS,
|
|
4
4
|
enablePaigyTools
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-TLASZUJD.js";
|
|
6
|
+
import "./chunk-3QUIB42M.js";
|
|
7
|
+
import "./chunk-4UZAPBKK.js";
|
|
8
8
|
|
|
9
9
|
// src/enable.ts
|
|
10
10
|
function main() {
|
package/dist/index.js
CHANGED
|
@@ -1,74 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
awaitMessage,
|
|
4
|
-
contactMessage
|
|
5
|
-
} from "./chunk-UG2FARVV.js";
|
|
6
2
|
import {
|
|
7
3
|
clearPairing,
|
|
8
4
|
joinBackgroundPair,
|
|
9
5
|
resolvePairing,
|
|
10
6
|
startPairing,
|
|
11
7
|
suggestedAgentName
|
|
12
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CXABA27N.js";
|
|
13
9
|
import {
|
|
14
|
-
AnswerCallerQuestionSchema,
|
|
15
|
-
AwaitReplySchema,
|
|
16
|
-
ClaimGoalSchema,
|
|
17
10
|
ENABLE_COMMAND,
|
|
18
|
-
GetThreadSchema,
|
|
19
11
|
OnboardSchema,
|
|
20
12
|
PAIGY_TOOL_IDS,
|
|
21
13
|
PairSchema,
|
|
22
14
|
SERVER_INSTRUCTIONS,
|
|
23
|
-
SearchThreadsSchema,
|
|
24
|
-
SetTaskStateToolSchema,
|
|
25
15
|
TOOLS,
|
|
26
|
-
UpdateGoalToolSchema,
|
|
27
16
|
autoConfigureClients,
|
|
28
17
|
claudeInstallHint,
|
|
29
18
|
paigyToolsAllowlisted
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import
|
|
32
|
-
CreateGoalSchema,
|
|
33
|
-
HandoffSchema
|
|
34
|
-
} from "./chunk-KE63GZEQ.js";
|
|
19
|
+
} from "./chunk-TLASZUJD.js";
|
|
20
|
+
import "./chunk-3QUIB42M.js";
|
|
35
21
|
import {
|
|
36
22
|
clearSurface,
|
|
37
23
|
writeSurface
|
|
38
24
|
} from "./chunk-3SVNPJPN.js";
|
|
39
25
|
import {
|
|
40
26
|
AGENT_NAME,
|
|
41
|
-
NotifyRequestSchema,
|
|
42
|
-
ScheduleCallbackSchema,
|
|
43
|
-
SetWorkStateSchema,
|
|
44
27
|
UnpairedError,
|
|
45
|
-
answerCallerQuestion,
|
|
46
|
-
awaitReply2,
|
|
47
|
-
checkReplies,
|
|
48
|
-
claimGoal,
|
|
49
|
-
contactParentId,
|
|
50
|
-
createGoal,
|
|
51
28
|
deleteKeyFile,
|
|
52
29
|
deleteToken,
|
|
53
|
-
getThread,
|
|
54
|
-
handoff,
|
|
55
30
|
hatch,
|
|
56
31
|
heartbeat,
|
|
57
32
|
listSlots,
|
|
58
33
|
overrideToken,
|
|
59
34
|
readToken,
|
|
60
35
|
revokeToken,
|
|
36
|
+
runTool,
|
|
61
37
|
saveToken,
|
|
62
|
-
scheduleCallback,
|
|
63
|
-
searchThreads,
|
|
64
38
|
setIdentity,
|
|
65
|
-
setTaskState,
|
|
66
|
-
setWorkState,
|
|
67
39
|
slotName,
|
|
68
|
-
submitNotification,
|
|
69
|
-
updateGoal,
|
|
70
40
|
whoAmI
|
|
71
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-4UZAPBKK.js";
|
|
72
42
|
|
|
73
43
|
// src/index.ts
|
|
74
44
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -77,36 +47,8 @@ import {
|
|
|
77
47
|
CallToolRequestSchema,
|
|
78
48
|
ListToolsRequestSchema
|
|
79
49
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
80
|
-
import { execSync } from "child_process";
|
|
81
50
|
import qrcode from "qrcode-generator";
|
|
82
51
|
var JOIN_CAP_MS = 45e3;
|
|
83
|
-
function fixAndRetry(err) {
|
|
84
|
-
const msg = String(err?.message ?? err);
|
|
85
|
-
const m = msg.match(/^notify failed: 422 (\{[\s\S]*\})$/);
|
|
86
|
-
if (!m) return null;
|
|
87
|
-
try {
|
|
88
|
-
const body = JSON.parse(m[1]);
|
|
89
|
-
return body.error === "fix_and_retry" ? { error: "fix_and_retry", problems: body.problems ?? [] } : null;
|
|
90
|
-
} catch {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function detectGit() {
|
|
95
|
-
const run = (cmd) => {
|
|
96
|
-
try {
|
|
97
|
-
return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }).trim();
|
|
98
|
-
} catch {
|
|
99
|
-
return "";
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
const remote = run("git remote get-url origin");
|
|
103
|
-
const match = remote.match(/[:/]([^/:]+\/[^/]+?)(?:\.git)?$/);
|
|
104
|
-
const branch = run("git rev-parse --abbrev-ref HEAD");
|
|
105
|
-
return {
|
|
106
|
-
repo: match ? match[1] : void 0,
|
|
107
|
-
branch: branch && branch !== "HEAD" ? branch : void 0
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
52
|
function pairedResult(token, sas, note, e2ee) {
|
|
111
53
|
clearSurface();
|
|
112
54
|
const base = {
|
|
@@ -379,96 +321,13 @@ async function handleTool(request, signal) {
|
|
|
379
321
|
}]
|
|
380
322
|
};
|
|
381
323
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const parsed = NotifyRequestSchema.parse(request.params.arguments);
|
|
389
|
-
const git = detectGit();
|
|
390
|
-
const enriched = {
|
|
391
|
-
...parsed,
|
|
392
|
-
repo: parsed.repo ?? git.repo,
|
|
393
|
-
branch: parsed.branch ?? git.branch,
|
|
394
|
-
// THREAD-SCOPED IDENTITY (stage 3, identity-ownership-design.md). A worker subagent
|
|
395
|
-
// shares the parent's identity/slot, so its requests would otherwise scatter across
|
|
396
|
-
// fresh threads and mix with a sibling's. Default an unthreaded contact to THIS
|
|
397
|
-
// subagent's own thread so its requests group and siblings never collide. An explicit
|
|
398
|
-
// parentId (continuing a specific conversation) always wins; a top-level parent session
|
|
399
|
-
// is left untouched (undefined ⇒ the server mints a fresh thread, exactly as today).
|
|
400
|
-
parentId: contactParentId(parsed.parentId)
|
|
401
|
-
};
|
|
402
|
-
let result;
|
|
403
|
-
try {
|
|
404
|
-
result = await submitNotification(enriched);
|
|
405
|
-
} catch (err) {
|
|
406
|
-
const fixable = fixAndRetry(err);
|
|
407
|
-
if (fixable) return { isError: true, content: [{ type: "text", text: JSON.stringify(fixable) }] };
|
|
408
|
-
throw err;
|
|
409
|
-
}
|
|
410
|
-
const sentAs = slotName(AGENT_NAME);
|
|
411
|
-
const shown = { ...result, message: contactMessage(result) };
|
|
412
|
-
return { content: [{ type: "text", text: JSON.stringify(sentAs ? { ...shown, sentAs } : shown) }] };
|
|
413
|
-
}
|
|
414
|
-
case "await_reply": {
|
|
415
|
-
const { notificationId } = AwaitReplySchema.parse(request.params.arguments);
|
|
416
|
-
const item = await awaitReply2(notificationId, {
|
|
417
|
-
...signal ? { signal } : {}
|
|
418
|
-
});
|
|
419
|
-
return { content: [{ type: "text", text: JSON.stringify({ ...item, message: awaitMessage(item) }) }] };
|
|
420
|
-
}
|
|
421
|
-
case "check_replies": {
|
|
422
|
-
const result = await checkReplies();
|
|
423
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
424
|
-
}
|
|
425
|
-
case "get_thread": {
|
|
426
|
-
const { parentId } = GetThreadSchema.parse(request.params.arguments);
|
|
427
|
-
return { content: [{ type: "text", text: JSON.stringify(await getThread(parentId)) }] };
|
|
428
|
-
}
|
|
429
|
-
case "search_threads": {
|
|
430
|
-
const { q } = SearchThreadsSchema.parse(request.params.arguments);
|
|
431
|
-
return { content: [{ type: "text", text: JSON.stringify(await searchThreads(q)) }] };
|
|
432
|
-
}
|
|
433
|
-
case "answer_caller_question": {
|
|
434
|
-
const { notificationId, answer } = AnswerCallerQuestionSchema.parse(request.params.arguments);
|
|
435
|
-
const result = await answerCallerQuestion(notificationId, answer);
|
|
436
|
-
return { content: [{ type: "text", text: JSON.stringify({
|
|
437
|
-
...result,
|
|
438
|
-
...result.live ? {} : { note: "The call already ended \u2014 send this as a threaded contact instead so the answer still reaches them." }
|
|
439
|
-
}) }] };
|
|
440
|
-
}
|
|
441
|
-
case "set_task_state": {
|
|
442
|
-
const { notificationId, state } = SetTaskStateToolSchema.parse(request.params.arguments);
|
|
443
|
-
const result = await setTaskState(notificationId, state);
|
|
444
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
445
|
-
}
|
|
446
|
-
case "set_work_state": {
|
|
447
|
-
const result = await setWorkState(SetWorkStateSchema.parse(request.params.arguments));
|
|
448
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
449
|
-
}
|
|
450
|
-
case "create_goal": {
|
|
451
|
-
const result = await createGoal(CreateGoalSchema.parse(request.params.arguments));
|
|
452
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
453
|
-
}
|
|
454
|
-
case "claim_goal": {
|
|
455
|
-
const { goalId } = ClaimGoalSchema.parse(request.params.arguments);
|
|
456
|
-
return { content: [{ type: "text", text: JSON.stringify(await claimGoal(goalId)) }] };
|
|
457
|
-
}
|
|
458
|
-
case "update_goal": {
|
|
459
|
-
const { goalId, ...input } = UpdateGoalToolSchema.parse(request.params.arguments);
|
|
460
|
-
return { content: [{ type: "text", text: JSON.stringify(await updateGoal(goalId, input)) }] };
|
|
461
|
-
}
|
|
462
|
-
case "schedule_callback": {
|
|
463
|
-
const result = await scheduleCallback(ScheduleCallbackSchema.parse(request.params.arguments));
|
|
464
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
465
|
-
}
|
|
466
|
-
case "handoff": {
|
|
467
|
-
const result = await handoff(HandoffSchema.parse(request.params.arguments));
|
|
468
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
324
|
+
default: {
|
|
325
|
+
const name = request.params.name;
|
|
326
|
+
const result = await runTool(name, request.params.arguments ?? {}, { waits: true, signal });
|
|
327
|
+
const sentAs = name === "contact" ? slotName(AGENT_NAME) : null;
|
|
328
|
+
const shaped = sentAs && result && typeof result === "object" ? { ...result, sentAs } : result;
|
|
329
|
+
return { content: [{ type: "text", text: JSON.stringify(shaped) }] };
|
|
469
330
|
}
|
|
470
|
-
default:
|
|
471
|
-
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
472
331
|
}
|
|
473
332
|
}
|
|
474
333
|
var beatDown = false;
|
package/dist/listen.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
answerText
|
|
4
|
-
} from "./chunk-UG2FARVV.js";
|
|
5
2
|
import {
|
|
6
3
|
WAKE_EVENT,
|
|
7
4
|
wakeChannel
|
|
8
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3QUIB42M.js";
|
|
9
6
|
import {
|
|
10
7
|
ackReply,
|
|
11
8
|
authToken,
|
|
12
9
|
decodeReply,
|
|
13
10
|
registerDelivery,
|
|
14
11
|
sweepLeased
|
|
15
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4UZAPBKK.js";
|
|
16
13
|
|
|
17
14
|
// src/listen.ts
|
|
18
15
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -117,6 +114,29 @@ function uninstallService() {
|
|
|
117
114
|
return unit;
|
|
118
115
|
}
|
|
119
116
|
|
|
117
|
+
// src/message.ts
|
|
118
|
+
function answerText(answer) {
|
|
119
|
+
switch (answer.kind) {
|
|
120
|
+
case "text":
|
|
121
|
+
return answer.text;
|
|
122
|
+
case "option":
|
|
123
|
+
return answer.label ?? answer.optionId;
|
|
124
|
+
case "multi":
|
|
125
|
+
case "ranked":
|
|
126
|
+
return (answer.labels ?? answer.optionIds).join(", ");
|
|
127
|
+
case "confirm":
|
|
128
|
+
return answer.approved ? "approved" : "declined";
|
|
129
|
+
case "clarify":
|
|
130
|
+
return answer.chunks.join(" ");
|
|
131
|
+
case "turns":
|
|
132
|
+
return answer.turns.map((turn) => turn.reply).join(" ");
|
|
133
|
+
case "precedent":
|
|
134
|
+
return answer.answer;
|
|
135
|
+
case "ignored":
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
// src/listen.ts
|
|
121
141
|
var emit = (obj) => void process.stdout.write(JSON.stringify(obj) + "\n");
|
|
122
142
|
function launchEnv(work, reason) {
|
package/dist/onboard.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
resolvePairing,
|
|
4
4
|
startPairing,
|
|
5
5
|
suggestedAgentName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CXABA27N.js";
|
|
7
7
|
import {
|
|
8
8
|
autoConfigureClients,
|
|
9
9
|
claudeInstallHint,
|
|
10
10
|
openBrowser
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-TLASZUJD.js";
|
|
12
|
+
import "./chunk-3QUIB42M.js";
|
|
13
13
|
import {
|
|
14
14
|
AGENT_NAME,
|
|
15
15
|
TOKEN_PATH,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
saveToken,
|
|
21
21
|
setIdentity,
|
|
22
22
|
whoAmI
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-4UZAPBKK.js";
|
|
24
24
|
|
|
25
25
|
// src/onboard.ts
|
|
26
26
|
function reportRegistered() {
|
package/dist/slot.js
CHANGED
package/dist/statusline.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
readToken,
|
|
10
10
|
sessionSlot,
|
|
11
11
|
slotName
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4UZAPBKK.js";
|
|
13
13
|
|
|
14
14
|
// src/statusline.ts
|
|
15
15
|
import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
@@ -35,7 +35,9 @@ function render(v) {
|
|
|
35
35
|
case "session": {
|
|
36
36
|
const who = v.name ? `${v.name} \xB7 ` : "";
|
|
37
37
|
if (!v.status) return `paigy: ${who}revoked`;
|
|
38
|
-
|
|
38
|
+
const n = v.status.waiting ?? 0;
|
|
39
|
+
const waiting = n > 0 ? ` \xB7 ${n} waiting` : "";
|
|
40
|
+
return `paigy: ${who}${MODE_LABEL[v.status.sessionMode]} \xB7 ${v.status.phone ? "phone \u2713" : "no phone"}${waiting}`;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"vitest": "^2.1.8",
|
|
41
41
|
"@paigy/crypto": "0.0.0",
|
|
42
42
|
"@paigy/schema": "0.0.0",
|
|
43
|
-
"@paigy/sdk": "0.2.
|
|
43
|
+
"@paigy/sdk": "0.2.1"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup",
|