@paigy/mcp 0.33.0 → 0.35.0
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/dist/{chunk-SASREQWV.js → chunk-KNMI3CJ3.js} +1 -1
- package/dist/{chunk-E3OWSUGJ.js → chunk-U6SLIORK.js} +288 -56
- package/dist/{chunk-JT7I3EGY.js → chunk-YOGAG4YY.js} +80 -7
- package/dist/enable.js +2 -2
- package/dist/index.js +9 -9
- package/dist/listen.js +4 -4
- package/dist/onboard.js +5 -5
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -2645,10 +2645,10 @@ var IntentSchema = z.object({
|
|
|
2645
2645
|
// questions included. Found auditing five calls' stored feedback, 2026-08-01.
|
|
2646
2646
|
kind: z.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
|
|
2647
2647
|
detail: z.string(),
|
|
2648
|
-
/**
|
|
2649
|
-
*
|
|
2650
|
-
* the
|
|
2651
|
-
*
|
|
2648
|
+
/** Defer only: seconds until the callback the caller asked for, when something upstream
|
|
2649
|
+
* already read the time. Nothing sets it today (#397 documented an MCP parser that was
|
|
2650
|
+
* never written) — the API reads the defer's `detail` itself with `notes/when.ts`
|
|
2651
|
+
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
2652
2652
|
dueInSeconds: z.number().int().positive().optional(),
|
|
2653
2653
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
2654
2654
|
* already read the utterance, so `feedback_from_call.kind` stops defaulting to
|
|
@@ -2756,6 +2756,12 @@ var PendingRepliesSchema = z.object({
|
|
|
2756
2756
|
pending: z.array(
|
|
2757
2757
|
z.object({ parentId: z.string(), notificationId: z.string(), createdAt: z.string() })
|
|
2758
2758
|
),
|
|
2759
|
+
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
2760
|
+
* sees for this session's identity. From inside a session there was no way to find out —
|
|
2761
|
+
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
2762
|
+
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
2763
|
+
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
2764
|
+
you: z.object({ name: z.string(), device: z.string().nullable(), tokenId: z.string() }).optional(),
|
|
2759
2765
|
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
2760
2766
|
* contact on the same parentId. Keeps reappearing until you call
|
|
2761
2767
|
* set_task_state on its notificationId. */
|
|
@@ -2767,7 +2773,13 @@ var PendingRepliesSchema = z.object({
|
|
|
2767
2773
|
createdAt: z.string(),
|
|
2768
2774
|
/** The user seeded this request with a past conversation — call get_thread on it
|
|
2769
2775
|
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
2770
|
-
contextParentId: z.string().optional()
|
|
2776
|
+
contextParentId: z.string().optional(),
|
|
2777
|
+
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
2778
|
+
* the account — the name here — which has not been seen since it landed, so nobody
|
|
2779
|
+
* came for it. Handed to you because you are the session that is here. Take it like
|
|
2780
|
+
* any request (set_task_state claims it, reply with contact on its parentId), and say
|
|
2781
|
+
* whose it was, because the user chose that agent on purpose. */
|
|
2782
|
+
stranded: z.string().optional()
|
|
2771
2783
|
})
|
|
2772
2784
|
),
|
|
2773
2785
|
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
@@ -3068,7 +3080,13 @@ var BrokerTuningSchema = z.object({
|
|
|
3068
3080
|
* uninterpretable, 'high' = whenever not fully certain. */
|
|
3069
3081
|
clarifyEagerness: z.enum(["low", "normal", "high"]).optional(),
|
|
3070
3082
|
/** The user's own shorthand: when they say `say`, they mean `mean`. */
|
|
3071
|
-
phrasebook: z.array(z.object({ say: z.string().min(1).max(60), mean: z.string().min(1).max(120) })).max(24).optional()
|
|
3083
|
+
phrasebook: z.array(z.object({ say: z.string().min(1).max(60), mean: z.string().min(1).max(120) })).max(24).optional(),
|
|
3084
|
+
/** The language calls are PLANNED in, when the account has chosen one (#1272). Absent —
|
|
3085
|
+
* which is every account today — means the agent's own words decide, per ask: a call
|
|
3086
|
+
* about an English ask opens in English. This is the only thing that overrides that,
|
|
3087
|
+
* and a live caller who switches language mid-call still outranks it (broker/lang.ts).
|
|
3088
|
+
* Set per user (no UI yet), like `voiceTuning`. */
|
|
3089
|
+
language: z.enum(["en", "es"]).optional()
|
|
3072
3090
|
});
|
|
3073
3091
|
var UserSettingsSchema = z.object({
|
|
3074
3092
|
permissions: z.object({
|
|
@@ -3276,7 +3294,9 @@ var NoteSchema = z.object({
|
|
|
3276
3294
|
/** The request thread minted at assignment; null until assigned. */
|
|
3277
3295
|
parentId: z.string().nullable(),
|
|
3278
3296
|
/** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
3279
|
-
* call — never a deadline
|
|
3297
|
+
* call — never a deadline. It only ever comes from the user's own words, so when it
|
|
3298
|
+
* passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
|
|
3299
|
+
* a ring at that time); after that it rides like any other. Null = "the very next
|
|
3280
3300
|
* call", the right reading of "remind me to…" with no time attached. */
|
|
3281
3301
|
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
3282
3302
|
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
@@ -3319,6 +3339,59 @@ var AssignNoteSchema = z.object({
|
|
|
3319
3339
|
workspace: z.string().min(1)
|
|
3320
3340
|
}).optional()
|
|
3321
3341
|
}).refine((a) => !!a.target !== !!a.spawn, { message: "exactly one of target or spawn" });
|
|
3342
|
+
var TriageVerdictSchema = z.enum(["keep", "stale", "close", "assign"]);
|
|
3343
|
+
var TriageItemSchema = z.object({
|
|
3344
|
+
noteId: z.string(),
|
|
3345
|
+
/** The note's headline at run time. */
|
|
3346
|
+
title: z.string(),
|
|
3347
|
+
/** WHY, in one short human line, evidence first — this is read on a phone underneath
|
|
3348
|
+
* the note's title: "no movement in 34 days", "worked 3 notes in this repo this week".
|
|
3349
|
+
* Never a model's reasoning transcript, never an id. */
|
|
3350
|
+
why: z.string()
|
|
3351
|
+
});
|
|
3352
|
+
var TriageAssignmentSchema = z.object({
|
|
3353
|
+
/** The agent's token id — what `dispatchNote` resolves and what a request is addressed to. */
|
|
3354
|
+
agent: z.string(),
|
|
3355
|
+
/** Its display name at run time (the name on the hatchling's card). Denormalized for the
|
|
3356
|
+
* same reason as `title`: the card must render from the proposal alone. */
|
|
3357
|
+
agentName: z.string(),
|
|
3358
|
+
notes: z.array(TriageItemSchema)
|
|
3359
|
+
});
|
|
3360
|
+
var TriageStatusSchema = z.enum(["open", "superseded", "dismissed"]);
|
|
3361
|
+
var SubmitTriageSchema = z.object({
|
|
3362
|
+
/** Which runtime judged: "ollama" (inference never left the machine) or a harness the
|
|
3363
|
+
* user already runs under their own credentials ("claude" / "codex" / "agy"). Recorded
|
|
3364
|
+
* so the phone can say where the content went — an unattributed privacy claim is worth
|
|
3365
|
+
* nothing, and #1106's promise is precisely "Paigy's servers never see this". */
|
|
3366
|
+
provider: z.string().min(1).max(60),
|
|
3367
|
+
/** The concrete model when the provider names one (an ollama tag); null otherwise. */
|
|
3368
|
+
model: z.string().max(200).nullable().optional(),
|
|
3369
|
+
/** How many open notes the run actually looked at — the denominator on the phone
|
|
3370
|
+
* ("6 of 50"), and the honest answer to "did it read the whole queue?". */
|
|
3371
|
+
reviewed: z.number().int().min(0).max(1e4).default(0),
|
|
3372
|
+
close: z.array(TriageItemSchema).max(200).default([]),
|
|
3373
|
+
stale: z.array(TriageItemSchema).max(200).default([]),
|
|
3374
|
+
assign: z.array(TriageAssignmentSchema).max(50).default([])
|
|
3375
|
+
});
|
|
3376
|
+
var TriageProposalSchema = SubmitTriageSchema.extend({
|
|
3377
|
+
id: z.string(),
|
|
3378
|
+
runAt: z.string(),
|
|
3379
|
+
status: TriageStatusSchema,
|
|
3380
|
+
model: z.string().nullable().default(null)
|
|
3381
|
+
});
|
|
3382
|
+
var AcceptTriageSchema = z.discriminatedUnion("group", [
|
|
3383
|
+
z.object({ group: z.literal("close"), noteIds: z.array(z.string()).max(200).optional() }),
|
|
3384
|
+
z.object({ group: z.literal("stale"), noteIds: z.array(z.string()).max(200).optional() }),
|
|
3385
|
+
z.object({
|
|
3386
|
+
group: z.literal("assign"),
|
|
3387
|
+
agent: z.string().min(1),
|
|
3388
|
+
noteIds: z.array(z.string()).max(200).optional()
|
|
3389
|
+
})
|
|
3390
|
+
]);
|
|
3391
|
+
var AcceptTriageResultSchema = z.object({
|
|
3392
|
+
accepted: z.array(z.string()),
|
|
3393
|
+
failed: z.array(z.object({ noteId: z.string(), reason: z.string() }))
|
|
3394
|
+
});
|
|
3322
3395
|
var DeliveryModeSchema = z.enum(["poll", "self_hosted"]);
|
|
3323
3396
|
var RegisterDeliverySchema = z.object({ mode: DeliveryModeSchema });
|
|
3324
3397
|
var OAuthStartSchema = z.object({
|
|
@@ -4100,7 +4173,6 @@ async function submitNotification(req, opts = {}) {
|
|
|
4100
4173
|
if (!res.ok) throw new Error(`notify failed: ${res.status} ${await res.text()}`);
|
|
4101
4174
|
return await res.json();
|
|
4102
4175
|
}
|
|
4103
|
-
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4104
4176
|
function decryptAnswer(reply, deps = {}) {
|
|
4105
4177
|
if (!reply.sealed) return reply.answer;
|
|
4106
4178
|
const keyFile = (deps.readKeyFile ?? readKeyFile)();
|
|
@@ -4109,15 +4181,6 @@ function decryptAnswer(reply, deps = {}) {
|
|
|
4109
4181
|
}
|
|
4110
4182
|
return openSealedAnswer(reply.sealed, keyFile.keypair);
|
|
4111
4183
|
}
|
|
4112
|
-
async function pollAnswer(notificationId) {
|
|
4113
|
-
const token = readToken();
|
|
4114
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/await?notificationId=${encodeURIComponent(notificationId)}`, {
|
|
4115
|
-
headers: { authorization: `Bearer ${token}` }
|
|
4116
|
-
}));
|
|
4117
|
-
if (!res.ok) throw new Error(`await failed: ${res.status} ${await res.text()}`);
|
|
4118
|
-
const item = await res.json();
|
|
4119
|
-
return item.type === "idle" ? item : decryptItem(item);
|
|
4120
|
-
}
|
|
4121
4184
|
var AWAIT_WINDOW_MS = 45e3;
|
|
4122
4185
|
var _partialSeen = /* @__PURE__ */ new Map();
|
|
4123
4186
|
async function pollPartials(notificationId) {
|
|
@@ -4138,30 +4201,6 @@ async function pollPartials(notificationId) {
|
|
|
4138
4201
|
return null;
|
|
4139
4202
|
}
|
|
4140
4203
|
}
|
|
4141
|
-
async function awaitReply(notificationId, opts = {}) {
|
|
4142
|
-
const intervalMs = opts.intervalMs ?? 5e3;
|
|
4143
|
-
const windowMs = opts.windowMs ?? AWAIT_WINDOW_MS;
|
|
4144
|
-
const doSleep = opts.sleep ?? sleep2;
|
|
4145
|
-
const now = opts.now ?? Date.now;
|
|
4146
|
-
const start = now();
|
|
4147
|
-
while (true) {
|
|
4148
|
-
if (opts.signal?.aborted) return { type: "idle" };
|
|
4149
|
-
const item = await pollAnswer(notificationId);
|
|
4150
|
-
if (item.type !== "idle") return item;
|
|
4151
|
-
const partial = await pollPartials(notificationId);
|
|
4152
|
-
if (partial) return partial;
|
|
4153
|
-
if (now() - start >= windowMs) return { type: "idle" };
|
|
4154
|
-
await doSleep(intervalMs);
|
|
4155
|
-
}
|
|
4156
|
-
}
|
|
4157
|
-
function decryptItem(item) {
|
|
4158
|
-
if (item.type !== "reply") return item;
|
|
4159
|
-
const enriched = { ...item, intents: landIntents(item.intents) };
|
|
4160
|
-
if (!enriched.sealed) return enriched;
|
|
4161
|
-
const answer = decryptAnswer(enriched);
|
|
4162
|
-
const { sealed: _s, ...rest } = enriched;
|
|
4163
|
-
return { ...rest, answer };
|
|
4164
|
-
}
|
|
4165
4204
|
function landIntents(intents) {
|
|
4166
4205
|
if (!intents?.length) return intents;
|
|
4167
4206
|
return intents.map((i) => {
|
|
@@ -4187,10 +4226,14 @@ async function searchThreads(q) {
|
|
|
4187
4226
|
async function checkReplies(opts = {}) {
|
|
4188
4227
|
const token = authToken(opts.token);
|
|
4189
4228
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/pending`, {
|
|
4190
|
-
headers: {
|
|
4229
|
+
headers: {
|
|
4230
|
+
authorization: `Bearer ${token}`,
|
|
4231
|
+
...opts.replyAck ? { "x-paigy-await-ack": "1" } : {}
|
|
4232
|
+
}
|
|
4191
4233
|
}));
|
|
4192
4234
|
if (!res.ok) throw new Error(`check_replies failed: ${res.status} ${await res.text()}`);
|
|
4193
4235
|
const pending = await res.json();
|
|
4236
|
+
if (opts.replyAck) return pending;
|
|
4194
4237
|
return {
|
|
4195
4238
|
...pending,
|
|
4196
4239
|
replies: pending.replies.map((raw) => {
|
|
@@ -4255,15 +4298,6 @@ async function heartbeat(runtime, opts = {}) {
|
|
|
4255
4298
|
}));
|
|
4256
4299
|
if (!res.ok) throw new Error(`heartbeat failed: ${res.status}`);
|
|
4257
4300
|
}
|
|
4258
|
-
async function setTaskState(notificationId, state, opts = {}) {
|
|
4259
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/notify/${notificationId}/state`, {
|
|
4260
|
-
method: "PATCH",
|
|
4261
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
4262
|
-
body: JSON.stringify({ state })
|
|
4263
|
-
}));
|
|
4264
|
-
if (!res.ok) throw new Error(`set_task_state failed: ${res.status} ${await res.text()}`);
|
|
4265
|
-
return await res.json();
|
|
4266
|
-
}
|
|
4267
4301
|
async function registerDelivery(mode) {
|
|
4268
4302
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/delivery`, {
|
|
4269
4303
|
method: "POST",
|
|
@@ -4293,6 +4327,204 @@ async function handoff(req) {
|
|
|
4293
4327
|
if (!res.ok) throw new Error(`handoff failed: ${res.status} ${await res.text()}`);
|
|
4294
4328
|
return await res.json();
|
|
4295
4329
|
}
|
|
4330
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4331
|
+
var ReplyLeaseExpiredError = class extends Error {
|
|
4332
|
+
constructor() {
|
|
4333
|
+
super(
|
|
4334
|
+
"The Paigy reply lease expired before acknowledgement. The answer remains durable; call await_reply again to reclaim it before acting."
|
|
4335
|
+
);
|
|
4336
|
+
this.name = "ReplyLeaseExpiredError";
|
|
4337
|
+
}
|
|
4338
|
+
};
|
|
4339
|
+
var ReplyAckError = class extends Error {
|
|
4340
|
+
constructor(message, retryable) {
|
|
4341
|
+
super(message);
|
|
4342
|
+
this.retryable = retryable;
|
|
4343
|
+
this.name = "ReplyAckError";
|
|
4344
|
+
}
|
|
4345
|
+
retryable;
|
|
4346
|
+
};
|
|
4347
|
+
function ensureAuthed2(res) {
|
|
4348
|
+
if (res.status === 401) throw new UnpairedError();
|
|
4349
|
+
return res;
|
|
4350
|
+
}
|
|
4351
|
+
function decryptAnswer2(reply, deps = {}) {
|
|
4352
|
+
if (!reply.sealed) return reply.answer;
|
|
4353
|
+
const keyFile = (deps.readKeyFile ?? readKeyFile)();
|
|
4354
|
+
if (!keyFile?.keypair) {
|
|
4355
|
+
throw new Error(
|
|
4356
|
+
"received a sealed answer but this agent has no device key to open it \u2014 re-pair."
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
return openSealedAnswer(reply.sealed, keyFile.keypair);
|
|
4360
|
+
}
|
|
4361
|
+
function landIntents2(intents) {
|
|
4362
|
+
if (!intents?.length) return intents;
|
|
4363
|
+
return intents.map((intent) => {
|
|
4364
|
+
if (intent.kind !== "defer") return intent;
|
|
4365
|
+
const due = parseDue(intent.detail);
|
|
4366
|
+
return due !== null ? { ...intent, dueInSeconds: due } : intent;
|
|
4367
|
+
});
|
|
4368
|
+
}
|
|
4369
|
+
function decodeReply(item, deps = {}) {
|
|
4370
|
+
const { delivery: _delivery, ...withoutDelivery } = item;
|
|
4371
|
+
const enriched = {
|
|
4372
|
+
...withoutDelivery,
|
|
4373
|
+
intents: landIntents2(withoutDelivery.intents)
|
|
4374
|
+
};
|
|
4375
|
+
if (!enriched.sealed) return enriched;
|
|
4376
|
+
const answer = decryptAnswer2(enriched, deps);
|
|
4377
|
+
const { sealed: _sealed, ...plaintext } = enriched;
|
|
4378
|
+
return { ...plaintext, answer };
|
|
4379
|
+
}
|
|
4380
|
+
function deliveryOf(item) {
|
|
4381
|
+
if (item.type !== "reply" || !("delivery" in item) || item.delivery === void 0) {
|
|
4382
|
+
return null;
|
|
4383
|
+
}
|
|
4384
|
+
const delivery = item.delivery;
|
|
4385
|
+
if (!delivery || typeof delivery.leaseId !== "string" || !delivery.leaseId || typeof delivery.expiresAt !== "string" || !delivery.expiresAt) {
|
|
4386
|
+
throw new Error(
|
|
4387
|
+
"Paigy returned invalid reply-delivery metadata; the answer was not acknowledged and remains recoverable."
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4390
|
+
return delivery;
|
|
4391
|
+
}
|
|
4392
|
+
async function responseDetail(res) {
|
|
4393
|
+
try {
|
|
4394
|
+
return (await res.text()).slice(0, 300);
|
|
4395
|
+
} catch {
|
|
4396
|
+
return "";
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
async function acknowledge(token, leaseId, opts = {}) {
|
|
4400
|
+
const attempts = Math.max(1, Math.min(opts.attempts ?? 3, 5));
|
|
4401
|
+
const backoffMs = Math.max(0, opts.backoffMs ?? 100);
|
|
4402
|
+
const doSleep = opts.doSleep ?? sleep2;
|
|
4403
|
+
let lastError;
|
|
4404
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
4405
|
+
try {
|
|
4406
|
+
const res = ensureAuthed2(await reach(`${BACKEND_URL}/api/await/ack`, {
|
|
4407
|
+
method: "POST",
|
|
4408
|
+
headers: {
|
|
4409
|
+
authorization: `Bearer ${token}`,
|
|
4410
|
+
"content-type": "application/json"
|
|
4411
|
+
},
|
|
4412
|
+
body: JSON.stringify({ leaseId })
|
|
4413
|
+
}));
|
|
4414
|
+
if (res.ok) return;
|
|
4415
|
+
const detail2 = await responseDetail(res);
|
|
4416
|
+
if (res.status === 409) throw new ReplyLeaseExpiredError();
|
|
4417
|
+
const retryable = res.status === 408 || res.status === 425 || res.status === 429 || res.status >= 500;
|
|
4418
|
+
throw new ReplyAckError(
|
|
4419
|
+
`Paigy reply acknowledgement failed: ${res.status}${detail2 ? ` ${detail2}` : ""}`,
|
|
4420
|
+
retryable
|
|
4421
|
+
);
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
if (error instanceof UnpairedError || error instanceof ReplyLeaseExpiredError || error instanceof ReplyAckError && !error.retryable) {
|
|
4424
|
+
throw error;
|
|
4425
|
+
}
|
|
4426
|
+
lastError = error;
|
|
4427
|
+
if (attempt + 1 < attempts) {
|
|
4428
|
+
await doSleep(backoffMs * 2 ** attempt);
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
const detail = lastError instanceof Error ? lastError.message : String(lastError);
|
|
4433
|
+
throw new Error(
|
|
4434
|
+
`Could not acknowledge the Paigy reply after ${attempts} attempts (${detail}). The answer was not returned to the agent and remains recoverable after the lease expires.`
|
|
4435
|
+
);
|
|
4436
|
+
}
|
|
4437
|
+
async function receiveReply(item, token, opts = {}) {
|
|
4438
|
+
const decoded = decodeReply(item, opts);
|
|
4439
|
+
const delivery = deliveryOf(item);
|
|
4440
|
+
if (!delivery) {
|
|
4441
|
+
if (opts.requireDelivery) {
|
|
4442
|
+
throw new Error("Paigy returned a catch-up reply without a delivery lease; the answer was not exposed.");
|
|
4443
|
+
}
|
|
4444
|
+
return decoded;
|
|
4445
|
+
}
|
|
4446
|
+
await acknowledge(token, delivery.leaseId, {
|
|
4447
|
+
attempts: opts.ackAttempts,
|
|
4448
|
+
backoffMs: opts.ackBackoffMs,
|
|
4449
|
+
doSleep: opts.ackSleep
|
|
4450
|
+
});
|
|
4451
|
+
return decoded;
|
|
4452
|
+
}
|
|
4453
|
+
async function pollAnswer(notificationId, opts = {}) {
|
|
4454
|
+
const token = authToken() ?? "";
|
|
4455
|
+
const res = ensureAuthed2(await reach(
|
|
4456
|
+
`${BACKEND_URL}/api/await?notificationId=${encodeURIComponent(notificationId)}`,
|
|
4457
|
+
{
|
|
4458
|
+
headers: {
|
|
4459
|
+
authorization: `Bearer ${token}`,
|
|
4460
|
+
"x-paigy-await-ack": "1"
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
));
|
|
4464
|
+
if (!res.ok) throw new Error(`await failed: ${res.status} ${await res.text()}`);
|
|
4465
|
+
const wire = await res.json();
|
|
4466
|
+
if (wire.type !== "reply") return wire;
|
|
4467
|
+
return receiveReply(wire, token, opts);
|
|
4468
|
+
}
|
|
4469
|
+
async function awaitReply(notificationId, opts = {}) {
|
|
4470
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
4471
|
+
const windowMs = opts.windowMs ?? AWAIT_WINDOW_MS;
|
|
4472
|
+
const doSleep = opts.sleep ?? sleep2;
|
|
4473
|
+
const now = opts.now ?? Date.now;
|
|
4474
|
+
const start = now();
|
|
4475
|
+
while (true) {
|
|
4476
|
+
if (opts.signal?.aborted) return { type: "idle" };
|
|
4477
|
+
const item = await pollAnswer(notificationId);
|
|
4478
|
+
if (item.type !== "idle") return item;
|
|
4479
|
+
const partial = await pollPartials(notificationId);
|
|
4480
|
+
if (partial) return partial;
|
|
4481
|
+
if (now() - start >= windowMs) return { type: "idle" };
|
|
4482
|
+
await doSleep(intervalMs);
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
async function checkReplies2(opts = {}) {
|
|
4486
|
+
const pending = await checkReplies({ ...opts, replyAck: true });
|
|
4487
|
+
const token = authToken(opts.token) ?? "";
|
|
4488
|
+
const replies = await Promise.all(pending.replies.map(async (reply) => {
|
|
4489
|
+
const received = await receiveReply(
|
|
4490
|
+
{ type: "reply", ...reply },
|
|
4491
|
+
token,
|
|
4492
|
+
{ requireDelivery: true }
|
|
4493
|
+
);
|
|
4494
|
+
const { type: _type, ...withoutType } = received;
|
|
4495
|
+
return withoutType;
|
|
4496
|
+
}));
|
|
4497
|
+
return {
|
|
4498
|
+
...pending,
|
|
4499
|
+
replies
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
function ensureAuthed3(res) {
|
|
4503
|
+
if (res.status === 401) throw new UnpairedError();
|
|
4504
|
+
return res;
|
|
4505
|
+
}
|
|
4506
|
+
async function setTaskState(notificationId, state, opts = {}) {
|
|
4507
|
+
const token = authToken(opts.token) ?? "";
|
|
4508
|
+
const res = ensureAuthed3(await reach(
|
|
4509
|
+
`${BACKEND_URL}/api/notify/${encodeURIComponent(notificationId)}/state`,
|
|
4510
|
+
{
|
|
4511
|
+
method: "PATCH",
|
|
4512
|
+
headers: {
|
|
4513
|
+
"content-type": "application/json",
|
|
4514
|
+
authorization: `Bearer ${token}`,
|
|
4515
|
+
"x-paigy-await-ack": "1"
|
|
4516
|
+
},
|
|
4517
|
+
body: JSON.stringify({ state })
|
|
4518
|
+
}
|
|
4519
|
+
));
|
|
4520
|
+
if (!res.ok) {
|
|
4521
|
+
throw new Error(`set_task_state failed: ${res.status} ${await res.text()}`);
|
|
4522
|
+
}
|
|
4523
|
+
return await res.json();
|
|
4524
|
+
}
|
|
4525
|
+
function overrideToken2(secret) {
|
|
4526
|
+
overrideToken(secret);
|
|
4527
|
+
}
|
|
4296
4528
|
|
|
4297
4529
|
export {
|
|
4298
4530
|
BACKEND_URL,
|
|
@@ -4322,18 +4554,18 @@ export {
|
|
|
4322
4554
|
pairStep,
|
|
4323
4555
|
UnpairedError,
|
|
4324
4556
|
submitNotification,
|
|
4325
|
-
awaitReply,
|
|
4326
4557
|
getThread,
|
|
4327
4558
|
searchThreads,
|
|
4328
|
-
checkReplies,
|
|
4329
4559
|
answerCallerQuestion,
|
|
4330
|
-
overrideToken,
|
|
4331
4560
|
hatch,
|
|
4332
4561
|
whoAmI,
|
|
4333
4562
|
setIdentity,
|
|
4334
4563
|
heartbeat,
|
|
4335
|
-
setTaskState,
|
|
4336
4564
|
registerDelivery,
|
|
4337
4565
|
scheduleCallback,
|
|
4338
|
-
handoff
|
|
4566
|
+
handoff,
|
|
4567
|
+
awaitReply,
|
|
4568
|
+
checkReplies2,
|
|
4569
|
+
setTaskState,
|
|
4570
|
+
overrideToken2
|
|
4339
4571
|
};
|
|
@@ -251,10 +251,10 @@ var IntentSchema = z.object({
|
|
|
251
251
|
// questions included. Found auditing five calls' stored feedback, 2026-08-01.
|
|
252
252
|
kind: z.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
|
|
253
253
|
detail: z.string(),
|
|
254
|
-
/**
|
|
255
|
-
*
|
|
256
|
-
* the
|
|
257
|
-
*
|
|
254
|
+
/** Defer only: seconds until the callback the caller asked for, when something upstream
|
|
255
|
+
* already read the time. Nothing sets it today (#397 documented an MCP parser that was
|
|
256
|
+
* never written) — the API reads the defer's `detail` itself with `notes/when.ts`
|
|
257
|
+
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
258
258
|
dueInSeconds: z.number().int().positive().optional(),
|
|
259
259
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
260
260
|
* already read the utterance, so `feedback_from_call.kind` stops defaulting to
|
|
@@ -362,6 +362,12 @@ var PendingRepliesSchema = z.object({
|
|
|
362
362
|
pending: z.array(
|
|
363
363
|
z.object({ parentId: z.string(), notificationId: z.string(), createdAt: z.string() })
|
|
364
364
|
),
|
|
365
|
+
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
366
|
+
* sees for this session's identity. From inside a session there was no way to find out —
|
|
367
|
+
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
368
|
+
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
369
|
+
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
370
|
+
you: z.object({ name: z.string(), device: z.string().nullable(), tokenId: z.string() }).optional(),
|
|
365
371
|
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
366
372
|
* contact on the same parentId. Keeps reappearing until you call
|
|
367
373
|
* set_task_state on its notificationId. */
|
|
@@ -373,7 +379,13 @@ var PendingRepliesSchema = z.object({
|
|
|
373
379
|
createdAt: z.string(),
|
|
374
380
|
/** The user seeded this request with a past conversation — call get_thread on it
|
|
375
381
|
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
376
|
-
contextParentId: z.string().optional()
|
|
382
|
+
contextParentId: z.string().optional(),
|
|
383
|
+
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
384
|
+
* the account — the name here — which has not been seen since it landed, so nobody
|
|
385
|
+
* came for it. Handed to you because you are the session that is here. Take it like
|
|
386
|
+
* any request (set_task_state claims it, reply with contact on its parentId), and say
|
|
387
|
+
* whose it was, because the user chose that agent on purpose. */
|
|
388
|
+
stranded: z.string().optional()
|
|
377
389
|
})
|
|
378
390
|
),
|
|
379
391
|
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
@@ -684,7 +696,13 @@ var BrokerTuningSchema = z.object({
|
|
|
684
696
|
* uninterpretable, 'high' = whenever not fully certain. */
|
|
685
697
|
clarifyEagerness: z.enum(["low", "normal", "high"]).optional(),
|
|
686
698
|
/** The user's own shorthand: when they say `say`, they mean `mean`. */
|
|
687
|
-
phrasebook: z.array(z.object({ say: z.string().min(1).max(60), mean: z.string().min(1).max(120) })).max(24).optional()
|
|
699
|
+
phrasebook: z.array(z.object({ say: z.string().min(1).max(60), mean: z.string().min(1).max(120) })).max(24).optional(),
|
|
700
|
+
/** The language calls are PLANNED in, when the account has chosen one (#1272). Absent —
|
|
701
|
+
* which is every account today — means the agent's own words decide, per ask: a call
|
|
702
|
+
* about an English ask opens in English. This is the only thing that overrides that,
|
|
703
|
+
* and a live caller who switches language mid-call still outranks it (broker/lang.ts).
|
|
704
|
+
* Set per user (no UI yet), like `voiceTuning`. */
|
|
705
|
+
language: z.enum(["en", "es"]).optional()
|
|
688
706
|
});
|
|
689
707
|
var UserSettingsSchema = z.object({
|
|
690
708
|
permissions: z.object({
|
|
@@ -892,7 +910,9 @@ var NoteSchema = z.object({
|
|
|
892
910
|
/** The request thread minted at assignment; null until assigned. */
|
|
893
911
|
parentId: z.string().nullable(),
|
|
894
912
|
/** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
895
|
-
* call — never a deadline
|
|
913
|
+
* call — never a deadline. It only ever comes from the user's own words, so when it
|
|
914
|
+
* passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
|
|
915
|
+
* a ring at that time); after that it rides like any other. Null = "the very next
|
|
896
916
|
* call", the right reading of "remind me to…" with no time attached. */
|
|
897
917
|
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
898
918
|
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
@@ -935,6 +955,59 @@ var AssignNoteSchema = z.object({
|
|
|
935
955
|
workspace: z.string().min(1)
|
|
936
956
|
}).optional()
|
|
937
957
|
}).refine((a) => !!a.target !== !!a.spawn, { message: "exactly one of target or spawn" });
|
|
958
|
+
var TriageVerdictSchema = z.enum(["keep", "stale", "close", "assign"]);
|
|
959
|
+
var TriageItemSchema = z.object({
|
|
960
|
+
noteId: z.string(),
|
|
961
|
+
/** The note's headline at run time. */
|
|
962
|
+
title: z.string(),
|
|
963
|
+
/** WHY, in one short human line, evidence first — this is read on a phone underneath
|
|
964
|
+
* the note's title: "no movement in 34 days", "worked 3 notes in this repo this week".
|
|
965
|
+
* Never a model's reasoning transcript, never an id. */
|
|
966
|
+
why: z.string()
|
|
967
|
+
});
|
|
968
|
+
var TriageAssignmentSchema = z.object({
|
|
969
|
+
/** The agent's token id — what `dispatchNote` resolves and what a request is addressed to. */
|
|
970
|
+
agent: z.string(),
|
|
971
|
+
/** Its display name at run time (the name on the hatchling's card). Denormalized for the
|
|
972
|
+
* same reason as `title`: the card must render from the proposal alone. */
|
|
973
|
+
agentName: z.string(),
|
|
974
|
+
notes: z.array(TriageItemSchema)
|
|
975
|
+
});
|
|
976
|
+
var TriageStatusSchema = z.enum(["open", "superseded", "dismissed"]);
|
|
977
|
+
var SubmitTriageSchema = z.object({
|
|
978
|
+
/** Which runtime judged: "ollama" (inference never left the machine) or a harness the
|
|
979
|
+
* user already runs under their own credentials ("claude" / "codex" / "agy"). Recorded
|
|
980
|
+
* so the phone can say where the content went — an unattributed privacy claim is worth
|
|
981
|
+
* nothing, and #1106's promise is precisely "Paigy's servers never see this". */
|
|
982
|
+
provider: z.string().min(1).max(60),
|
|
983
|
+
/** The concrete model when the provider names one (an ollama tag); null otherwise. */
|
|
984
|
+
model: z.string().max(200).nullable().optional(),
|
|
985
|
+
/** How many open notes the run actually looked at — the denominator on the phone
|
|
986
|
+
* ("6 of 50"), and the honest answer to "did it read the whole queue?". */
|
|
987
|
+
reviewed: z.number().int().min(0).max(1e4).default(0),
|
|
988
|
+
close: z.array(TriageItemSchema).max(200).default([]),
|
|
989
|
+
stale: z.array(TriageItemSchema).max(200).default([]),
|
|
990
|
+
assign: z.array(TriageAssignmentSchema).max(50).default([])
|
|
991
|
+
});
|
|
992
|
+
var TriageProposalSchema = SubmitTriageSchema.extend({
|
|
993
|
+
id: z.string(),
|
|
994
|
+
runAt: z.string(),
|
|
995
|
+
status: TriageStatusSchema,
|
|
996
|
+
model: z.string().nullable().default(null)
|
|
997
|
+
});
|
|
998
|
+
var AcceptTriageSchema = z.discriminatedUnion("group", [
|
|
999
|
+
z.object({ group: z.literal("close"), noteIds: z.array(z.string()).max(200).optional() }),
|
|
1000
|
+
z.object({ group: z.literal("stale"), noteIds: z.array(z.string()).max(200).optional() }),
|
|
1001
|
+
z.object({
|
|
1002
|
+
group: z.literal("assign"),
|
|
1003
|
+
agent: z.string().min(1),
|
|
1004
|
+
noteIds: z.array(z.string()).max(200).optional()
|
|
1005
|
+
})
|
|
1006
|
+
]);
|
|
1007
|
+
var AcceptTriageResultSchema = z.object({
|
|
1008
|
+
accepted: z.array(z.string()),
|
|
1009
|
+
failed: z.array(z.object({ noteId: z.string(), reason: z.string() }))
|
|
1010
|
+
});
|
|
938
1011
|
var DeliveryModeSchema = z.enum(["poll", "self_hosted"]);
|
|
939
1012
|
var WAKE_EVENT = "wake";
|
|
940
1013
|
var wakeChannel = (tokenId) => `wake:${tokenId}`;
|
package/dist/enable.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HandoffSchema,
|
|
4
4
|
MISSED_CALL_PLAN
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YOGAG4YY.js";
|
|
6
6
|
import {
|
|
7
7
|
ENABLE_COMMAND,
|
|
8
8
|
PAIGY_TOOL_IDS,
|
|
9
9
|
autoConfigureClients,
|
|
10
10
|
claudeInstallHint,
|
|
11
11
|
paigyToolsAllowlisted
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KNMI3CJ3.js";
|
|
13
13
|
import {
|
|
14
14
|
clearSurface,
|
|
15
15
|
writeSurface
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
UnpairedError,
|
|
23
23
|
answerCallerQuestion,
|
|
24
24
|
awaitReply,
|
|
25
|
-
|
|
25
|
+
checkReplies2,
|
|
26
26
|
deleteKeyFile,
|
|
27
27
|
deleteToken,
|
|
28
28
|
fetchCredential,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
lintNotify,
|
|
35
35
|
listSlots,
|
|
36
36
|
normalizeSpeech,
|
|
37
|
-
|
|
37
|
+
overrideToken2,
|
|
38
38
|
pairStep,
|
|
39
39
|
readKeyFile,
|
|
40
40
|
readToken,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
startE2ee,
|
|
52
52
|
submitNotification,
|
|
53
53
|
whoAmI
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-U6SLIORK.js";
|
|
55
55
|
|
|
56
56
|
// src/index.ts
|
|
57
57
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -404,7 +404,7 @@ function prependNote(result, note) {
|
|
|
404
404
|
}
|
|
405
405
|
async function hatchUnderDevice(name, voice) {
|
|
406
406
|
if (!listSlots().includes("Desktop")) return null;
|
|
407
|
-
|
|
407
|
+
overrideToken2(readToken("Desktop"));
|
|
408
408
|
try {
|
|
409
409
|
const minted = await hatch(name ?? suggestedAgentName() ?? "Agent", voice ?? null);
|
|
410
410
|
const dt = { ok: true, access_token: minted.token, name: minted.name, device: null };
|
|
@@ -413,7 +413,7 @@ async function hatchUnderDevice(name, voice) {
|
|
|
413
413
|
} catch {
|
|
414
414
|
return null;
|
|
415
415
|
} finally {
|
|
416
|
-
|
|
416
|
+
overrideToken2(null);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
async function runPair(device_code, name, voice, note) {
|
|
@@ -468,7 +468,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
468
468
|
},
|
|
469
469
|
{
|
|
470
470
|
name: "check_replies",
|
|
471
|
-
description: "The catch-up sweep for everything outstanding \u2014 a PURE read, takes no arguments, safe to call as often as you like: nothing here is consumed by reading it. Returns `replies` (answers to notifications you sent), your still-pending notifications, and `requests` \u2014 requests the user started toward you (each { notificationId, parentId, text }). Each keeps reappearing on every call until you actually engage with it: call set_task_state on its notificationId, which is what claims/acknowledges it \u2014 a human-initiated reply or request must never be silently dropped just because you read the list without acting. Also returns `threads` \u2014 the SAME replies + requests grouped by conversation, oldest thread first, each with a `busy` flag and its `items` in arrival order. WORK ONE THREAD AT A TIME: take the oldest thread whose `busy` is false, handle ALL of its items together in a single turn (one set_task_state), then go to the next \u2014 don't interleave threads item-by-item. A `busy` thread already has a turn in progress; leave it and let its new items ride the next turn. Use check_replies when booting up / starting a session, or when you've been waiting a long time on something else. To wait on an answer to a contact call you just made, use await_reply instead. Also returns owedCallbacks: callbacks now due that you promised \u2014 fulfill each with contact on its parentId. EVERY Paigy reply \u2014 this one, await_reply's, and contact's \u2014 may carry `also`: work assigned to you that no wake could reach, handed to you because you happened to be here. It is NOT what you asked about and it is never urgent: FINISH what you came for first, then take it up. Each entry has a `noteId` and the owner's own words; report on its `parentId` thread when it has one, and call set_task_state on that thread as you would for any assigned work. Ignoring it costs nothing \u2014 it rides your next reply too. Also returns `stalled`: work (either direction) you reported in_progress via set_task_state a while ago and never reported completed \u2014 likely left half-done by this session or a prior one that crashed or went idle. For each, either continue the work and report a real state, or investigate why it stalled. Replies may carry `intents`/`transcript`/`covered` (call-mapped answers) \u2014 handle intents exactly as await_reply's description says (defer \u2192 schedule_callback now; delegate \u2192 decide and say so; channel \u2192 honor next contact), and treat a `covered` list missing one of your declared points as that part still unanswered.",
|
|
471
|
+
description: "The catch-up sweep for everything outstanding \u2014 a PURE read, takes no arguments, safe to call as often as you like: nothing here is consumed by reading it. Returns `replies` (answers to notifications you sent), your still-pending notifications, and `requests` \u2014 requests the user started toward you (each { notificationId, parentId, text }). Each keeps reappearing on every call until you actually engage with it: call set_task_state on its notificationId, which is what claims/acknowledges it \u2014 a human-initiated reply or request must never be silently dropped just because you read the list without acting. Also returns `threads` \u2014 the SAME replies + requests grouped by conversation, oldest thread first, each with a `busy` flag and its `items` in arrival order. WORK ONE THREAD AT A TIME: take the oldest thread whose `busy` is false, handle ALL of its items together in a single turn (one set_task_state), then go to the next \u2014 don't interleave threads item-by-item. A `busy` thread already has a turn in progress; leave it and let its new items ride the next turn. Use check_replies when booting up / starting a session, or when you've been waiting a long time on something else. To wait on an answer to a contact call you just made, use await_reply instead. Also returns owedCallbacks: callbacks now due that you promised \u2014 fulfill each with contact on its parentId. EVERY Paigy reply \u2014 this one, await_reply's, and contact's \u2014 may carry `also`: work assigned to you that no wake could reach, handed to you because you happened to be here. It is NOT what you asked about and it is never urgent: FINISH what you came for first, then take it up. Each entry has a `noteId` and the owner's own words; report on its `parentId` thread when it has one, and call set_task_state on that thread as you would for any assigned work. Ignoring it costs nothing \u2014 it rides your next reply too. Also returns `stalled`: work (either direction) you reported in_progress via set_task_state a while ago and never reported completed \u2014 likely left half-done by this session or a prior one that crashed or went idle. For each, either continue the work and report a real state, or investigate why it stalled. Also returns `you` \u2014 WHICH IDENTITY you are speaking as ({ name, device, tokenId }), the same name and device the user sees on their Agents screen. This is the only safe way to find out (calling `pair` can MINT a new identity instead of telling you about the current one). Use it when the user asks who you are, and to tell whether work addressed to a name is addressed to you. A request may also carry `stranded`: it was addressed to ANOTHER agent on this account (that name) which has not been seen since it landed, so nobody came for it and it is handed to you because you are the session that is here. Take it exactly like your own \u2014 set_task_state claims it, reply with contact on its parentId \u2014 and say whose it was, because the user picked that agent on purpose. Replies may carry `intents`/`transcript`/`covered` (call-mapped answers) \u2014 handle intents exactly as await_reply's description says (defer \u2192 schedule_callback now; delegate \u2192 decide and say so; channel \u2192 honor next contact), and treat a `covered` list missing one of your declared points as that part still unanswered.",
|
|
472
472
|
inputSchema: json(z.object({}))
|
|
473
473
|
},
|
|
474
474
|
{
|
|
@@ -658,7 +658,7 @@ async function handleTool(request, signal) {
|
|
|
658
658
|
return { content: [{ type: "text", text: JSON.stringify(item) }] };
|
|
659
659
|
}
|
|
660
660
|
case "check_replies": {
|
|
661
|
-
const result = await
|
|
661
|
+
const result = await checkReplies2();
|
|
662
662
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
663
663
|
}
|
|
664
664
|
case "get_thread": {
|
package/dist/listen.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
WAKE_EVENT,
|
|
4
4
|
wakeChannel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YOGAG4YY.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
checkReplies2,
|
|
8
8
|
registerDelivery
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-U6SLIORK.js";
|
|
10
10
|
|
|
11
11
|
// src/listen.ts
|
|
12
12
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -170,7 +170,7 @@ function launchEnv(work, reason) {
|
|
|
170
170
|
}
|
|
171
171
|
async function sweep(reason) {
|
|
172
172
|
try {
|
|
173
|
-
const work = await
|
|
173
|
+
const work = await checkReplies2();
|
|
174
174
|
emit({ type: "sweep", reason, ...work });
|
|
175
175
|
const cmd = process.env.PAIGY_ON_WAKE;
|
|
176
176
|
const inbound = work.replies.length > 0 || work.requests.length > 0;
|
package/dist/onboard.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
autoConfigureClients,
|
|
4
4
|
claudeInstallHint,
|
|
5
5
|
openBrowser
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KNMI3CJ3.js";
|
|
7
7
|
import {
|
|
8
8
|
AGENT_NAME,
|
|
9
9
|
TOKEN_PATH,
|
|
10
10
|
hatch,
|
|
11
11
|
listSlots,
|
|
12
|
-
|
|
12
|
+
overrideToken2,
|
|
13
13
|
pollToken,
|
|
14
14
|
readToken,
|
|
15
15
|
requestCode,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setIdentity,
|
|
18
18
|
sleep,
|
|
19
19
|
whoAmI
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-U6SLIORK.js";
|
|
21
21
|
|
|
22
22
|
// src/onboard.ts
|
|
23
23
|
async function main() {
|
|
@@ -50,7 +50,7 @@ Paigy MCP onboarding (agent: "${AGENT_NAME}")
|
|
|
50
50
|
console.log("The saved token is no longer valid \u2014 re-pairing.\n");
|
|
51
51
|
}
|
|
52
52
|
if (listSlots().includes("Desktop")) {
|
|
53
|
-
|
|
53
|
+
overrideToken2(readToken("Desktop"));
|
|
54
54
|
try {
|
|
55
55
|
const minted = await hatch(process.env.PAIGY_NAME ?? "Agent", null);
|
|
56
56
|
saveToken({ ok: true, access_token: minted.token, name: minted.name, device: null });
|
|
@@ -67,7 +67,7 @@ Paigy MCP onboarding (agent: "${AGENT_NAME}")
|
|
|
67
67
|
} catch {
|
|
68
68
|
console.log("(This machine's saved credential didn't work \u2014 falling back to a code.)\n");
|
|
69
69
|
} finally {
|
|
70
|
-
|
|
70
|
+
overrideToken2(null);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
const code = await requestCode();
|
package/dist/statusline.js
CHANGED