@paigy/harness 0.3.10 → 0.3.11

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.
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
3
+
4
+ // src/harness/agy-acp.ts
5
+ import { spawn } from "child_process";
6
+ import { randomUUID } from "crypto";
7
+ import { createInterface } from "readline";
8
+ var frame = (body) => JSON.stringify({ jsonrpc: "2.0", ...body });
9
+ function agyArgs(bypass) {
10
+ return [
11
+ "--input-format",
12
+ "stream-json",
13
+ "--output-format",
14
+ "stream-json",
15
+ ...bypass ? ["--dangerously-skip-permissions"] : [],
16
+ "--print="
17
+ ];
18
+ }
19
+ var AgyAcp = class {
20
+ sessionId;
21
+ cwd = process.cwd();
22
+ bypass = false;
23
+ started = false;
24
+ promptId;
25
+ /** A frame from the harness. */
26
+ fromClient(msg) {
27
+ const out = { client: [], agy: [] };
28
+ if (msg.method === void 0 || msg.id === void 0) return out;
29
+ switch (msg.method) {
30
+ case "initialize":
31
+ out.client.push(frame({ id: msg.id, result: { protocolVersion: 2, agentCapabilities: {}, authMethods: [] } }));
32
+ return out;
33
+ case "session/new":
34
+ this.sessionId = randomUUID();
35
+ if (typeof msg.params?.cwd === "string") this.cwd = msg.params.cwd;
36
+ out.client.push(frame({ id: msg.id, result: { sessionId: this.sessionId } }));
37
+ return out;
38
+ case "session/set_mode":
39
+ case "session/set_config_option": {
40
+ const value = msg.params?.modeId ?? msg.params?.value;
41
+ if (typeof value === "string") this.bypass = value === "bypassPermissions";
42
+ out.client.push(frame({ id: msg.id, result: {} }));
43
+ return out;
44
+ }
45
+ case "session/prompt": {
46
+ const blocks = msg.params?.prompt ?? [];
47
+ const content = blocks.map((b) => b.type === "text" ? b.text ?? "" : "").filter(Boolean).join("\n\n");
48
+ if (!this.started) {
49
+ this.started = true;
50
+ out.start = { cwd: this.cwd, args: agyArgs(this.bypass) };
51
+ }
52
+ this.promptId = msg.id;
53
+ out.agy.push(JSON.stringify({ event: "user", message: { role: "user", content } }));
54
+ return out;
55
+ }
56
+ default:
57
+ out.client.push(frame({ id: msg.id, error: { code: -32601, message: `Method not found: ${msg.method}` } }));
58
+ return out;
59
+ }
60
+ }
61
+ /** A line from `agy`. */
62
+ fromAgy(line) {
63
+ const out = { client: [], agy: [] };
64
+ let ev;
65
+ try {
66
+ ev = JSON.parse(line);
67
+ } catch {
68
+ return out;
69
+ }
70
+ const update = (body) => out.client.push(frame({ method: "session/update", params: { sessionId: this.sessionId, update: body } }));
71
+ if (ev.event === "step_update") {
72
+ const s = ev.step_update ?? {};
73
+ if (s.step_type === "agent_response" && typeof s.text_delta === "string" && s.text_delta) {
74
+ update({ sessionUpdate: "agent_message_chunk", content: { type: "text", text: s.text_delta } });
75
+ } else if (s.step_type === "tool" && s.state === "ACTIVE" && typeof s.tool_name === "string") {
76
+ update({ sessionUpdate: "tool_call", title: s.tool_name, kind: s.tool_name });
77
+ }
78
+ return out;
79
+ }
80
+ if (ev.event === "result" && this.promptId !== void 0) {
81
+ const r = ev.result ?? {};
82
+ const denied = Array.isArray(r.denied_actions) ? r.denied_actions : [];
83
+ if (denied.length) {
84
+ const names = denied.map((d) => d.display_name ?? d.action ?? "a tool").join(", ");
85
+ update({ sessionUpdate: "agent_message_chunk", content: {
86
+ type: "text",
87
+ text: `
88
+
89
+ Antigravity cannot ask for permission when it runs in the background, so it was denied: ${names}.`
90
+ } });
91
+ }
92
+ const id = this.promptId;
93
+ this.promptId = void 0;
94
+ out.client.push(r.status === "SUCCESS" ? frame({ id, result: { stopReason: "end_turn" } }) : frame({ id, error: { code: -32e3, message: String(r.error ?? r.status ?? "agy turn failed") } }));
95
+ }
96
+ return out;
97
+ }
98
+ /** `agy` exited: a turn still open fails by name rather than hanging the harness. */
99
+ exited(code) {
100
+ const out = { client: [], agy: [] };
101
+ if (this.promptId !== void 0) {
102
+ out.client.push(frame({ id: this.promptId, error: { code: -32e3, message: `agy exited ${code ?? "by signal"}` } }));
103
+ this.promptId = void 0;
104
+ }
105
+ return out;
106
+ }
107
+ };
108
+ function main() {
109
+ const adapter = new AgyAcp();
110
+ let agy;
111
+ const apply = (out) => {
112
+ for (const f of out.client) process.stdout.write(`${f}
113
+ `);
114
+ if (out.start) {
115
+ agy = spawn("agy", out.start.args, { cwd: out.start.cwd, stdio: ["pipe", "pipe", "pipe"], env: process.env });
116
+ createInterface({ input: agy.stdout }).on("line", (l) => apply(adapter.fromAgy(l)));
117
+ agy.stderr.on("data", (chunk) => process.stderr.write(chunk));
118
+ agy.on("exit", (code) => {
119
+ apply(adapter.exited(code));
120
+ process.exit(code ?? 1);
121
+ });
122
+ agy.on("error", (e) => {
123
+ process.stderr.write(`agy: ${e.message}
124
+ `);
125
+ apply(adapter.exited(null));
126
+ process.exit(1);
127
+ });
128
+ }
129
+ for (const l of out.agy) agy?.stdin?.write(`${l}
130
+ `);
131
+ };
132
+ createInterface({ input: process.stdin }).on("line", (line) => {
133
+ try {
134
+ apply(adapter.fromClient(JSON.parse(line)));
135
+ } catch {
136
+ }
137
+ }).on("close", () => {
138
+ agy?.stdin?.end();
139
+ if (!agy) process.exit(0);
140
+ });
141
+ }
142
+
143
+ // src/agy-acp.ts
144
+ main();
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"
@@ -23893,7 +23895,7 @@ async function listNotes(opts = {}) {
23893
23895
  }
