@paigy/harness 0.3.10 → 0.3.12
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/agy-acp.js +144 -0
- package/dist/cli.js +170 -93
- package/dist/main.js +164 -90
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -125,6 +125,8 @@ var init_catalog = __esm({
|
|
|
125
125
|
name: "agy",
|
|
126
126
|
label: "Antigravity",
|
|
127
127
|
cli: "agy",
|
|
128
|
+
// `agy` has no ACP mode; its adapter ships inside the harness (`harness/agy-acp.ts`).
|
|
129
|
+
adapter: "paigy-agy-acp",
|
|
128
130
|
authProbe: ["agy", "help"],
|
|
129
131
|
loginHint: "Install and set up Antigravity (`agy`).",
|
|
130
132
|
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
@@ -22757,11 +22759,17 @@ function sessionId() {
|
|
|
22757
22759
|
return SESSION_ID;
|
|
22758
22760
|
}
|
|
22759
22761
|
async function reach(url, init) {
|
|
22762
|
+
return send(url, init, SESSION_ID);
|
|
22763
|
+
}
|
|
22764
|
+
function reachAs(session) {
|
|
22765
|
+
return (url, init) => send(url, init, session);
|
|
22766
|
+
}
|
|
22767
|
+
async function send(url, init, session) {
|
|
22760
22768
|
try {
|
|
22761
22769
|
const headers = {
|
|
22762
22770
|
...init?.headers,
|
|
22763
22771
|
"x-paigy-instance": INSTANCE_ID,
|
|
22764
|
-
"x-paigy-session":
|
|
22772
|
+
"x-paigy-session": session
|
|
22765
22773
|
};
|
|
22766
22774
|
return await fetch(url, { ...init, headers, dispatcher: await proxy() });
|
|
22767
22775
|
} catch (e) {
|
|
@@ -23893,7 +23901,7 @@ async function listNotes(opts = {}) {
|
|
|
23893
23901
|
}
|
|
23894
23902
|
async function listConnections(opts = {}) {
|
|
23895
23903
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
|
|
23896
|
-
headers: { authorization: `Bearer ${authToken(opts.token)}
|
|
23904
|
+
headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
|
|
23897
23905
|
}));
|
|
23898
23906
|
if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
|
|
23899
23907
|
return await res.json();
|
|
@@ -23919,14 +23927,14 @@ async function acceptTriage(proposalId, body, opts = {}) {
|
|
|
23919
23927
|
async function contact(input, opts = {}) {
|
|
23920
23928
|
const parsed = ContactSchema.parse(input);
|
|
23921
23929
|
opts.signal?.throwIfAborted();
|
|
23922
|
-
const
|
|
23930
|
+
const send22 = opts.reach ?? reach;
|
|
23923
23931
|
const headers = { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}`, "x-paigy-model": "goal-entry-v1" };
|
|
23924
23932
|
let deliveryId;
|
|
23925
23933
|
if ("deliveryId" in parsed) deliveryId = parsed.deliveryId;
|
|
23926
23934
|
else {
|
|
23927
23935
|
if (!opts.reach && readKeyFile()?.e2ee) throw new Error("Goal contact does not support E2EE yet; refusing to send plaintext.");
|
|
23928
23936
|
const shaped = deriveAsk(NotifyRequestSchema.parse({ ask: parsed.ask, waiting: parsed.waiting, options: parsed.options }));
|
|
23929
|
-
const res = ensureAuthed(await
|
|
23937
|
+
const res = ensureAuthed(await send22(`${BACKEND_URL}/api/goals/${parsed.goalIds[0]}/contact`, {
|
|
23930
23938
|
method: "POST",
|
|
23931
23939
|
headers,
|
|
23932
23940
|
signal: opts.signal,
|
|
@@ -23938,11 +23946,9 @@ async function contact(input, opts = {}) {
|
|
|
23938
23946
|
if (!deliveryId) throw new Error("Goal contact returned no Delivery identity");
|
|
23939
23947
|
}
|
|
23940
23948
|
const read = async (signal2) => {
|
|
23941
|
-
const res = ensureAuthed(await
|
|
23949
|
+
const res = ensureAuthed(await send22(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
23942
23950
|
if (!res.ok) await fail("read_delivery", res);
|
|
23943
|
-
|
|
23944
|
-
return delivery.kind === "notification" ? { ...delivery, message: `${delivery.message}
|
|
23945
|
-
Inbox delivery is asynchronous; use claim_goal/get_goal to collect durable answers. Do not poll this Notification.` } : delivery;
|
|
23951
|
+
return await res.json();
|
|
23946
23952
|
};
|
|
23947
23953
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
23948
23954
|
if (opts.waits === false) return read(opts.signal);
|
|
@@ -24062,7 +24068,7 @@ async function subscribeWake(onNudge, opts = {}) {
|
|
|
24062
24068
|
}
|
|
24063
24069
|
};
|
|
24064
24070
|
}
|
|
24065
|
-
var import_undici, require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __copyProps2, __toESM2, require_nacl_fast, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, SESSION_ID, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, GetThreadSchema, GET_THREAD_DESCRIPTION, SearchThreadsSchema, SEARCH_THREADS_DESCRIPTION, AGENT_TOOLS, AGENT_TOOL_NAMES, ContextSchema, ParticipantSchema, TransformSchema, OptionSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, BrokerTuningSchema, UserSettingsSchema, HistoryItemSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueItemSchema, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema,
|
|
24071
|
+
var import_undici, require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __copyProps2, __toESM2, require_nacl_fast, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, SESSION_ID, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, GetThreadSchema, GET_THREAD_DESCRIPTION, SearchThreadsSchema, SEARCH_THREADS_DESCRIPTION, AGENT_TOOLS, AGENT_TOOL_NAMES, LIVE_MS, ContextSchema, ParticipantSchema, TransformSchema, OptionSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, BrokerTuningSchema, UserSettingsSchema, HistoryItemSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueItemSchema, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, EnvelopeRecipientSchema, EnvelopeHeaderSchema, EnvelopeSchema, SealedAnswerSchema, DeviceCredentialSchema, DeviceRosterSchema, WakeNudgeSchema, PairingStatusSchema, PairingRevealSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, FeedbackResolutionSchema, FeedbackOutcomeSchema, import_tweetnacl, import_tweetnacl2, import_tweetnacl3, import_tweetnacl4, import_tweetnacl5, AGENT_NAME, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, ApiError, AWAIT_WINDOW_MS, tokenOverride, authToken, HEARTBEAT_MS, RETRY_MS, JOIN, lines;
|
|
24066
24072
|
var init_dist = __esm({
|
|
24067
24073
|
"../../packages/sdk/dist/index.js"() {
|
|
24068
24074
|
"use strict";
|
|
@@ -26735,6 +26741,9 @@ var init_dist = __esm({
|
|
|
26735
26741
|
CONTACT_DESCRIPTION = "Contact the user about exactly one existing Goal: pass goalIds:[goalId], ask, channel:'notification'|'call', and waiting:'none'|'hard'. Options supply choices. Notification returns immediately; collect durable answers with claim_goal/get_goal. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Continuation sends nothing and rereads the same durable evidence, including previously read answers. Entries retain authorship and provenance; accepted decisions are separate from quoted speech. Call state open does not mean ringing. Unsupported: soft waiting, multiple Goals/questions, outcome admission, and re-presentation of an existing request. Create a Goal explicitly first; never resend a pending ask to continue waiting.";
|
|
26736
26742
|
CreateGoalSchema = external_exports.object({
|
|
26737
26743
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
26744
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
26745
|
+
* rings"). Omit it and the brain writes one at admission from the outcome. */
|
|
26746
|
+
title: external_exports.string().trim().min(1).max(80).optional(),
|
|
26738
26747
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
26739
26748
|
idempotencyKey: external_exports.string().trim().min(1).max(200),
|
|
26740
26749
|
/** A past conversation this Goal should be read against — History's "new session from this"
|
|
@@ -26754,11 +26763,14 @@ var init_dist = __esm({
|
|
|
26754
26763
|
CreateGoalToolSchema = CreateGoalSchema.extend({
|
|
26755
26764
|
idempotencyKey: CreateGoalSchema.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
26756
26765
|
}).strict();
|
|
26757
|
-
CREATE_GOAL_DESCRIPTION =
|
|
26766
|
+
CREATE_GOAL_DESCRIPTION = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only: the owner must claim it before doing work, then update it as it advances. Returns an admission receipt with goalId, current state, revision, ownerParticipant, and the next step; no Goal content or execution lease.';
|
|
26758
26767
|
UpdateGoalSchema = external_exports.object({
|
|
26759
26768
|
revision: external_exports.number().int().positive(),
|
|
26760
26769
|
changes: external_exports.object({
|
|
26761
26770
|
outcome: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
26771
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud. The
|
|
26772
|
+
* brain writes one at admission; this is the owner saying it better. Null clears it. */
|
|
26773
|
+
title: external_exports.string().trim().min(1).max(80).nullable().optional(),
|
|
26762
26774
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
26763
26775
|
parentGoalId: external_exports.string().uuid().nullable().optional(),
|
|
26764
26776
|
dependencies: external_exports.array(external_exports.object({ goalId: external_exports.string().uuid(), gate: external_exports.enum(["start", "finish"]) }).strict()).optional(),
|
|
@@ -26774,10 +26786,10 @@ var init_dist = __esm({
|
|
|
26774
26786
|
UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
|
|
26775
26787
|
ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
26776
26788
|
GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
26777
|
-
GET_GOAL_DESCRIPTION = "Read
|
|
26778
|
-
UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. Returns the
|
|
26779
|
-
CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns
|
|
26780
|
-
CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals,
|
|
26789
|
+
GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
|
|
26790
|
+
UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. Returns the Goal as get_goal reads it, at its new revision.";
|
|
26791
|
+
CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
26792
|
+
CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
|
|
26781
26793
|
CheckRepliesSchema = external_exports.object({}).strict();
|
|
26782
26794
|
GetThreadSchema = external_exports.object({
|
|
26783
26795
|
parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
|
|
@@ -26798,6 +26810,7 @@ var init_dist = __esm({
|
|
|
26798
26810
|
{ name: "update_goal", description: UPDATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(UpdateGoalToolSchema) }
|
|
26799
26811
|
];
|
|
26800
26812
|
AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
26813
|
+
LIVE_MS = 3 * 6e4;
|
|
26801
26814
|
ContextSchema = external_exports.object({
|
|
26802
26815
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
26803
26816
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -27579,7 +27592,19 @@ var init_dist = __esm({
|
|
|
27579
27592
|
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
27580
27593
|
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
27581
27594
|
* for a settled question whose reply carried nothing readable. */
|
|
27582
|
-
answer: external_exports.string().nullable().default(null)
|
|
27595
|
+
answer: external_exports.string().nullable().default(null),
|
|
27596
|
+
/** The Goal this question belongs to — a step knows its Goal on its own, not only through
|
|
27597
|
+
* an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
|
|
27598
|
+
* READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
|
|
27599
|
+
* order, its route, home's trees and the list of steps all take a step's Goal from here, so
|
|
27600
|
+
* this is the field they agree through rather than each re-deriving it from the row it
|
|
27601
|
+
* arrived under. Required because the API projects it on every need it sends. */
|
|
27602
|
+
goalId: external_exports.string(),
|
|
27603
|
+
/** True only while an unmet START gate holds the Goal — a Goal that merely waits to
|
|
27604
|
+
* *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
|
|
27605
|
+
* the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
|
|
27606
|
+
* gate at all. */
|
|
27607
|
+
blocked: external_exports.boolean().default(false)
|
|
27583
27608
|
});
|
|
27584
27609
|
QueueItemSchema = external_exports.object({
|
|
27585
27610
|
id: external_exports.string(),
|
|
@@ -27718,25 +27743,6 @@ var init_dist = __esm({
|
|
|
27718
27743
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
27719
27744
|
realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
|
|
27720
27745
|
});
|
|
27721
|
-
StatusSchema = external_exports.object({
|
|
27722
|
-
name: external_exports.string(),
|
|
27723
|
-
sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
|
|
27724
|
-
/** A phone is registered for push/ring (any push token on the account). */
|
|
27725
|
-
phone: external_exports.boolean(),
|
|
27726
|
-
/** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
|
|
27727
|
-
* it never picked up. THE SAME NUMBER the harness's wake gate reads
|
|
27728
|
-
* (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
|
|
27729
|
-
* zero while the sweep sees one is two ideas of "waiting".
|
|
27730
|
-
*
|
|
27731
|
-
* Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
|
|
27732
|
-
* that nobody spawned, so a terminal session only learns of work by asking. The harness
|
|
27733
|
-
* used to paper over that by spawning a SECOND process on the identity; now it stands
|
|
27734
|
-
* back, correctly, and the person sitting at the terminal is the one who can act. A
|
|
27735
|
-
* coffee-beans request sat unread for three days.
|
|
27736
|
-
*
|
|
27737
|
-
* Optional: an older API sends no field, and the statusline then renders exactly as before. */
|
|
27738
|
-
waiting: external_exports.number().int().nonnegative().optional()
|
|
27739
|
-
});
|
|
27740
27746
|
EnvelopeRecipientSchema = external_exports.object({
|
|
27741
27747
|
keyId: external_exports.string(),
|
|
27742
27748
|
epk: external_exports.string(),
|
|
@@ -32475,7 +32481,7 @@ var require_lib = __commonJS({
|
|
|
32475
32481
|
|
|
32476
32482
|
// src/triage/judge.ts
|
|
32477
32483
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
32478
|
-
import { homedir as
|
|
32484
|
+
import { homedir as homedir7 } from "os";
|
|
32479
32485
|
import { delimiter as delimiter3 } from "path";
|
|
32480
32486
|
function pickOllamaModel(tags) {
|
|
32481
32487
|
if (tags.length === 0) return null;
|
|
@@ -32558,7 +32564,7 @@ async function judgeFor(asked, deps = {}) {
|
|
|
32558
32564
|
const out = spawnSync2(bin, runner.argv(null), {
|
|
32559
32565
|
input: prompt,
|
|
32560
32566
|
encoding: "utf8",
|
|
32561
|
-
cwd:
|
|
32567
|
+
cwd: homedir7(),
|
|
32562
32568
|
timeout: RUN_TIMEOUT_MS,
|
|
32563
32569
|
maxBuffer: 32 * 1024 * 1024,
|
|
32564
32570
|
env: { ...process.env, PATH: [process.env["PATH"], ...probeDirs()].filter(Boolean).join(delimiter3) }
|
|
@@ -33148,8 +33154,11 @@ var import_qrcode = __toESM(require_lib(), 1);
|
|
|
33148
33154
|
import { hostname } from "os";
|
|
33149
33155
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
33150
33156
|
import { execFile as execFile2, execSync } from "child_process";
|
|
33151
|
-
import { homedir as
|
|
33152
|
-
import { join as
|
|
33157
|
+
import { homedir as homedir8 } from "os";
|
|
33158
|
+
import { join as join7 } from "path";
|
|
33159
|
+
|
|
33160
|
+
// src/run.ts
|
|
33161
|
+
init_dist();
|
|
33153
33162
|
|
|
33154
33163
|
// src/harness/session.ts
|
|
33155
33164
|
import { spawn } from "child_process";
|
|
@@ -33164,15 +33173,19 @@ function optionFor(options, decision) {
|
|
|
33164
33173
|
return options.find((o) => o.kind === want)?.optionId ?? null;
|
|
33165
33174
|
}
|
|
33166
33175
|
function createAcpDriver(opts) {
|
|
33167
|
-
return new AcpDriver(opts.cwd, opts.mode);
|
|
33176
|
+
return new AcpDriver(opts.cwd, opts.mode, opts.resume);
|
|
33168
33177
|
}
|
|
33169
33178
|
var AcpDriver = class {
|
|
33170
|
-
|
|
33179
|
+
/** `resume`: the id of an existing session to continue rather than open a new one (#1637) —
|
|
33180
|
+
* the session a bound identity belongs to, so the process this drives IS its holder. */
|
|
33181
|
+
constructor(cwd, mode, resume) {
|
|
33171
33182
|
this.cwd = cwd;
|
|
33172
33183
|
this.mode = mode;
|
|
33184
|
+
this.resume = resume;
|
|
33173
33185
|
}
|
|
33174
33186
|
cwd;
|
|
33175
33187
|
mode;
|
|
33188
|
+
resume;
|
|
33176
33189
|
nextId = 1;
|
|
33177
33190
|
initId;
|
|
33178
33191
|
sessionNewId;
|
|
@@ -33256,15 +33269,15 @@ var AcpDriver = class {
|
|
|
33256
33269
|
this.sessionNewId = this.nextId++;
|
|
33257
33270
|
return {
|
|
33258
33271
|
events: [],
|
|
33259
|
-
writes: [frame2({ id: this.sessionNewId, method: "session/new", params: { cwd: this.cwd, mcpServers: [] } })]
|
|
33272
|
+
writes: [frame2(this.resume ? { id: this.sessionNewId, method: "session/resume", params: { sessionId: this.resume, cwd: this.cwd, mcpServers: [] } } : { id: this.sessionNewId, method: "session/new", params: { cwd: this.cwd, mcpServers: [] } })]
|
|
33260
33273
|
};
|
|
33261
33274
|
}
|
|
33262
33275
|
if (msg.id === this.sessionNewId) {
|
|
33263
33276
|
this.sessionNewId = void 0;
|
|
33264
|
-
const sessionId2 = msg.result?.sessionId;
|
|
33277
|
+
const sessionId2 = msg.error ? void 0 : msg.result?.sessionId ?? this.resume;
|
|
33265
33278
|
if (!sessionId2) {
|
|
33266
33279
|
return {
|
|
33267
|
-
events: [{ kind: "error", message:
|
|
33280
|
+
events: [{ kind: "error", message: `${this.resume ? "session/resume" : "session/new"} failed: ${msg.error?.message ?? "no sessionId"}` }],
|
|
33268
33281
|
writes: []
|
|
33269
33282
|
};
|
|
33270
33283
|
}
|
|
@@ -33379,7 +33392,7 @@ init_catalog();
|
|
|
33379
33392
|
var ADAPTER_BIN = {
|
|
33380
33393
|
claude: "claude-agent-acp",
|
|
33381
33394
|
codex: "codex-acp",
|
|
33382
|
-
agy: "agy"
|
|
33395
|
+
agy: "paigy-agy-acp"
|
|
33383
33396
|
};
|
|
33384
33397
|
function splitLines(buffer, chunk) {
|
|
33385
33398
|
const combined = buffer + chunk;
|
|
@@ -33401,7 +33414,8 @@ function startSession(opts) {
|
|
|
33401
33414
|
env: {
|
|
33402
33415
|
...process.env,
|
|
33403
33416
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter2),
|
|
33404
|
-
...opts.token ? { PAIGY_TOKEN: opts.token } : {}
|
|
33417
|
+
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
33418
|
+
...opts.resume ? { PAIGY_SESSION_ID: opts.resume } : {}
|
|
33405
33419
|
},
|
|
33406
33420
|
stdio: ["pipe", "pipe", "pipe"]
|
|
33407
33421
|
});
|
|
@@ -33421,7 +33435,7 @@ function startSession(opts) {
|
|
|
33421
33435
|
opts.onEvent({ kind: "error", message: e.message });
|
|
33422
33436
|
opts.onExit?.();
|
|
33423
33437
|
});
|
|
33424
|
-
const driver = createAcpDriver({ cwd, mode: opts.mode });
|
|
33438
|
+
const driver = createAcpDriver({ cwd, mode: opts.mode, ...opts.resume ? { resume: opts.resume } : {} });
|
|
33425
33439
|
let buffer = "";
|
|
33426
33440
|
child.stdout?.on("data", (chunk) => {
|
|
33427
33441
|
const { lines: lines2, rest } = splitLines(buffer, String(chunk));
|
|
@@ -34803,6 +34817,9 @@ var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
|
34803
34817
|
var CONTACT_DESCRIPTION2 = "Contact the user about exactly one existing Goal: pass goalIds:[goalId], ask, channel:'notification'|'call', and waiting:'none'|'hard'. Options supply choices. Notification returns immediately; collect durable answers with claim_goal/get_goal. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. Continuation sends nothing and rereads the same durable evidence, including previously read answers. Entries retain authorship and provenance; accepted decisions are separate from quoted speech. Call state open does not mean ringing. Unsupported: soft waiting, multiple Goals/questions, outcome admission, and re-presentation of an existing request. Create a Goal explicitly first; never resend a pending ask to continue waiting.";
|
|
34804
34818
|
var CreateGoalSchema2 = external_exports.object({
|
|
34805
34819
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
34820
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
34821
|
+
* rings"). Omit it and the brain writes one at admission from the outcome. */
|
|
34822
|
+
title: external_exports.string().trim().min(1).max(80).optional(),
|
|
34806
34823
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
34807
34824
|
idempotencyKey: external_exports.string().trim().min(1).max(200),
|
|
34808
34825
|
/** A past conversation this Goal should be read against — History's "new session from this"
|
|
@@ -34822,11 +34839,14 @@ var CreateGoalSchema2 = external_exports.object({
|
|
|
34822
34839
|
var CreateGoalToolSchema2 = CreateGoalSchema2.extend({
|
|
34823
34840
|
idempotencyKey: CreateGoalSchema2.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
34824
34841
|
}).strict();
|
|
34825
|
-
var CREATE_GOAL_DESCRIPTION2 =
|
|
34842
|
+
var CREATE_GOAL_DESCRIPTION2 = 'Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Pass title to name it in one to five words, as a person would refer to it out loud ("the night rings") \u2014 it heads every list and is spoken on a call; without one the brain writes it. Admission only: the owner must claim it before doing work, then update it as it advances. Returns an admission receipt with goalId, current state, revision, ownerParticipant, and the next step; no Goal content or execution lease.';
|
|
34826
34843
|
var UpdateGoalSchema2 = external_exports.object({
|
|
34827
34844
|
revision: external_exports.number().int().positive(),
|
|
34828
34845
|
changes: external_exports.object({
|
|
34829
34846
|
outcome: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
34847
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud. The
|
|
34848
|
+
* brain writes one at admission; this is the owner saying it better. Null clears it. */
|
|
34849
|
+
title: external_exports.string().trim().min(1).max(80).nullable().optional(),
|
|
34830
34850
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
34831
34851
|
parentGoalId: external_exports.string().uuid().nullable().optional(),
|
|
34832
34852
|
dependencies: external_exports.array(external_exports.object({ goalId: external_exports.string().uuid(), gate: external_exports.enum(["start", "finish"]) }).strict()).optional(),
|
|
@@ -34842,10 +34862,10 @@ var UpdateGoalSchema2 = external_exports.object({
|
|
|
34842
34862
|
var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
|
|
34843
34863
|
var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
34844
34864
|
var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
34845
|
-
var GET_GOAL_DESCRIPTION2 = "Read
|
|
34846
|
-
var UPDATE_GOAL_DESCRIPTION2 = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. Returns the
|
|
34847
|
-
var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns
|
|
34848
|
-
var CHECK_REPLIES_DESCRIPTION2 = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals,
|
|
34865
|
+
var GET_GOAL_DESCRIPTION2 = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
|
|
34866
|
+
var UPDATE_GOAL_DESCRIPTION2 = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. Returns the Goal as get_goal reads it, at its new revision.";
|
|
34867
|
+
var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
34868
|
+
var CHECK_REPLIES_DESCRIPTION2 = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
|
|
34849
34869
|
var CheckRepliesSchema2 = external_exports.object({}).strict();
|
|
34850
34870
|
var GetThreadSchema2 = external_exports.object({
|
|
34851
34871
|
parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
|
|
@@ -34881,6 +34901,7 @@ function entryWords(entry) {
|
|
|
34881
34901
|
}
|
|
34882
34902
|
return entry.sources.map((source) => source.text).join("\n");
|
|
34883
34903
|
}
|
|
34904
|
+
var LIVE_MS2 = 3 * 6e4;
|
|
34884
34905
|
var ContextSchema2 = external_exports.object({
|
|
34885
34906
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
34886
34907
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -35662,7 +35683,19 @@ var QueueQuestionSchema2 = external_exports.object({
|
|
|
35662
35683
|
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
35663
35684
|
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
35664
35685
|
* for a settled question whose reply carried nothing readable. */
|
|
35665
|
-
answer: external_exports.string().nullable().default(null)
|
|
35686
|
+
answer: external_exports.string().nullable().default(null),
|
|
35687
|
+
/** The Goal this question belongs to — a step knows its Goal on its own, not only through
|
|
35688
|
+
* an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
|
|
35689
|
+
* READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
|
|
35690
|
+
* order, its route, home's trees and the list of steps all take a step's Goal from here, so
|
|
35691
|
+
* this is the field they agree through rather than each re-deriving it from the row it
|
|
35692
|
+
* arrived under. Required because the API projects it on every need it sends. */
|
|
35693
|
+
goalId: external_exports.string(),
|
|
35694
|
+
/** True only while an unmet START gate holds the Goal — a Goal that merely waits to
|
|
35695
|
+
* *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
|
|
35696
|
+
* the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
|
|
35697
|
+
* gate at all. */
|
|
35698
|
+
blocked: external_exports.boolean().default(false)
|
|
35666
35699
|
});
|
|
35667
35700
|
var QueueItemSchema2 = external_exports.object({
|
|
35668
35701
|
id: external_exports.string(),
|
|
@@ -35799,25 +35832,6 @@ var DeliveryConfigSchema2 = external_exports.object({
|
|
|
35799
35832
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
35800
35833
|
realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
|
|
35801
35834
|
});
|
|
35802
|
-
var StatusSchema2 = external_exports.object({
|
|
35803
|
-
name: external_exports.string(),
|
|
35804
|
-
sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
|
|
35805
|
-
/** A phone is registered for push/ring (any push token on the account). */
|
|
35806
|
-
phone: external_exports.boolean(),
|
|
35807
|
-
/** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
|
|
35808
|
-
* it never picked up. THE SAME NUMBER the harness's wake gate reads
|
|
35809
|
-
* (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
|
|
35810
|
-
* zero while the sweep sees one is two ideas of "waiting".
|
|
35811
|
-
*
|
|
35812
|
-
* Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
|
|
35813
|
-
* that nobody spawned, so a terminal session only learns of work by asking. The harness
|
|
35814
|
-
* used to paper over that by spawning a SECOND process on the identity; now it stands
|
|
35815
|
-
* back, correctly, and the person sitting at the terminal is the one who can act. A
|
|
35816
|
-
* coffee-beans request sat unread for three days.
|
|
35817
|
-
*
|
|
35818
|
-
* Optional: an older API sends no field, and the statusline then renders exactly as before. */
|
|
35819
|
-
waiting: external_exports.number().int().nonnegative().optional()
|
|
35820
|
-
});
|
|
35821
35835
|
var EnvelopeRecipientSchema2 = external_exports.object({
|
|
35822
35836
|
keyId: external_exports.string(),
|
|
35823
35837
|
epk: external_exports.string(),
|
|
@@ -36004,7 +36018,9 @@ async function step(session, state, rail) {
|
|
|
36004
36018
|
unheard(brief2, state);
|
|
36005
36019
|
if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
|
|
36006
36020
|
else state.goal = { id: brief2.goalId, revision: brief2.revision };
|
|
36007
|
-
|
|
36021
|
+
const outcome = brief2.outcome?.trim() ?? "";
|
|
36022
|
+
const said = (brief2.entries ?? []).map((e) => entryWords(e).trim()).filter((w) => w && w.toLowerCase() !== outcome.toLowerCase());
|
|
36023
|
+
send2(session, state, [outcome, ...said].filter(Boolean).join("\n\n"));
|
|
36008
36024
|
return;
|
|
36009
36025
|
}
|
|
36010
36026
|
const brief = await rail.read(state.goal.id);
|
|
@@ -36023,9 +36039,9 @@ async function step(session, state, rail) {
|
|
|
36023
36039
|
}
|
|
36024
36040
|
if (state.asks?.size || !state.resting) return;
|
|
36025
36041
|
const words2 = unheard(brief, state);
|
|
36026
|
-
if (words2.length)
|
|
36042
|
+
if (words2.length) send2(session, state, words2.join("\n\n"));
|
|
36027
36043
|
}
|
|
36028
|
-
function
|
|
36044
|
+
function send2(session, state, text) {
|
|
36029
36045
|
state.resting = false;
|
|
36030
36046
|
session.send(text);
|
|
36031
36047
|
}
|
|
@@ -36158,7 +36174,7 @@ function runHarness(opts) {
|
|
|
36158
36174
|
let tail = [];
|
|
36159
36175
|
const state = { resting: true };
|
|
36160
36176
|
let session = null;
|
|
36161
|
-
const rail = railFor({ token: opts.token });
|
|
36177
|
+
const rail = railFor({ token: opts.token, ...opts.resume ? { reach: reachAs(opts.resume) } : {} });
|
|
36162
36178
|
async function handle(event) {
|
|
36163
36179
|
if (event.kind === "error") {
|
|
36164
36180
|
opts.log(`\u26A0 ${event.message}`);
|
|
@@ -36206,6 +36222,7 @@ function runHarness(opts) {
|
|
|
36206
36222
|
cwd: opts.cwd,
|
|
36207
36223
|
mode: opts.mode,
|
|
36208
36224
|
...opts.token ? { token: opts.token } : {},
|
|
36225
|
+
...opts.resume ? { resume: opts.resume } : {},
|
|
36209
36226
|
...opts.bin ? { bin: opts.bin } : {},
|
|
36210
36227
|
...opts.onExit ? { onExit: opts.onExit } : {},
|
|
36211
36228
|
onEvent: (event) => void handle(event).catch((err) => opts.log(`\u26A0 ${err.message}`))
|
|
@@ -36239,8 +36256,8 @@ function runHarness(opts) {
|
|
|
36239
36256
|
init_dist();
|
|
36240
36257
|
init_catalog();
|
|
36241
36258
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
36242
|
-
import { homedir as
|
|
36243
|
-
import { join as
|
|
36259
|
+
import { homedir as homedir6 } from "os";
|
|
36260
|
+
import { join as join6 } from "path";
|
|
36244
36261
|
|
|
36245
36262
|
// src/workspaces.ts
|
|
36246
36263
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -36279,10 +36296,66 @@ function resolveWakeDir(pinned, deps) {
|
|
|
36279
36296
|
return listWorkspaces(deps)[0];
|
|
36280
36297
|
}
|
|
36281
36298
|
|
|
36299
|
+
// src/owner.ts
|
|
36300
|
+
import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync } from "fs";
|
|
36301
|
+
import { homedir as homedir5 } from "os";
|
|
36302
|
+
import { join as join4 } from "path";
|
|
36303
|
+
var HEAD_BYTES = 64e3;
|
|
36304
|
+
function readHead(file) {
|
|
36305
|
+
const fd = openSync2(file, "r");
|
|
36306
|
+
try {
|
|
36307
|
+
const buf = Buffer.alloc(HEAD_BYTES);
|
|
36308
|
+
const n = readSync(fd, buf, 0, HEAD_BYTES, 0);
|
|
36309
|
+
return buf.subarray(0, n).toString("utf8");
|
|
36310
|
+
} finally {
|
|
36311
|
+
closeSync2(fd);
|
|
36312
|
+
}
|
|
36313
|
+
}
|
|
36314
|
+
function ownerSession(slot, deps = {}) {
|
|
36315
|
+
const m = /^session:([0-9a-f]{8})$/.exec(slot);
|
|
36316
|
+
if (!m) return null;
|
|
36317
|
+
const prefix = m[1];
|
|
36318
|
+
const root = deps.projectsDir ?? join4(homedir5(), ".claude", "projects");
|
|
36319
|
+
const list = deps.list ?? ((dir) => readdirSync(dir));
|
|
36320
|
+
const head = deps.head ?? readHead;
|
|
36321
|
+
let folders;
|
|
36322
|
+
try {
|
|
36323
|
+
folders = list(root);
|
|
36324
|
+
} catch {
|
|
36325
|
+
return null;
|
|
36326
|
+
}
|
|
36327
|
+
for (const folder of folders) {
|
|
36328
|
+
let files;
|
|
36329
|
+
try {
|
|
36330
|
+
files = list(join4(root, folder));
|
|
36331
|
+
} catch {
|
|
36332
|
+
continue;
|
|
36333
|
+
}
|
|
36334
|
+
const file = files.find((f) => f.startsWith(prefix) && f.endsWith(".jsonl"));
|
|
36335
|
+
if (!file) continue;
|
|
36336
|
+
let text;
|
|
36337
|
+
try {
|
|
36338
|
+
text = head(join4(root, folder, file));
|
|
36339
|
+
} catch {
|
|
36340
|
+
continue;
|
|
36341
|
+
}
|
|
36342
|
+
for (const line of text.split("\n")) {
|
|
36343
|
+
try {
|
|
36344
|
+
const row = JSON.parse(line);
|
|
36345
|
+
if (typeof row.cwd === "string" && typeof row.sessionId === "string" && row.sessionId.startsWith(prefix)) {
|
|
36346
|
+
return { sessionId: row.sessionId, cwd: row.cwd };
|
|
36347
|
+
}
|
|
36348
|
+
} catch {
|
|
36349
|
+
}
|
|
36350
|
+
}
|
|
36351
|
+
}
|
|
36352
|
+
return null;
|
|
36353
|
+
}
|
|
36354
|
+
|
|
36282
36355
|
// src/update.ts
|
|
36283
36356
|
import { execFile } from "child_process";
|
|
36284
36357
|
import { createRequire } from "module";
|
|
36285
|
-
import { dirname as dirname3, join as
|
|
36358
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
36286
36359
|
import { promisify } from "util";
|
|
36287
36360
|
var SERVICE_LABEL = "ai.paigy.harness";
|
|
36288
36361
|
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -36305,7 +36378,7 @@ async function latestVersion() {
|
|
|
36305
36378
|
}
|
|
36306
36379
|
async function install(version) {
|
|
36307
36380
|
const bin = dirname3(process.execPath);
|
|
36308
|
-
await promisify(execFile)(
|
|
36381
|
+
await promisify(execFile)(join5(bin, "npm"), ["install", "-g", `@paigy/harness@${version}`], {
|
|
36309
36382
|
env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ""}`, npm_config_update_notifier: "false" },
|
|
36310
36383
|
timeout: 5 * 6e4
|
|
36311
36384
|
});
|
|
@@ -36339,14 +36412,14 @@ function selfUpdater(deps) {
|
|
|
36339
36412
|
}
|
|
36340
36413
|
|
|
36341
36414
|
// src/host.ts
|
|
36342
|
-
var HOST_FILE =
|
|
36415
|
+
var HOST_FILE = join6(homedir6(), ".paigy", "host.json");
|
|
36343
36416
|
var runKey = (slot) => `slot:${slot}`;
|
|
36344
36417
|
var claimedRun = (sessionId2) => {
|
|
36345
36418
|
const slot = sessionSlot(sessionId2);
|
|
36346
36419
|
return { slot, key: runKey(slot) };
|
|
36347
36420
|
};
|
|
36348
|
-
async function slotHasWaitingWork(token) {
|
|
36349
|
-
return (await checkReplies({ token })).deliveries.length > 0;
|
|
36421
|
+
async function slotHasWaitingWork(token, session) {
|
|
36422
|
+
return (await checkReplies({ token, ...session ? { reach: reachAs(session) } : {} })).deliveries.length > 0;
|
|
36350
36423
|
}
|
|
36351
36424
|
var PRESENCE_FRESH_MS = 3 * 6e4;
|
|
36352
36425
|
function wakeAction(live, now, serverSeenAt) {
|
|
@@ -36359,6 +36432,7 @@ function startHost(opts) {
|
|
|
36359
36432
|
const asHost = { token: opts.token };
|
|
36360
36433
|
const refreshIdentities = async () => {
|
|
36361
36434
|
for (const slot of listSlots()) {
|
|
36435
|
+
if (slot === "Desktop") continue;
|
|
36362
36436
|
const token = readToken(slot);
|
|
36363
36437
|
if (!token) continue;
|
|
36364
36438
|
const me = await whoAmI({ token }).catch(() => null);
|
|
@@ -36438,9 +36512,11 @@ function startHost(opts) {
|
|
|
36438
36512
|
if (wakeAction(!!live, Date.now(), seenByServer) === "skip") return;
|
|
36439
36513
|
const token = readToken(slot);
|
|
36440
36514
|
if (!token) return;
|
|
36441
|
-
const
|
|
36515
|
+
const owner = ownerSession(slot);
|
|
36516
|
+
if (owner && !allowed(owner.cwd, opts.wsDeps)) return;
|
|
36517
|
+
const workspace = owner?.cwd ?? resolveWakeDir(slotIdentity(slot).workspace, opts.wsDeps);
|
|
36442
36518
|
if (!workspace) return;
|
|
36443
|
-
if (!await slotHasWaitingWork(token).catch(() => false)) return;
|
|
36519
|
+
if (!await slotHasWaitingWork(token, owner?.sessionId).catch(() => false)) return;
|
|
36444
36520
|
const label = slotName(slot) ?? slot;
|
|
36445
36521
|
const log = (line) => opts.log(`[${label}] ${line}`);
|
|
36446
36522
|
const run = runHarness({
|
|
@@ -36449,6 +36525,7 @@ function startHost(opts) {
|
|
|
36449
36525
|
mode: "bypass",
|
|
36450
36526
|
prompt: "",
|
|
36451
36527
|
token,
|
|
36528
|
+
...owner ? { resume: owner.sessionId } : {},
|
|
36452
36529
|
// A dead run must not squat the slot — evict so the next wake can respawn.
|
|
36453
36530
|
onExit: () => {
|
|
36454
36531
|
runs.delete(key);
|
|
@@ -36459,7 +36536,7 @@ function startHost(opts) {
|
|
|
36459
36536
|
runs.set(key, { run, label, token });
|
|
36460
36537
|
void heartbeat(void 0, { token }).catch(() => {
|
|
36461
36538
|
});
|
|
36462
|
-
opts.log(`\u25B6 woke ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
36539
|
+
opts.log(`\u25B6 ${owner ? "resumed" : "woke"} ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
36463
36540
|
}
|
|
36464
36541
|
}
|
|
36465
36542
|
const listening = /* @__PURE__ */ new Map();
|
|
@@ -36691,7 +36768,7 @@ async function pairThisMachine() {
|
|
|
36691
36768
|
}
|
|
36692
36769
|
}
|
|
36693
36770
|
function forwardCodexThreadId() {
|
|
36694
|
-
const cfg =
|
|
36771
|
+
const cfg = join7(process.env.CODEX_HOME || join7(homedir8(), ".codex"), "config.toml");
|
|
36695
36772
|
if (!existsSync5(cfg)) return;
|
|
36696
36773
|
const text = readFileSync4(cfg, "utf8");
|
|
36697
36774
|
const head = "[mcp_servers.paigy]\n";
|
|
@@ -36793,7 +36870,7 @@ async function main() {
|
|
|
36793
36870
|
const wsDeps = { file: workspacesFile() };
|
|
36794
36871
|
for (const dir of parsed.grant) addWorkspace(dir, wsDeps);
|
|
36795
36872
|
if (!listWorkspaces(wsDeps).length) {
|
|
36796
|
-
const guess =
|
|
36873
|
+
const guess = join7(homedir8(), "projects");
|
|
36797
36874
|
if (existsSync5(guess)) {
|
|
36798
36875
|
addWorkspace(guess, wsDeps);
|
|
36799
36876
|
console.log(`\u2713 workspace granted: ${guess} (change with --grant DIR)`);
|
|
@@ -36834,7 +36911,7 @@ async function main() {
|
|
|
36834
36911
|
}
|
|
36835
36912
|
}
|
|
36836
36913
|
try {
|
|
36837
|
-
const settings =
|
|
36914
|
+
const settings = join7(homedir8(), ".claude", "settings.json");
|
|
36838
36915
|
if (which("claude") && existsSync5(settings)) {
|
|
36839
36916
|
const cfg = JSON.parse(readFileSync4(settings, "utf8"));
|
|
36840
36917
|
if (!cfg["statusLine"]) {
|
|
@@ -36892,13 +36969,13 @@ async function main() {
|
|
|
36892
36969
|
<key>SoftResourceLimits</key><dict>
|
|
36893
36970
|
<key>NumberOfFiles</key><integer>65536</integer>
|
|
36894
36971
|
</dict>
|
|
36895
|
-
<key>StandardOutPath</key><string>${
|
|
36896
|
-
<key>StandardErrorPath</key><string>${
|
|
36972
|
+
<key>StandardOutPath</key><string>${join7(homedir8(), ".paigy", "host.log")}</string>
|
|
36973
|
+
<key>StandardErrorPath</key><string>${join7(homedir8(), ".paigy", "host.log")}</string>
|
|
36897
36974
|
</dict></plist>
|
|
36898
36975
|
`;
|
|
36899
|
-
const dir =
|
|
36976
|
+
const dir = join7(homedir8(), "Library", "LaunchAgents");
|
|
36900
36977
|
mkdirSync3(dir, { recursive: true });
|
|
36901
|
-
const path =
|
|
36978
|
+
const path = join7(dir, `${SERVICE_LABEL}.plist`);
|
|
36902
36979
|
writeFileSync4(path, plist);
|
|
36903
36980
|
execSync(`launchctl unload ${path} 2>/dev/null; launchctl load ${path}`, { shell: "/bin/sh" });
|
|
36904
36981
|
console.log(`\u2713 host installed as a login service (${path}) \u2014 logs at ~/.paigy/host.log`);
|
|
@@ -36921,7 +36998,7 @@ async function main() {
|
|
|
36921
36998
|
process.exit(1);
|
|
36922
36999
|
}
|
|
36923
37000
|
const name = slotName(slot) ?? slot;
|
|
36924
|
-
console.log(`\u2713 ${name} is handed off \u2014 wakes in ${cwd.replace(
|
|
37001
|
+
console.log(`\u2713 ${name} is handed off \u2014 wakes in ${cwd.replace(homedir8(), "~")}`);
|
|
36925
37002
|
if (!allowed(cwd, { file: workspacesFile() })) {
|
|
36926
37003
|
console.log(` \u26A0 that folder isn't on the allow-list, so wakes will land in the first granted`);
|
|
36927
37004
|
console.log(` workspace instead \u2014 add it in the desktop app, or: paigy-harness host --grant ${cwd}`);
|