@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/main.js
CHANGED
|
@@ -4580,7 +4580,7 @@ import { app, BrowserWindow, dialog, ipcMain } from "electron";
|
|
|
4580
4580
|
import { spawn as spawn2 } from "child_process";
|
|
4581
4581
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4582
4582
|
import { fileURLToPath } from "url";
|
|
4583
|
-
import { dirname as dirname4, join as
|
|
4583
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
4584
4584
|
|
|
4585
4585
|
// ../../packages/sdk/dist/index.js
|
|
4586
4586
|
import { createRequire as __sdkCreateRequire } from "module";
|
|
@@ -10915,11 +10915,17 @@ function sessionId() {
|
|
|
10915
10915
|
return SESSION_ID;
|
|
10916
10916
|
}
|
|
10917
10917
|
async function reach(url, init) {
|
|
10918
|
+
return send(url, init, SESSION_ID);
|
|
10919
|
+
}
|
|
10920
|
+
function reachAs(session) {
|
|
10921
|
+
return (url, init) => send(url, init, session);
|
|
10922
|
+
}
|
|
10923
|
+
async function send(url, init, session) {
|
|
10918
10924
|
try {
|
|
10919
10925
|
const headers = {
|
|
10920
10926
|
...init?.headers,
|
|
10921
10927
|
"x-paigy-instance": INSTANCE_ID,
|
|
10922
|
-
"x-paigy-session":
|
|
10928
|
+
"x-paigy-session": session
|
|
10923
10929
|
};
|
|
10924
10930
|
return await fetch(url, { ...init, headers, dispatcher: await proxy() });
|
|
10925
10931
|
} catch (e) {
|
|
@@ -12195,6 +12201,9 @@ var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
|
12195
12201
|
var 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.";
|
|
12196
12202
|
var CreateGoalSchema = external_exports.object({
|
|
12197
12203
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
12204
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
12205
|
+
* rings"). Omit it and the brain writes one at admission from the outcome. */
|
|
12206
|
+
title: external_exports.string().trim().min(1).max(80).optional(),
|
|
12198
12207
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
12199
12208
|
idempotencyKey: external_exports.string().trim().min(1).max(200),
|
|
12200
12209
|
/** A past conversation this Goal should be read against — History's "new session from this"
|
|
@@ -12214,11 +12223,14 @@ var CreateGoalSchema = external_exports.object({
|
|
|
12214
12223
|
var CreateGoalToolSchema = CreateGoalSchema.extend({
|
|
12215
12224
|
idempotencyKey: CreateGoalSchema.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
12216
12225
|
}).strict();
|
|
12217
|
-
var CREATE_GOAL_DESCRIPTION =
|
|
12226
|
+
var 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.';
|
|
12218
12227
|
var UpdateGoalSchema = external_exports.object({
|
|
12219
12228
|
revision: external_exports.number().int().positive(),
|
|
12220
12229
|
changes: external_exports.object({
|
|
12221
12230
|
outcome: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
12231
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud. The
|
|
12232
|
+
* brain writes one at admission; this is the owner saying it better. Null clears it. */
|
|
12233
|
+
title: external_exports.string().trim().min(1).max(80).nullable().optional(),
|
|
12222
12234
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
12223
12235
|
parentGoalId: external_exports.string().uuid().nullable().optional(),
|
|
12224
12236
|
dependencies: external_exports.array(external_exports.object({ goalId: external_exports.string().uuid(), gate: external_exports.enum(["start", "finish"]) }).strict()).optional(),
|
|
@@ -12234,10 +12246,10 @@ var UpdateGoalSchema = external_exports.object({
|
|
|
12234
12246
|
var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
|
|
12235
12247
|
var ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
12236
12248
|
var GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
12237
|
-
var GET_GOAL_DESCRIPTION = "Read
|
|
12238
|
-
var 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
|
|
12239
|
-
var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns
|
|
12240
|
-
var 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,
|
|
12249
|
+
var 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.";
|
|
12250
|
+
var 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.";
|
|
12251
|
+
var 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.";
|
|
12252
|
+
var 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.";
|
|
12241
12253
|
var CheckRepliesSchema = external_exports.object({}).strict();
|
|
12242
12254
|
var GetThreadSchema = external_exports.object({
|
|
12243
12255
|
parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
|
|
@@ -12258,6 +12270,7 @@ var AGENT_TOOLS = [
|
|
|
12258
12270
|
{ name: "update_goal", description: UPDATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(UpdateGoalToolSchema) }
|
|
12259
12271
|
];
|
|
12260
12272
|
var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
12273
|
+
var LIVE_MS = 3 * 6e4;
|
|
12261
12274
|
var ContextSchema = external_exports.object({
|
|
12262
12275
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
12263
12276
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -13094,7 +13107,19 @@ var QueueQuestionSchema = external_exports.object({
|
|
|
13094
13107
|
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
13095
13108
|
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
13096
13109
|
* for a settled question whose reply carried nothing readable. */
|
|
13097
|
-
answer: external_exports.string().nullable().default(null)
|
|
13110
|
+
answer: external_exports.string().nullable().default(null),
|
|
13111
|
+
/** The Goal this question belongs to — a step knows its Goal on its own, not only through
|
|
13112
|
+
* an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
|
|
13113
|
+
* READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
|
|
13114
|
+
* order, its route, home's trees and the list of steps all take a step's Goal from here, so
|
|
13115
|
+
* this is the field they agree through rather than each re-deriving it from the row it
|
|
13116
|
+
* arrived under. Required because the API projects it on every need it sends. */
|
|
13117
|
+
goalId: external_exports.string(),
|
|
13118
|
+
/** True only while an unmet START gate holds the Goal — a Goal that merely waits to
|
|
13119
|
+
* *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
|
|
13120
|
+
* the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
|
|
13121
|
+
* gate at all. */
|
|
13122
|
+
blocked: external_exports.boolean().default(false)
|
|
13098
13123
|
});
|
|
13099
13124
|
var QueueItemSchema = external_exports.object({
|
|
13100
13125
|
id: external_exports.string(),
|
|
@@ -13233,25 +13258,6 @@ var DeliveryConfigSchema = external_exports.object({
|
|
|
13233
13258
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
13234
13259
|
realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
|
|
13235
13260
|
});
|
|
13236
|
-
var StatusSchema = external_exports.object({
|
|
13237
|
-
name: external_exports.string(),
|
|
13238
|
-
sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
|
|
13239
|
-
/** A phone is registered for push/ring (any push token on the account). */
|
|
13240
|
-
phone: external_exports.boolean(),
|
|
13241
|
-
/** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
|
|
13242
|
-
* it never picked up. THE SAME NUMBER the harness's wake gate reads
|
|
13243
|
-
* (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
|
|
13244
|
-
* zero while the sweep sees one is two ideas of "waiting".
|
|
13245
|
-
*
|
|
13246
|
-
* Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
|
|
13247
|
-
* that nobody spawned, so a terminal session only learns of work by asking. The harness
|
|
13248
|
-
* used to paper over that by spawning a SECOND process on the identity; now it stands
|
|
13249
|
-
* back, correctly, and the person sitting at the terminal is the one who can act. A
|
|
13250
|
-
* coffee-beans request sat unread for three days.
|
|
13251
|
-
*
|
|
13252
|
-
* Optional: an older API sends no field, and the statusline then renders exactly as before. */
|
|
13253
|
-
waiting: external_exports.number().int().nonnegative().optional()
|
|
13254
|
-
});
|
|
13255
13261
|
var EnvelopeRecipientSchema = external_exports.object({
|
|
13256
13262
|
keyId: external_exports.string(),
|
|
13257
13263
|
epk: external_exports.string(),
|
|
@@ -13593,7 +13599,7 @@ async function listNotes(opts = {}) {
|
|
|
13593
13599
|
}
|
|
13594
13600
|
async function listConnections(opts = {}) {
|
|
13595
13601
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
|
|
13596
|
-
headers: { authorization: `Bearer ${authToken(opts.token)}
|
|
13602
|
+
headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
|
|
13597
13603
|
}));
|
|
13598
13604
|
if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
|
|
13599
13605
|
return await res.json();
|
|
@@ -13634,14 +13640,14 @@ async function dismissTriage(proposalId, opts = {}) {
|
|
|
13634
13640
|
async function contact(input, opts = {}) {
|
|
13635
13641
|
const parsed = ContactSchema.parse(input);
|
|
13636
13642
|
opts.signal?.throwIfAborted();
|
|
13637
|
-
const
|
|
13643
|
+
const send22 = opts.reach ?? reach;
|
|
13638
13644
|
const headers = { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}`, "x-paigy-model": "goal-entry-v1" };
|
|
13639
13645
|
let deliveryId;
|
|
13640
13646
|
if ("deliveryId" in parsed) deliveryId = parsed.deliveryId;
|
|
13641
13647
|
else {
|
|
13642
13648
|
if (!opts.reach && readKeyFile()?.e2ee) throw new Error("Goal contact does not support E2EE yet; refusing to send plaintext.");
|
|
13643
13649
|
const shaped = deriveAsk(NotifyRequestSchema.parse({ ask: parsed.ask, waiting: parsed.waiting, options: parsed.options }));
|
|
13644
|
-
const res = ensureAuthed(await
|
|
13650
|
+
const res = ensureAuthed(await send22(`${BACKEND_URL}/api/goals/${parsed.goalIds[0]}/contact`, {
|
|
13645
13651
|
method: "POST",
|
|
13646
13652
|
headers,
|
|
13647
13653
|
signal: opts.signal,
|
|
@@ -13653,11 +13659,9 @@ async function contact(input, opts = {}) {
|
|
|
13653
13659
|
if (!deliveryId) throw new Error("Goal contact returned no Delivery identity");
|
|
13654
13660
|
}
|
|
13655
13661
|
const read = async (signal2) => {
|
|
13656
|
-
const res = ensureAuthed(await
|
|
13662
|
+
const res = ensureAuthed(await send22(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
13657
13663
|
if (!res.ok) await fail("read_delivery", res);
|
|
13658
|
-
|
|
13659
|
-
return delivery.kind === "notification" ? { ...delivery, message: `${delivery.message}
|
|
13660
|
-
Inbox delivery is asynchronous; use claim_goal/get_goal to collect durable answers. Do not poll this Notification.` } : delivery;
|
|
13664
|
+
return await res.json();
|
|
13661
13665
|
};
|
|
13662
13666
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
13663
13667
|
if (opts.waits === false) return read(opts.signal);
|
|
@@ -13784,7 +13788,7 @@ async function subscribeWake(onNudge, opts = {}) {
|
|
|
13784
13788
|
|
|
13785
13789
|
// src/main.ts
|
|
13786
13790
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
13787
|
-
import { homedir as
|
|
13791
|
+
import { homedir as homedir8, hostname } from "os";
|
|
13788
13792
|
|
|
13789
13793
|
// src/harness/catalog.ts
|
|
13790
13794
|
import { spawnSync } from "child_process";
|
|
@@ -13816,6 +13820,8 @@ var CATALOG = [
|
|
|
13816
13820
|
name: "agy",
|
|
13817
13821
|
label: "Antigravity",
|
|
13818
13822
|
cli: "agy",
|
|
13823
|
+
// `agy` has no ACP mode; its adapter ships inside the harness (`harness/agy-acp.ts`).
|
|
13824
|
+
adapter: "paigy-agy-acp",
|
|
13819
13825
|
authProbe: ["agy", "help"],
|
|
13820
13826
|
loginHint: "Install and set up Antigravity (`agy`).",
|
|
13821
13827
|
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
@@ -13876,8 +13882,8 @@ function detectAll(deps = {}) {
|
|
|
13876
13882
|
|
|
13877
13883
|
// src/host.ts
|
|
13878
13884
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
13879
|
-
import { homedir as
|
|
13880
|
-
import { join as
|
|
13885
|
+
import { homedir as homedir6 } from "os";
|
|
13886
|
+
import { join as join6 } from "path";
|
|
13881
13887
|
|
|
13882
13888
|
// src/workspaces.ts
|
|
13883
13889
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -13922,6 +13928,62 @@ function resolveWakeDir(pinned, deps) {
|
|
|
13922
13928
|
return listWorkspaces(deps)[0];
|
|
13923
13929
|
}
|
|
13924
13930
|
|
|
13931
|
+
// src/owner.ts
|
|
13932
|
+
import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync } from "fs";
|
|
13933
|
+
import { homedir as homedir4 } from "os";
|
|
13934
|
+
import { join as join4 } from "path";
|
|
13935
|
+
var HEAD_BYTES = 64e3;
|
|
13936
|
+
function readHead(file) {
|
|
13937
|
+
const fd = openSync2(file, "r");
|
|
13938
|
+
try {
|
|
13939
|
+
const buf = Buffer.alloc(HEAD_BYTES);
|
|
13940
|
+
const n = readSync(fd, buf, 0, HEAD_BYTES, 0);
|
|
13941
|
+
return buf.subarray(0, n).toString("utf8");
|
|
13942
|
+
} finally {
|
|
13943
|
+
closeSync2(fd);
|
|
13944
|
+
}
|
|
13945
|
+
}
|
|
13946
|
+
function ownerSession(slot, deps = {}) {
|
|
13947
|
+
const m = /^session:([0-9a-f]{8})$/.exec(slot);
|
|
13948
|
+
if (!m) return null;
|
|
13949
|
+
const prefix = m[1];
|
|
13950
|
+
const root = deps.projectsDir ?? join4(homedir4(), ".claude", "projects");
|
|
13951
|
+
const list = deps.list ?? ((dir) => readdirSync(dir));
|
|
13952
|
+
const head = deps.head ?? readHead;
|
|
13953
|
+
let folders;
|
|
13954
|
+
try {
|
|
13955
|
+
folders = list(root);
|
|
13956
|
+
} catch {
|
|
13957
|
+
return null;
|
|
13958
|
+
}
|
|
13959
|
+
for (const folder of folders) {
|
|
13960
|
+
let files;
|
|
13961
|
+
try {
|
|
13962
|
+
files = list(join4(root, folder));
|
|
13963
|
+
} catch {
|
|
13964
|
+
continue;
|
|
13965
|
+
}
|
|
13966
|
+
const file = files.find((f) => f.startsWith(prefix) && f.endsWith(".jsonl"));
|
|
13967
|
+
if (!file) continue;
|
|
13968
|
+
let text;
|
|
13969
|
+
try {
|
|
13970
|
+
text = head(join4(root, folder, file));
|
|
13971
|
+
} catch {
|
|
13972
|
+
continue;
|
|
13973
|
+
}
|
|
13974
|
+
for (const line of text.split("\n")) {
|
|
13975
|
+
try {
|
|
13976
|
+
const row = JSON.parse(line);
|
|
13977
|
+
if (typeof row.cwd === "string" && typeof row.sessionId === "string" && row.sessionId.startsWith(prefix)) {
|
|
13978
|
+
return { sessionId: row.sessionId, cwd: row.cwd };
|
|
13979
|
+
}
|
|
13980
|
+
} catch {
|
|
13981
|
+
}
|
|
13982
|
+
}
|
|
13983
|
+
}
|
|
13984
|
+
return null;
|
|
13985
|
+
}
|
|
13986
|
+
|
|
13925
13987
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
|
|
13926
13988
|
var ignoreOverride2 = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
|
|
13927
13989
|
var defaultOptions2 = {
|
|
@@ -15266,6 +15328,9 @@ var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
|
15266
15328
|
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.";
|
|
15267
15329
|
var CreateGoalSchema2 = external_exports.object({
|
|
15268
15330
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
15331
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
15332
|
+
* rings"). Omit it and the brain writes one at admission from the outcome. */
|
|
15333
|
+
title: external_exports.string().trim().min(1).max(80).optional(),
|
|
15269
15334
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
15270
15335
|
idempotencyKey: external_exports.string().trim().min(1).max(200),
|
|
15271
15336
|
/** A past conversation this Goal should be read against — History's "new session from this"
|
|
@@ -15285,11 +15350,14 @@ var CreateGoalSchema2 = external_exports.object({
|
|
|
15285
15350
|
var CreateGoalToolSchema2 = CreateGoalSchema2.extend({
|
|
15286
15351
|
idempotencyKey: CreateGoalSchema2.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
15287
15352
|
}).strict();
|
|
15288
|
-
var CREATE_GOAL_DESCRIPTION2 =
|
|
15353
|
+
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.';
|
|
15289
15354
|
var UpdateGoalSchema2 = external_exports.object({
|
|
15290
15355
|
revision: external_exports.number().int().positive(),
|
|
15291
15356
|
changes: external_exports.object({
|
|
15292
15357
|
outcome: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
15358
|
+
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud. The
|
|
15359
|
+
* brain writes one at admission; this is the owner saying it better. Null clears it. */
|
|
15360
|
+
title: external_exports.string().trim().min(1).max(80).nullable().optional(),
|
|
15293
15361
|
ownerParticipant: external_exports.string().trim().min(1).optional(),
|
|
15294
15362
|
parentGoalId: external_exports.string().uuid().nullable().optional(),
|
|
15295
15363
|
dependencies: external_exports.array(external_exports.object({ goalId: external_exports.string().uuid(), gate: external_exports.enum(["start", "finish"]) }).strict()).optional(),
|
|
@@ -15305,10 +15373,10 @@ var UpdateGoalSchema2 = external_exports.object({
|
|
|
15305
15373
|
var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
|
|
15306
15374
|
var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
15307
15375
|
var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
15308
|
-
var GET_GOAL_DESCRIPTION2 = "Read
|
|
15309
|
-
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
|
|
15310
|
-
var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns
|
|
15311
|
-
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,
|
|
15376
|
+
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.";
|
|
15377
|
+
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.";
|
|
15378
|
+
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.";
|
|
15379
|
+
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.";
|
|
15312
15380
|
var CheckRepliesSchema2 = external_exports.object({}).strict();
|
|
15313
15381
|
var GetThreadSchema2 = external_exports.object({
|
|
15314
15382
|
parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
|
|
@@ -15344,6 +15412,7 @@ function entryWords(entry) {
|
|
|
15344
15412
|
}
|
|
15345
15413
|
return entry.sources.map((source) => source.text).join("\n");
|
|
15346
15414
|
}
|
|
15415
|
+
var LIVE_MS2 = 3 * 6e4;
|
|
15347
15416
|
var ContextSchema2 = external_exports.object({
|
|
15348
15417
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
15349
15418
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -16125,7 +16194,19 @@ var QueueQuestionSchema2 = external_exports.object({
|
|
|
16125
16194
|
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
16126
16195
|
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
16127
16196
|
* for a settled question whose reply carried nothing readable. */
|
|
16128
|
-
answer: external_exports.string().nullable().default(null)
|
|
16197
|
+
answer: external_exports.string().nullable().default(null),
|
|
16198
|
+
/** The Goal this question belongs to — a step knows its Goal on its own, not only through
|
|
16199
|
+
* an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
|
|
16200
|
+
* READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
|
|
16201
|
+
* order, its route, home's trees and the list of steps all take a step's Goal from here, so
|
|
16202
|
+
* this is the field they agree through rather than each re-deriving it from the row it
|
|
16203
|
+
* arrived under. Required because the API projects it on every need it sends. */
|
|
16204
|
+
goalId: external_exports.string(),
|
|
16205
|
+
/** True only while an unmet START gate holds the Goal — a Goal that merely waits to
|
|
16206
|
+
* *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
|
|
16207
|
+
* the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
|
|
16208
|
+
* gate at all. */
|
|
16209
|
+
blocked: external_exports.boolean().default(false)
|
|
16129
16210
|
});
|
|
16130
16211
|
var QueueItemSchema2 = external_exports.object({
|
|
16131
16212
|
id: external_exports.string(),
|
|
@@ -16262,25 +16343,6 @@ var DeliveryConfigSchema2 = external_exports.object({
|
|
|
16262
16343
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
16263
16344
|
realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
|
|
16264
16345
|
});
|
|
16265
|
-
var StatusSchema2 = external_exports.object({
|
|
16266
|
-
name: external_exports.string(),
|
|
16267
|
-
sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
|
|
16268
|
-
/** A phone is registered for push/ring (any push token on the account). */
|
|
16269
|
-
phone: external_exports.boolean(),
|
|
16270
|
-
/** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
|
|
16271
|
-
* it never picked up. THE SAME NUMBER the harness's wake gate reads
|
|
16272
|
-
* (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
|
|
16273
|
-
* zero while the sweep sees one is two ideas of "waiting".
|
|
16274
|
-
*
|
|
16275
|
-
* Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
|
|
16276
|
-
* that nobody spawned, so a terminal session only learns of work by asking. The harness
|
|
16277
|
-
* used to paper over that by spawning a SECOND process on the identity; now it stands
|
|
16278
|
-
* back, correctly, and the person sitting at the terminal is the one who can act. A
|
|
16279
|
-
* coffee-beans request sat unread for three days.
|
|
16280
|
-
*
|
|
16281
|
-
* Optional: an older API sends no field, and the statusline then renders exactly as before. */
|
|
16282
|
-
waiting: external_exports.number().int().nonnegative().optional()
|
|
16283
|
-
});
|
|
16284
16346
|
var EnvelopeRecipientSchema2 = external_exports.object({
|
|
16285
16347
|
keyId: external_exports.string(),
|
|
16286
16348
|
epk: external_exports.string(),
|
|
@@ -16433,7 +16495,7 @@ function sameTail(a, b) {
|
|
|
16433
16495
|
// src/harness/session.ts
|
|
16434
16496
|
import { spawn } from "child_process";
|
|
16435
16497
|
import { existsSync as existsSync4 } from "fs";
|
|
16436
|
-
import { homedir as
|
|
16498
|
+
import { homedir as homedir5 } from "os";
|
|
16437
16499
|
import { resolve as resolve2, delimiter as delimiter2 } from "path";
|
|
16438
16500
|
|
|
16439
16501
|
// src/harness/acp.ts
|
|
@@ -16443,15 +16505,19 @@ function optionFor(options, decision) {
|
|
|
16443
16505
|
return options.find((o) => o.kind === want)?.optionId ?? null;
|
|
16444
16506
|
}
|
|
16445
16507
|
function createAcpDriver(opts) {
|
|
16446
|
-
return new AcpDriver(opts.cwd, opts.mode);
|
|
16508
|
+
return new AcpDriver(opts.cwd, opts.mode, opts.resume);
|
|
16447
16509
|
}
|
|
16448
16510
|
var AcpDriver = class {
|
|
16449
|
-
|
|
16511
|
+
/** `resume`: the id of an existing session to continue rather than open a new one (#1637) —
|
|
16512
|
+
* the session a bound identity belongs to, so the process this drives IS its holder. */
|
|
16513
|
+
constructor(cwd, mode, resume) {
|
|
16450
16514
|
this.cwd = cwd;
|
|
16451
16515
|
this.mode = mode;
|
|
16516
|
+
this.resume = resume;
|
|
16452
16517
|
}
|
|
16453
16518
|
cwd;
|
|
16454
16519
|
mode;
|
|
16520
|
+
resume;
|
|
16455
16521
|
nextId = 1;
|
|
16456
16522
|
initId;
|
|
16457
16523
|
sessionNewId;
|
|
@@ -16535,15 +16601,15 @@ var AcpDriver = class {
|
|
|
16535
16601
|
this.sessionNewId = this.nextId++;
|
|
16536
16602
|
return {
|
|
16537
16603
|
events: [],
|
|
16538
|
-
writes: [frame2({ id: this.sessionNewId, method: "session/new", params: { cwd: this.cwd, mcpServers: [] } })]
|
|
16604
|
+
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: [] } })]
|
|
16539
16605
|
};
|
|
16540
16606
|
}
|
|
16541
16607
|
if (msg.id === this.sessionNewId) {
|
|
16542
16608
|
this.sessionNewId = void 0;
|
|
16543
|
-
const sessionId2 = msg.result?.sessionId;
|
|
16609
|
+
const sessionId2 = msg.error ? void 0 : msg.result?.sessionId ?? this.resume;
|
|
16544
16610
|
if (!sessionId2) {
|
|
16545
16611
|
return {
|
|
16546
|
-
events: [{ kind: "error", message:
|
|
16612
|
+
events: [{ kind: "error", message: `${this.resume ? "session/resume" : "session/new"} failed: ${msg.error?.message ?? "no sessionId"}` }],
|
|
16547
16613
|
writes: []
|
|
16548
16614
|
};
|
|
16549
16615
|
}
|
|
@@ -16657,7 +16723,7 @@ var frame2 = (body) => JSON.stringify({ jsonrpc: "2.0", ...body });
|
|
|
16657
16723
|
var ADAPTER_BIN = {
|
|
16658
16724
|
claude: "claude-agent-acp",
|
|
16659
16725
|
codex: "codex-acp",
|
|
16660
|
-
agy: "agy"
|
|
16726
|
+
agy: "paigy-agy-acp"
|
|
16661
16727
|
};
|
|
16662
16728
|
function splitLines(buffer, chunk) {
|
|
16663
16729
|
const combined = buffer + chunk;
|
|
@@ -16666,7 +16732,7 @@ function splitLines(buffer, chunk) {
|
|
|
16666
16732
|
return { lines: parts.filter((l) => l.trim()), rest };
|
|
16667
16733
|
}
|
|
16668
16734
|
function startSession(opts) {
|
|
16669
|
-
const cwd = resolve2(opts.cwd.replace(/^~(?=$|\/)/,
|
|
16735
|
+
const cwd = resolve2(opts.cwd.replace(/^~(?=$|\/)/, homedir5()));
|
|
16670
16736
|
if (!existsSync4(cwd)) {
|
|
16671
16737
|
queueMicrotask(() => opts.onEvent({ kind: "error", message: `workspace does not exist: ${cwd}` }));
|
|
16672
16738
|
}
|
|
@@ -16679,7 +16745,8 @@ function startSession(opts) {
|
|
|
16679
16745
|
env: {
|
|
16680
16746
|
...process.env,
|
|
16681
16747
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter2),
|
|
16682
|
-
...opts.token ? { PAIGY_TOKEN: opts.token } : {}
|
|
16748
|
+
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
16749
|
+
...opts.resume ? { PAIGY_SESSION_ID: opts.resume } : {}
|
|
16683
16750
|
},
|
|
16684
16751
|
stdio: ["pipe", "pipe", "pipe"]
|
|
16685
16752
|
});
|
|
@@ -16699,7 +16766,7 @@ function startSession(opts) {
|
|
|
16699
16766
|
opts.onEvent({ kind: "error", message: e.message });
|
|
16700
16767
|
opts.onExit?.();
|
|
16701
16768
|
});
|
|
16702
|
-
const driver = createAcpDriver({ cwd, mode: opts.mode });
|
|
16769
|
+
const driver = createAcpDriver({ cwd, mode: opts.mode, ...opts.resume ? { resume: opts.resume } : {} });
|
|
16703
16770
|
let buffer = "";
|
|
16704
16771
|
child.stdout?.on("data", (chunk) => {
|
|
16705
16772
|
const { lines: lines2, rest } = splitLines(buffer, String(chunk));
|
|
@@ -16759,7 +16826,9 @@ async function step(session, state, rail) {
|
|
|
16759
16826
|
unheard(brief2, state);
|
|
16760
16827
|
if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
|
|
16761
16828
|
else state.goal = { id: brief2.goalId, revision: brief2.revision };
|
|
16762
|
-
|
|
16829
|
+
const outcome = brief2.outcome?.trim() ?? "";
|
|
16830
|
+
const said = (brief2.entries ?? []).map((e) => entryWords(e).trim()).filter((w) => w && w.toLowerCase() !== outcome.toLowerCase());
|
|
16831
|
+
send2(session, state, [outcome, ...said].filter(Boolean).join("\n\n"));
|
|
16763
16832
|
return;
|
|
16764
16833
|
}
|
|
16765
16834
|
const brief = await rail.read(state.goal.id);
|
|
@@ -16778,9 +16847,9 @@ async function step(session, state, rail) {
|
|
|
16778
16847
|
}
|
|
16779
16848
|
if (state.asks?.size || !state.resting) return;
|
|
16780
16849
|
const words2 = unheard(brief, state);
|
|
16781
|
-
if (words2.length)
|
|
16850
|
+
if (words2.length) send2(session, state, words2.join("\n\n"));
|
|
16782
16851
|
}
|
|
16783
|
-
function
|
|
16852
|
+
function send2(session, state, text) {
|
|
16784
16853
|
state.resting = false;
|
|
16785
16854
|
session.send(text);
|
|
16786
16855
|
}
|
|
@@ -16913,7 +16982,7 @@ function runHarness(opts) {
|
|
|
16913
16982
|
let tail = [];
|
|
16914
16983
|
const state = { resting: true };
|
|
16915
16984
|
let session = null;
|
|
16916
|
-
const rail = railFor({ token: opts.token });
|
|
16985
|
+
const rail = railFor({ token: opts.token, ...opts.resume ? { reach: reachAs(opts.resume) } : {} });
|
|
16917
16986
|
async function handle(event) {
|
|
16918
16987
|
if (event.kind === "error") {
|
|
16919
16988
|
opts.log(`\u26A0 ${event.message}`);
|
|
@@ -16961,6 +17030,7 @@ function runHarness(opts) {
|
|
|
16961
17030
|
cwd: opts.cwd,
|
|
16962
17031
|
mode: opts.mode,
|
|
16963
17032
|
...opts.token ? { token: opts.token } : {},
|
|
17033
|
+
...opts.resume ? { resume: opts.resume } : {},
|
|
16964
17034
|
...opts.bin ? { bin: opts.bin } : {},
|
|
16965
17035
|
...opts.onExit ? { onExit: opts.onExit } : {},
|
|
16966
17036
|
onEvent: (event) => void handle(event).catch((err) => opts.log(`\u26A0 ${err.message}`))
|
|
@@ -16993,7 +17063,7 @@ function runHarness(opts) {
|
|
|
16993
17063
|
// src/update.ts
|
|
16994
17064
|
import { execFile } from "child_process";
|
|
16995
17065
|
import { createRequire } from "module";
|
|
16996
|
-
import { dirname as dirname3, join as
|
|
17066
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
16997
17067
|
import { promisify } from "util";
|
|
16998
17068
|
var SERVICE_LABEL = "ai.paigy.harness";
|
|
16999
17069
|
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -17016,7 +17086,7 @@ async function latestVersion() {
|
|
|
17016
17086
|
}
|
|
17017
17087
|
async function install(version) {
|
|
17018
17088
|
const bin = dirname3(process.execPath);
|
|
17019
|
-
await promisify(execFile)(
|
|
17089
|
+
await promisify(execFile)(join5(bin, "npm"), ["install", "-g", `@paigy/harness@${version}`], {
|
|
17020
17090
|
env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ""}`, npm_config_update_notifier: "false" },
|
|
17021
17091
|
timeout: 5 * 6e4
|
|
17022
17092
|
});
|
|
@@ -17050,7 +17120,7 @@ function selfUpdater(deps) {
|
|
|
17050
17120
|
}
|
|
17051
17121
|
|
|
17052
17122
|
// src/host.ts
|
|
17053
|
-
var HOST_FILE =
|
|
17123
|
+
var HOST_FILE = join6(homedir6(), ".paigy", "host.json");
|
|
17054
17124
|
function readHostState() {
|
|
17055
17125
|
try {
|
|
17056
17126
|
const s = JSON.parse(readFileSync3(HOST_FILE, "utf8"));
|
|
@@ -17074,8 +17144,8 @@ var claimedRun = (sessionId2) => {
|
|
|
17074
17144
|
const slot = sessionSlot(sessionId2);
|
|
17075
17145
|
return { slot, key: runKey(slot) };
|
|
17076
17146
|
};
|
|
17077
|
-
async function slotHasWaitingWork(token) {
|
|
17078
|
-
return (await checkReplies({ token })).deliveries.length > 0;
|
|
17147
|
+
async function slotHasWaitingWork(token, session) {
|
|
17148
|
+
return (await checkReplies({ token, ...session ? { reach: reachAs(session) } : {} })).deliveries.length > 0;
|
|
17079
17149
|
}
|
|
17080
17150
|
var PRESENCE_FRESH_MS = 3 * 6e4;
|
|
17081
17151
|
function wakeAction(live, now, serverSeenAt) {
|
|
@@ -17088,6 +17158,7 @@ function startHost(opts) {
|
|
|
17088
17158
|
const asHost = { token: opts.token };
|
|
17089
17159
|
const refreshIdentities = async () => {
|
|
17090
17160
|
for (const slot of listSlots()) {
|
|
17161
|
+
if (slot === "Desktop") continue;
|
|
17091
17162
|
const token = readToken(slot);
|
|
17092
17163
|
if (!token) continue;
|
|
17093
17164
|
const me = await whoAmI({ token }).catch(() => null);
|
|
@@ -17167,9 +17238,11 @@ function startHost(opts) {
|
|
|
17167
17238
|
if (wakeAction(!!live, Date.now(), seenByServer) === "skip") return;
|
|
17168
17239
|
const token = readToken(slot);
|
|
17169
17240
|
if (!token) return;
|
|
17170
|
-
const
|
|
17241
|
+
const owner = ownerSession(slot);
|
|
17242
|
+
if (owner && !allowed(owner.cwd, opts.wsDeps)) return;
|
|
17243
|
+
const workspace = owner?.cwd ?? resolveWakeDir(slotIdentity(slot).workspace, opts.wsDeps);
|
|
17171
17244
|
if (!workspace) return;
|
|
17172
|
-
if (!await slotHasWaitingWork(token).catch(() => false)) return;
|
|
17245
|
+
if (!await slotHasWaitingWork(token, owner?.sessionId).catch(() => false)) return;
|
|
17173
17246
|
const label = slotName(slot) ?? slot;
|
|
17174
17247
|
const log2 = (line) => opts.log(`[${label}] ${line}`);
|
|
17175
17248
|
const run = runHarness({
|
|
@@ -17178,6 +17251,7 @@ function startHost(opts) {
|
|
|
17178
17251
|
mode: "bypass",
|
|
17179
17252
|
prompt: "",
|
|
17180
17253
|
token,
|
|
17254
|
+
...owner ? { resume: owner.sessionId } : {},
|
|
17181
17255
|
// A dead run must not squat the slot — evict so the next wake can respawn.
|
|
17182
17256
|
onExit: () => {
|
|
17183
17257
|
runs.delete(key);
|
|
@@ -17188,7 +17262,7 @@ function startHost(opts) {
|
|
|
17188
17262
|
runs.set(key, { run, label, token });
|
|
17189
17263
|
void heartbeat(void 0, { token }).catch(() => {
|
|
17190
17264
|
});
|
|
17191
|
-
opts.log(`\u25B6 woke ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
17265
|
+
opts.log(`\u25B6 ${owner ? "resumed" : "woke"} ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
17192
17266
|
}
|
|
17193
17267
|
}
|
|
17194
17268
|
const listening = /* @__PURE__ */ new Map();
|
|
@@ -17497,7 +17571,7 @@ function staleWhy(note) {
|
|
|
17497
17571
|
|
|
17498
17572
|
// src/triage/judge.ts
|
|
17499
17573
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
17500
|
-
import { homedir as
|
|
17574
|
+
import { homedir as homedir7 } from "os";
|
|
17501
17575
|
import { delimiter as delimiter3 } from "path";
|
|
17502
17576
|
var PRIVACY = {
|
|
17503
17577
|
ollama: "never leaves this machine",
|
|
@@ -17601,7 +17675,7 @@ async function judgeFor(asked, deps = {}) {
|
|
|
17601
17675
|
const out = spawnSync2(bin, runner.argv(null), {
|
|
17602
17676
|
input: prompt,
|
|
17603
17677
|
encoding: "utf8",
|
|
17604
|
-
cwd:
|
|
17678
|
+
cwd: homedir7(),
|
|
17605
17679
|
timeout: RUN_TIMEOUT_MS,
|
|
17606
17680
|
maxBuffer: 32 * 1024 * 1024,
|
|
17607
17681
|
env: { ...process.env, PATH: [process.env["PATH"], ...probeDirs()].filter(Boolean).join(delimiter3) }
|
|
@@ -17899,7 +17973,7 @@ var win = null;
|
|
|
17899
17973
|
function log(message) {
|
|
17900
17974
|
win?.webContents.send("paigy:log", message);
|
|
17901
17975
|
}
|
|
17902
|
-
var ICON =
|
|
17976
|
+
var ICON = join7(here, "..", "assets", "icon.png");
|
|
17903
17977
|
function createWindow() {
|
|
17904
17978
|
win = new BrowserWindow({
|
|
17905
17979
|
// Sized for the working log, which is now the window's centerpiece; the layout is a
|
|
@@ -17911,9 +17985,9 @@ function createWindow() {
|
|
|
17911
17985
|
minHeight: 520,
|
|
17912
17986
|
icon: ICON,
|
|
17913
17987
|
// Windows/Linux window icon; macOS uses the dock icon below
|
|
17914
|
-
webPreferences: { preload:
|
|
17988
|
+
webPreferences: { preload: join7(here, "preload.cjs"), contextIsolation: true, nodeIntegration: false }
|
|
17915
17989
|
});
|
|
17916
|
-
void win.loadFile(
|
|
17990
|
+
void win.loadFile(join7(here, "..", "renderer", "index.html"));
|
|
17917
17991
|
}
|
|
17918
17992
|
var host = null;
|
|
17919
17993
|
app.whenReady().then(() => {
|
|
@@ -17982,7 +18056,7 @@ ipcMain.handle("paigy:agent", async (_e, name) => {
|
|
|
17982
18056
|
});
|
|
17983
18057
|
ipcMain.handle("paigy:agent-log", (_e, name) => {
|
|
17984
18058
|
try {
|
|
17985
|
-
const all = readFileSync4(
|
|
18059
|
+
const all = readFileSync4(join7(homedir8(), ".paigy", "host.log"), "utf8").split("\n");
|
|
17986
18060
|
const mine = all.filter((l) => l.includes(`[${name}]`) || /[▶✖◉⚠]/.test(l) && l.includes(name));
|
|
17987
18061
|
return mine.slice(-500).join("\n");
|
|
17988
18062
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/harness",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "Run Claude Code / Codex on this machine, bridged to Paigy — launchable from your phone.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/main.js",
|
|
8
8
|
"bin": {
|
|
9
9
|
"paigy-harness": "dist/cli.js",
|
|
10
|
-
"paigy": "dist/cli.js"
|
|
10
|
+
"paigy": "dist/cli.js",
|
|
11
|
+
"paigy-agy-acp": "dist/agy-acp.js"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsup",
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
},
|
|
33
34
|
"files": [
|
|
34
35
|
"dist/cli.js",
|
|
36
|
+
"dist/agy-acp.js",
|
|
35
37
|
"README.md"
|
|
36
38
|
],
|
|
37
39
|
"publishConfig": {
|