@paigy/harness 0.3.9 → 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";
@@ -26765,7 +26765,8 @@ var init_dist = __esm({
26765
26765
  children: external_exports.array(external_exports.object({ outcome: external_exports.string().trim().min(1).max(1e4), ownerParticipant: external_exports.string().trim().min(1), gate: external_exports.enum(["start", "finish"]).optional() }).strict()).optional(),
26766
26766
  state: external_exports.enum(["active", "done", "cancelled"]).optional(),
26767
26767
  progress: external_exports.string().trim().min(1).max(1e4).optional(),
26768
- reviewed: external_exports.literal(true).optional()
26768
+ reviewed: external_exports.literal(true).optional(),
26769
+ dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
26769
26770
  }).strict().refine((v) => Object.keys(v).length > 0),
26770
26771
  reason: external_exports.string().trim().min(1).max(2e3),
26771
26772
  operationId: external_exports.string().uuid().optional()
@@ -26773,10 +26774,10 @@ var init_dist = __esm({
26773
26774
  UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
26774
26775
  ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
26775
26776
  GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
26776
- 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.";
26777
- 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. Returns the new revision and a prose summary.";
26778
- 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.";
26779
- 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.";
26780
26781
  CheckRepliesSchema = external_exports.object({}).strict();
26781
26782
  GetThreadSchema = external_exports.object({
26782
26783
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -27717,25 +27718,6 @@ var init_dist = __esm({
27717
27718
  * carries credentials; only a `poll` registration can come back with null here. */
27718
27719
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
27719
27720
  });
27720
- StatusSchema = external_exports.object({
27721
- name: external_exports.string(),
27722
- sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
27723
- /** A phone is registered for push/ring (any push token on the account). */
27724
- phone: external_exports.boolean(),
27725
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
27726
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
27727
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
27728
- * zero while the sweep sees one is two ideas of "waiting".
27729
- *
27730
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
27731
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
27732
- * used to paper over that by spawning a SECOND process on the identity; now it stands
27733
- * back, correctly, and the person sitting at the terminal is the one who can act. A
27734
- * coffee-beans request sat unread for three days.
27735
- *
27736
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
27737
- waiting: external_exports.number().int().nonnegative().optional()
27738
- });
27739
27721
  EnvelopeRecipientSchema = external_exports.object({
27740
27722
  keyId: external_exports.string(),
27741
27723
  epk: external_exports.string(),
@@ -33378,7 +33360,7 @@ init_catalog();
33378
33360
  var ADAPTER_BIN = {
33379
33361
  claude: "claude-agent-acp",
33380
33362
  codex: "codex-acp",
33381
- agy: "agy"
33363
+ agy: "paigy-agy-acp"
33382
33364
  };
33383
33365
  function splitLines(buffer, chunk) {
33384
33366
  const combined = buffer + chunk;
@@ -34832,7 +34814,8 @@ var UpdateGoalSchema2 = external_exports.object({
34832
34814
  children: external_exports.array(external_exports.object({ outcome: external_exports.string().trim().min(1).max(1e4), ownerParticipant: external_exports.string().trim().min(1), gate: external_exports.enum(["start", "finish"]).optional() }).strict()).optional(),
34833
34815
  state: external_exports.enum(["active", "done", "cancelled"]).optional(),
34834
34816
  progress: external_exports.string().trim().min(1).max(1e4).optional(),
34835
- reviewed: external_exports.literal(true).optional()
34817
+ reviewed: external_exports.literal(true).optional(),
34818
+ dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
34836
34819
  }).strict().refine((v) => Object.keys(v).length > 0),
34837
34820
  reason: external_exports.string().trim().min(1).max(2e3),
34838
34821
  operationId: external_exports.string().uuid().optional()
@@ -34840,10 +34823,10 @@ var UpdateGoalSchema2 = external_exports.object({
34840
34823
  var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
34841
34824
  var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
34842
34825
  var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
34843
- 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.";
34844
- 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. Returns the new revision and a prose summary.";
34845
- 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.";
34846
- 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.";
34847
34830
  var CheckRepliesSchema2 = external_exports.object({}).strict();
34848
34831
  var GetThreadSchema2 = external_exports.object({
34849
34832
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -35797,25 +35780,6 @@ var DeliveryConfigSchema2 = external_exports.object({
35797
35780
  * carries credentials; only a `poll` registration can come back with null here. */
35798
35781
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
35799
35782
  });
35800
- var StatusSchema2 = external_exports.object({
35801
- name: external_exports.string(),
35802
- sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
35803
- /** A phone is registered for push/ring (any push token on the account). */
35804
- phone: external_exports.boolean(),
35805
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
35806
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
35807
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
35808
- * zero while the sweep sees one is two ideas of "waiting".
35809
- *
35810
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
35811
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
35812
- * used to paper over that by spawning a SECOND process on the identity; now it stands
35813
- * back, correctly, and the person sitting at the terminal is the one who can act. A
35814
- * coffee-beans request sat unread for three days.
35815
- *
35816
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
35817
- waiting: external_exports.number().int().nonnegative().optional()
35818
- });
35819
35783
  var EnvelopeRecipientSchema2 = external_exports.object({
35820
35784
  keyId: external_exports.string(),
35821
35785
  epk: external_exports.string(),
@@ -36002,7 +35966,9 @@ async function step(session, state, rail) {
36002
35966
  unheard(brief2, state);
36003
35967
  if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
36004
35968
  else state.goal = { id: brief2.goalId, revision: brief2.revision };
36005
- 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"));
36006
35972
  return;
36007
35973
  }
36008
35974
  const brief = await rail.read(state.goal.id);
@@ -36357,6 +36323,7 @@ function startHost(opts) {
36357
36323
  const asHost = { token: opts.token };
36358
36324
  const refreshIdentities = async () => {
36359
36325
  for (const slot of listSlots()) {
36326
+ if (slot === "Desktop") continue;
36360
36327
  const token = readToken(slot);
36361
36328
  if (!token) continue;
36362
36329
  const me = await whoAmI({ token }).catch(() => null);
package/dist/main.js CHANGED
@@ -12225,7 +12225,8 @@ var UpdateGoalSchema = external_exports.object({
12225
12225
  children: external_exports.array(external_exports.object({ outcome: external_exports.string().trim().min(1).max(1e4), ownerParticipant: external_exports.string().trim().min(1), gate: external_exports.enum(["start", "finish"]).optional() }).strict()).optional(),
12226
12226
  state: external_exports.enum(["active", "done", "cancelled"]).optional(),
12227
12227
  progress: external_exports.string().trim().min(1).max(1e4).optional(),
12228
- reviewed: external_exports.literal(true).optional()
12228
+ reviewed: external_exports.literal(true).optional(),
12229
+ dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
12229
12230
  }).strict().refine((v) => Object.keys(v).length > 0),
12230
12231
  reason: external_exports.string().trim().min(1).max(2e3),
12231
12232
  operationId: external_exports.string().uuid().optional()
@@ -12233,10 +12234,10 @@ var UpdateGoalSchema = external_exports.object({
12233
12234
  var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
12234
12235
  var ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
12235
12236
  var GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
12236
- 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.";
12237
- 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. Returns the new revision and a prose summary.";
12238
- 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.";
12239
- 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.";
12240
12241
  var CheckRepliesSchema = external_exports.object({}).strict();
12241
12242
  var GetThreadSchema = external_exports.object({
12242
12243
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -13232,25 +13233,6 @@ var DeliveryConfigSchema = external_exports.object({
13232
13233
  * carries credentials; only a `poll` registration can come back with null here. */
13233
13234
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
13234
13235
  });
13235
- var StatusSchema = external_exports.object({
13236
- name: external_exports.string(),
13237
- sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
13238
- /** A phone is registered for push/ring (any push token on the account). */
13239
- phone: external_exports.boolean(),
13240
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
13241
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
13242
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
13243
- * zero while the sweep sees one is two ideas of "waiting".
13244
- *
13245
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
13246
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
13247
- * used to paper over that by spawning a SECOND process on the identity; now it stands
13248
- * back, correctly, and the person sitting at the terminal is the one who can act. A
13249
- * coffee-beans request sat unread for three days.
13250
- *
13251
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
13252
- waiting: external_exports.number().int().nonnegative().optional()
13253
- });
13254
13236
  var EnvelopeRecipientSchema = external_exports.object({
13255
13237
  keyId: external_exports.string(),
13256
13238
  epk: external_exports.string(),
@@ -13592,7 +13574,7 @@ async function listNotes(opts = {}) {
13592
13574
  }
13593
13575
  async function listConnections(opts = {}) {
13594
13576
  const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
13595
- headers: { authorization: `Bearer ${authToken(opts.token)}` }
13577
+ headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
13596
13578
  }));
13597
13579
  if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
13598
13580
  return await res.json();
@@ -13654,9 +13636,7 @@ async function contact(input, opts = {}) {
13654
13636
  const read = async (signal2) => {
13655
13637
  const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
13656
13638
  if (!res.ok) await fail("read_delivery", res);
13657
- const delivery = await res.json();
13658
- return delivery.kind === "notification" ? { ...delivery, message: `${delivery.message}
13659
- Inbox delivery is asynchronous; use claim_goal/get_goal to collect durable answers. Do not poll this Notification.` } : delivery;
13639
+ return await res.json();
13660
13640
  };
13661
13641
  const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
13662
13642
  if (opts.waits === false) return read(opts.signal);
@@ -13815,6 +13795,8 @@ var CATALOG = [
13815
13795
  name: "agy",
13816
13796
  label: "Antigravity",
13817
13797
  cli: "agy",
13798
+ // `agy` has no ACP mode; its adapter ships inside the harness (`harness/agy-acp.ts`).
13799
+ adapter: "paigy-agy-acp",
13818
13800
  authProbe: ["agy", "help"],
13819
13801
  loginHint: "Install and set up Antigravity (`agy`).",
13820
13802
  installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
@@ -15295,7 +15277,8 @@ var UpdateGoalSchema2 = external_exports.object({
15295
15277
  children: external_exports.array(external_exports.object({ outcome: external_exports.string().trim().min(1).max(1e4), ownerParticipant: external_exports.string().trim().min(1), gate: external_exports.enum(["start", "finish"]).optional() }).strict()).optional(),
15296
15278
  state: external_exports.enum(["active", "done", "cancelled"]).optional(),
15297
15279
  progress: external_exports.string().trim().min(1).max(1e4).optional(),
15298
- reviewed: external_exports.literal(true).optional()
15280
+ reviewed: external_exports.literal(true).optional(),
15281
+ dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
15299
15282
  }).strict().refine((v) => Object.keys(v).length > 0),
15300
15283
  reason: external_exports.string().trim().min(1).max(2e3),
15301
15284
  operationId: external_exports.string().uuid().optional()
@@ -15303,10 +15286,10 @@ var UpdateGoalSchema2 = external_exports.object({
15303
15286
  var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend({ goalId: external_exports.string().uuid() }).strict();
15304
15287
  var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
15305
15288
  var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
15306
- 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.";
15307
- 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. Returns the new revision and a prose summary.";
15308
- 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.";
15309
- 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.";
15310
15293
  var CheckRepliesSchema2 = external_exports.object({}).strict();
15311
15294
  var GetThreadSchema2 = external_exports.object({
15312
15295
  parentId: external_exports.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -16260,25 +16243,6 @@ var DeliveryConfigSchema2 = external_exports.object({
16260
16243
  * carries credentials; only a `poll` registration can come back with null here. */
16261
16244
  realtime: external_exports.object({ url: external_exports.string(), anonKey: external_exports.string() }).nullable()
16262
16245
  });
16263
- var StatusSchema2 = external_exports.object({
16264
- name: external_exports.string(),
16265
- sessionMode: external_exports.enum(["default", "all_calls", "silent"]),
16266
- /** A phone is registered for push/ring (any push token on the account). */
16267
- phone: external_exports.boolean(),
16268
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
16269
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
16270
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
16271
- * zero while the sweep sees one is two ideas of "waiting".
16272
- *
16273
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
16274
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
16275
- * used to paper over that by spawning a SECOND process on the identity; now it stands
16276
- * back, correctly, and the person sitting at the terminal is the one who can act. A
16277
- * coffee-beans request sat unread for three days.
16278
- *
16279
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
16280
- waiting: external_exports.number().int().nonnegative().optional()
16281
- });
16282
16246
  var EnvelopeRecipientSchema2 = external_exports.object({
16283
16247
  keyId: external_exports.string(),
16284
16248
  epk: external_exports.string(),
@@ -16655,7 +16619,7 @@ var frame2 = (body) => JSON.stringify({ jsonrpc: "2.0", ...body });
16655
16619
  var ADAPTER_BIN = {
16656
16620
  claude: "claude-agent-acp",
16657
16621
  codex: "codex-acp",
16658
- agy: "agy"
16622
+ agy: "paigy-agy-acp"
16659
16623
  };
16660
16624
  function splitLines(buffer, chunk) {
16661
16625
  const combined = buffer + chunk;
@@ -16757,7 +16721,9 @@ async function step(session, state, rail) {
16757
16721
  unheard(brief2, state);
16758
16722
  if (ended(brief2.state)) await rail.update(brief2.goalId, { revision: brief2.revision, changes: { reviewed: true }, reason: "Typed into the session." });
16759
16723
  else state.goal = { id: brief2.goalId, revision: brief2.revision };
16760
- 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"));
16761
16727
  return;
16762
16728
  }
16763
16729
  const brief = await rail.read(state.goal.id);
@@ -17086,6 +17052,7 @@ function startHost(opts) {
17086
17052
  const asHost = { token: opts.token };
17087
17053
  const refreshIdentities = async () => {
17088
17054
  for (const slot of listSlots()) {
17055
+ if (slot === "Desktop") continue;
17089
17056
  const token = readToken(slot);
17090
17057
  if (!token) continue;
17091
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.9",
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": {