23894
23896
  async function listConnections(opts = {}) {
23895
23897
  const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
23896
- headers: { authorization: `Bearer ${authToken(opts.token)}` }
23898
+ headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
23897
23899
  }));
23898
23900
  if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
23899
23901
  return await res.json();
@@ -23940,9 +23942,7 @@ async function contact(input, opts = {}) {
23940
23942
  const read = async (signal2) => {
23941
23943
  const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
23942
23944
  if (!res.ok) await fail("read_delivery", res);
23943
- const delivery = await res.json();
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;
23945
+ return await res.json();
23946
23946
  };
23947
23947
  const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
23948
23948
  if (opts.waits === false) return read(opts.signal);
@@ -24062,7 +24062,7 @@ async function subscribeWake(onNudge, opts = {}) {
24062
24062
  }
24063
24063
  };
24064
24064
  }
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, StatusSchema, 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;
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, 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
24066
  var init_dist = __esm({
24067
24067
  "../../packages/sdk/dist/index.js"() {
24068
24068
  "use strict";
@@ -26774,10 +26774,10 @@ var init_dist = __esm({
26774
26774
  UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
26775
26775
  ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
26776
26776
  GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
26777
- GET_GOAL_DESCRIPTION = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
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 new revision and a prose summary.";
26779
- CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
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, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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.";
26777
+ 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.";
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 Goal as get_goal reads it, at its new revision.";
26779
+ 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.";
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, 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
26781
  CheckRepliesSchema = external_exports.object({}).strict();
26782
26782
  GetThreadSchema = external_exports.object({
26783
26783
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -27718,25 +27718,6 @@ var init_dist = __esm({
27718
27718
  * carries credentials; only a `poll` registration can come back with null here. */
27719
27719
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
27720
27720
  });
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
27721
  EnvelopeRecipientSchema = external_exports.object({
27741
27722
  keyId: external_exports.string(),
27742
27723
  epk: external_exports.string(),
@@ -33379,7 +33360,7 @@ init_catalog();
33379
33360
  var ADAPTER_BIN = {
33380
33361
  claude: "claude-agent-acp",
33381
33362
  codex: "codex-acp",
33382
- agy: "agy"
33363
+ agy: "paigy-agy-acp"
33383
33364
  };
33384
33365
  function splitLines(buffer, chunk) {
33385
33366
  const combined = buffer + chunk;
@@ -34842,10 +34823,10 @@ var UpdateGoalSchema2 = external_exports.object({
34842
34823
  var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
34843
34824
  var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
34844
34825
  var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
34845
- var GET_GOAL_DESCRIPTION2 = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
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 new revision and a prose summary.";
34847
- var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
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, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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.";
34826
+ 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.";
34827
+ 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 Goal as get_goal reads it, at its new revision.";
34828
+ 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.";
34829
+ 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
34830
  var CheckRepliesSchema2 = external_exports.object({}).strict();
34850
34831
  var GetThreadSchema2 = external_exports.object({
34851
34832
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -35799,25 +35780,6 @@ var DeliveryConfigSchema2 = external_exports.object({
35799
35780
  * carries credentials; only a `poll` registration can come back with null here. */
35800
35781
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
35801
35782
  });
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
35783
  var EnvelopeRecipientSchema2 = external_exports.object({
35822
35784
  keyId: external_exports.string(),
35823
35785
  epk: external_exports.string(),
@@ -36004,7 +35966,9 @@ async function step(session, state, rail) {
36004
35966
  unheard(brief2, state);
36005
35967
  if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
36006
35968
  else state.goal = { id: brief2.goalId, revision: brief2.revision };
36007
- send(session, state, brief2.message);
35969
+ const outcome = brief2.outcome?.trim() ?? "";
35970
+ const said = (brief2.entries ?? []).map((e) => entryWords(e).trim()).filter((w) => w && w.toLowerCase() !== outcome.toLowerCase());
35971
+ send(session, state, [outcome, ...said].filter(Boolean).join("\n\n"));
36008
35972
  return;
36009
35973
  }
36010
35974
  const brief = await rail.read(state.goal.id);
@@ -36359,6 +36323,7 @@ function startHost(opts) {
36359
36323
  const asHost = { token: opts.token };
36360
36324
  const refreshIdentities = async () => {
36361
36325
  for (const slot of listSlots()) {
36326
+ if (slot === "Desktop") continue;
36362
36327
  const token = readToken(slot);
36363
36328
  if (!token) continue;
36364
36329
  const me = await whoAmI({ token }).catch(() => null);
package/dist/main.js CHANGED
@@ -12234,10 +12234,10 @@ var UpdateGoalSchema = external_exports.object({
12234
12234
  var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
12235
12235
  var ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
12236
12236
  var GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
12237
- var GET_GOAL_DESCRIPTION = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
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 new revision and a prose summary.";
12239
- var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
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, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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.";
12237
+ 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.";
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 Goal as get_goal reads it, at its new revision.";
12239
+ 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.";
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, 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
12241
  var CheckRepliesSchema = external_exports.object({}).strict();
12242
12242
  var GetThreadSchema = external_exports.object({
12243
12243
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -13233,25 +13233,6 @@ var DeliveryConfigSchema = external_exports.object({
13233
13233
  * carries credentials; only a `poll` registration can come back with null here. */
13234
13234
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
13235
13235
  });
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
13236
  var EnvelopeRecipientSchema = external_exports.object({
13256
13237
  keyId: external_exports.string(),
13257
13238
  epk: external_exports.string(),
@@ -13593,7 +13574,7 @@ async function listNotes(opts = {}) {
13593
13574
  }
13594
13575
  async function listConnections(opts = {}) {
13595
13576
  const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
13596
- headers: { authorization: `Bearer ${authToken(opts.token)}` }
13577
+ headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
13597
13578
  }));
13598
13579
  if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
13599
13580
  return await res.json();
@@ -13655,9 +13636,7 @@ async function contact(input, opts = {}) {
13655
13636
  const read = async (signal2) => {
13656
13637
  const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
13657
13638
  if (!res.ok) await fail("read_delivery", res);
13658
- const delivery = await res.json();
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;
13639
+ return await res.json();
13661
13640
  };
13662
13641
  const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
13663
13642
  if (opts.waits === false) return read(opts.signal);
@@ -13816,6 +13795,8 @@ var CATALOG = [
13816
13795
  name: "agy",
13817
13796
  label: "Antigravity",
13818
13797
  cli: "agy",
13798
+ // `agy` has no ACP mode; its adapter ships inside the harness (`harness/agy-acp.ts`).
13799
+ adapter: "paigy-agy-acp",
13819
13800
  authProbe: ["agy", "help"],
13820
13801
  loginHint: "Install and set up Antigravity (`agy`).",
13821
13802
  installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
@@ -15305,10 +15286,10 @@ var UpdateGoalSchema2 = external_exports.object({
15305
15286
  var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
15306
15287
  var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
15307
15288
  var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
15308
- var GET_GOAL_DESCRIPTION2 = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
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 new revision and a prose summary.";
15310
- var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
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, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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.";
15289
+ 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.";
15290
+ 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 Goal as get_goal reads it, at its new revision.";
15291
+ 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.";
15292
+ 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
15293
  var CheckRepliesSchema2 = external_exports.object({}).strict();
15313
15294
  var GetThreadSchema2 = external_exports.object({
15314
15295
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -16262,25 +16243,6 @@ var DeliveryConfigSchema2 = external_exports.object({
16262
16243
  * carries credentials; only a `poll` registration can come back with null here. */
16263
16244
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
16264
16245
  });
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
16246
  var EnvelopeRecipientSchema2 = external_exports.object({
16285
16247
  keyId: external_exports.string(),
16286
16248
  epk: external_exports.string(),
@@ -16657,7 +16619,7 @@ var frame2 = (body) => JSON.stringify({ jsonrpc: "2.0", ...body });
16657
16619
  var ADAPTER_BIN = {
16658
16620
  claude: "claude-agent-acp",
16659
16621
  codex: "codex-acp",
16660
- agy: "agy"
16622
+ agy: "paigy-agy-acp"
16661
16623
  };
16662
16624
  function splitLines(buffer, chunk) {
16663
16625
  const combined = buffer + chunk;
@@ -16759,7 +16721,9 @@ async function step(session, state, rail) {
16759
16721
  unheard(brief2, state);
16760
16722
  if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
16761
16723
  else state.goal = { id: brief2.goalId, revision: brief2.revision };
16762
- send(session, state, brief2.message);
16724
+ const outcome = brief2.outcome?.trim() ?? "";
16725
+ const said = (brief2.entries ?? []).map((e) => entryWords(e).trim()).filter((w) => w && w.toLowerCase() !== outcome.toLowerCase());
16726
+ send(session, state, [outcome, ...said].filter(Boolean).join("\n\n"));
16763
16727
  return;
16764
16728
  }
16765
16729
  const brief = await rail.read(state.goal.id);
@@ -17088,6 +17052,7 @@ function startHost(opts) {
17088
17052
  const asHost = { token: opts.token };
17089
17053
  const refreshIdentities = async () => {
17090
17054
  for (const slot of listSlots()) {
17055
+ if (slot === "Desktop") continue;
17091
17056
  const token = readToken(slot);
17092
17057
  if (!token) continue;
17093
17058
  const me = await whoAmI({ token }).catch(() => null);
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@paigy/harness",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
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": {