@dna113p/machines 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/dist/examples/agy.d.ts +1 -0
- package/dist/examples/agy.js +30 -0
- package/dist/src/acp.js +2 -40
- package/dist/src/agent-protocol.d.ts +6 -0
- package/dist/src/agent-protocol.js +38 -0
- package/dist/src/agy.d.ts +11 -0
- package/dist/src/agy.js +168 -0
- package/dist/src/catalog.js +13 -3
- package/docs/architecture.md +2 -0
- package/docs/authoring.md +40 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ Run it with `node demo.ts`. No build is needed to develop against the source.
|
|
|
63
63
|
| Agent escalation | `npm run example:agents` | Built-in fake runners |
|
|
64
64
|
| Child host and Human response | `npm run example:host` | Node only |
|
|
65
65
|
| Real Agent writes and verifies a file | `npm run example:pi` | Configured Pi through `pi-acp`; may use network and model credits |
|
|
66
|
+
| Real AGY writes and verifies a file | `npm run example:agy` | Configured Antigravity CLI (`agy`); may use network and model credits |
|
|
66
67
|
|
|
67
68
|
## Run your own workflows
|
|
68
69
|
|
|
@@ -140,7 +141,7 @@ npm pack
|
|
|
140
141
|
Then install the generated `dna113p-machines-0.1.0.tgz` into a project:
|
|
141
142
|
|
|
142
143
|
```bash
|
|
143
|
-
npm install /absolute/path/to/machines/dna113p-machines-0.
|
|
144
|
+
npm install /absolute/path/to/machines/dna113p-machines-0.2.0.tgz
|
|
144
145
|
npx machine list
|
|
145
146
|
```
|
|
146
147
|
|
|
@@ -173,6 +174,11 @@ workflow decisions in Human states. The launcher defaults unnamed Agents to
|
|
|
173
174
|
`npx -y pi-acp`; [Agent presets](docs/authoring.md#agent-presets) let you select
|
|
174
175
|
another ACP command or runner explicitly.
|
|
175
176
|
|
|
177
|
+
The built-in `agy` preset selects the Antigravity CLI with AGY permission checks
|
|
178
|
+
enabled. [AGY configuration](docs/authoring.md#antigravity-agy) explains runner
|
|
179
|
+
settings and explicit opt-in to automatic tool approval. `npm run example:agy`
|
|
180
|
+
opts in for its temporary-directory demonstration.
|
|
181
|
+
|
|
176
182
|
## Checks
|
|
177
183
|
|
|
178
184
|
```bash
|
|
@@ -183,4 +189,21 @@ npm run build:plugin
|
|
|
183
189
|
npm run smoke:plugin # exercise a relocated standalone plugin
|
|
184
190
|
```
|
|
185
191
|
|
|
192
|
+
## Releasing
|
|
193
|
+
|
|
194
|
+
The [publishing workflow](.github/workflows/publish.yml) uses a standard GitHub
|
|
195
|
+
runner and npm trusted publishing, without an npm token stored in GitHub.
|
|
196
|
+
Pushing a `vX.Y.Z` tag runs the checks above and publishes to npm if they pass.
|
|
197
|
+
The tag must match the stable version in `package.json`.
|
|
198
|
+
|
|
199
|
+
For example, to release the next minor version from an up-to-date, clean `main`:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm version minor
|
|
203
|
+
git push origin main --follow-tags
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Running **Publish to npm** manually in GitHub Actions performs the checks and a
|
|
207
|
+
publishing dry run; it does not publish a package.
|
|
208
|
+
|
|
186
209
|
Licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mkdtemp, readFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { agent, final, machine, operation, run, } from "../src/index.js";
|
|
5
|
+
import { agyAgent } from "../src/agy.js";
|
|
6
|
+
const workspace = await mkdtemp(join(tmpdir(), "machines-agy-"));
|
|
7
|
+
const helloPath = join(workspace, "hello.txt");
|
|
8
|
+
const example = machine({
|
|
9
|
+
initial: "write",
|
|
10
|
+
states: {
|
|
11
|
+
write: agent("Create hello.txt containing exactly: Hello from Machines.", { completed: "verify" }, { cwd: workspace }),
|
|
12
|
+
verify: operation(async () => {
|
|
13
|
+
const content = await readFile(helloPath, "utf8");
|
|
14
|
+
if (content.trim() !== "Hello from Machines.") {
|
|
15
|
+
throw new Error("hello.txt did not contain the expected text");
|
|
16
|
+
}
|
|
17
|
+
return { type: "passed" };
|
|
18
|
+
}, { passed: "done" }),
|
|
19
|
+
done: final(),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const result = await run(example, {
|
|
23
|
+
agents: {
|
|
24
|
+
// This demo explicitly permits unattended tools in its temporary workspace.
|
|
25
|
+
default: agyAgent("agy", [], { dangerouslySkipPermissions: true }),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
console.log(`agy workspace: ${workspace}`);
|
|
29
|
+
console.log(`hello.txt: ${(await readFile(helloPath, "utf8")).trim()}`);
|
|
30
|
+
console.log(`verified --> ${String(result.value)}`);
|
package/dist/src/acp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { Readable, Writable } from "node:stream";
|
|
3
3
|
import * as acp from "@agentclientprotocol/sdk";
|
|
4
|
+
import { agentPrompt, readAgentEvent } from "./agent-protocol.js";
|
|
4
5
|
export function acpAgent(command, args = [], options = {}) {
|
|
5
6
|
return async (request, report) => {
|
|
6
7
|
if (request.cwd === undefined) {
|
|
@@ -112,7 +113,7 @@ async function runSession(child, request, outputMode, report, harness) {
|
|
|
112
113
|
if (result.stopReason !== "end_turn") {
|
|
113
114
|
throw new Error(`ACP Agent stopped with "${result.stopReason}"`);
|
|
114
115
|
}
|
|
115
|
-
return
|
|
116
|
+
return readAgentEvent(output, { adapter: "ACP", includeMessage: outputMode === "capture" });
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
119
|
function identityUpdate(options, harness) {
|
|
@@ -137,45 +138,6 @@ function identityUpdate(options, harness) {
|
|
|
137
138
|
...(thinking === undefined ? {} : { thinking }),
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
|
-
function agentPrompt(request) {
|
|
141
|
-
return [
|
|
142
|
-
request.prompt,
|
|
143
|
-
"",
|
|
144
|
-
"Complete the work using the available tools.",
|
|
145
|
-
`Allowed outcome types: ${request.outcomes.join(", ")}.`,
|
|
146
|
-
"When finished, end your final response with exactly one line in this form:",
|
|
147
|
-
'MACHINES_EVENT {"type":"completed"}',
|
|
148
|
-
"Replace completed with exactly one allowed outcome type.",
|
|
149
|
-
"Include additional JSON fields only when the task asks for them.",
|
|
150
|
-
].join("\n");
|
|
151
|
-
}
|
|
152
|
-
function returnedEvent(output, includeMessage) {
|
|
153
|
-
const matches = [...output.matchAll(/^MACHINES_EVENT (.+)$/gmu)];
|
|
154
|
-
const encoded = matches.at(-1)?.[1];
|
|
155
|
-
if (encoded === undefined) {
|
|
156
|
-
throw new Error("ACP Agent finished without returning a Machines event");
|
|
157
|
-
}
|
|
158
|
-
let event;
|
|
159
|
-
try {
|
|
160
|
-
event = JSON.parse(encoded);
|
|
161
|
-
}
|
|
162
|
-
catch (cause) {
|
|
163
|
-
throw new Error("ACP Agent returned invalid event JSON", { cause });
|
|
164
|
-
}
|
|
165
|
-
if (event === null
|
|
166
|
-
|| typeof event !== "object"
|
|
167
|
-
|| !("type" in event)
|
|
168
|
-
|| typeof event.type !== "string") {
|
|
169
|
-
throw new Error("ACP Agent returned an invalid Machines event");
|
|
170
|
-
}
|
|
171
|
-
const result = event;
|
|
172
|
-
const message = output
|
|
173
|
-
.replace(/^MACHINES_EVENT .+$(?:\r?\n)?/gmu, "")
|
|
174
|
-
.trim();
|
|
175
|
-
return includeMessage && message !== ""
|
|
176
|
-
? { ...result, message }
|
|
177
|
-
: result;
|
|
178
|
-
}
|
|
179
141
|
function waitForSpawn(child) {
|
|
180
142
|
return new Promise((resolve, reject) => {
|
|
181
143
|
child.once("spawn", resolve);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function agentPrompt(request) {
|
|
2
|
+
return [
|
|
3
|
+
request.prompt,
|
|
4
|
+
"",
|
|
5
|
+
"Complete the work using the available tools.",
|
|
6
|
+
`Allowed outcome types: ${request.outcomes.join(", ")}.`,
|
|
7
|
+
"When finished, end your final response with exactly one line in this form:",
|
|
8
|
+
'MACHINES_EVENT {"type":"completed"}',
|
|
9
|
+
"Replace completed with exactly one allowed outcome type.",
|
|
10
|
+
"Include additional JSON fields only when the task asks for them.",
|
|
11
|
+
].join("\n");
|
|
12
|
+
}
|
|
13
|
+
export function readAgentEvent(output, options) {
|
|
14
|
+
const matches = [...output.matchAll(/^MACHINES_EVENT (.+)$/gmu)];
|
|
15
|
+
const encoded = matches.at(-1)?.[1];
|
|
16
|
+
if (encoded === undefined) {
|
|
17
|
+
throw new Error(`${options.adapter} Agent finished without returning a Machines event`);
|
|
18
|
+
}
|
|
19
|
+
let event;
|
|
20
|
+
try {
|
|
21
|
+
event = JSON.parse(encoded);
|
|
22
|
+
}
|
|
23
|
+
catch (cause) {
|
|
24
|
+
throw new Error(`${options.adapter} Agent returned invalid event JSON`, { cause });
|
|
25
|
+
}
|
|
26
|
+
if (!isEvent(event)) {
|
|
27
|
+
throw new Error(`${options.adapter} Agent returned an invalid Machines event`);
|
|
28
|
+
}
|
|
29
|
+
const message = output.replace(/^MACHINES_EVENT .+$(?:\r?\n)?/gmu, "").trim();
|
|
30
|
+
return options.includeMessage && message !== "" ? { ...event, message } : event;
|
|
31
|
+
}
|
|
32
|
+
function isEvent(value) {
|
|
33
|
+
return value !== null
|
|
34
|
+
&& typeof value === "object"
|
|
35
|
+
&& !Array.isArray(value)
|
|
36
|
+
&& "type" in value
|
|
37
|
+
&& typeof value.type === "string";
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentRunner } from "./index.ts";
|
|
2
|
+
export interface AgyAgentOptions {
|
|
3
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
4
|
+
readonly harness?: string;
|
|
5
|
+
readonly model?: string;
|
|
6
|
+
readonly effort?: "low" | "medium" | "high";
|
|
7
|
+
/** Explicitly opt in to auto-approving all AGY tool permission requests. */
|
|
8
|
+
readonly dangerouslySkipPermissions?: boolean;
|
|
9
|
+
readonly output?: "capture" | "stream";
|
|
10
|
+
}
|
|
11
|
+
export declare function agyAgent(command?: string, args?: readonly string[], options?: AgyAgentOptions): AgentRunner;
|
package/dist/src/agy.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import * as v from "valibot";
|
|
4
|
+
import { agentPrompt, readAgentEvent } from "./agent-protocol.js";
|
|
5
|
+
export function agyAgent(command = "agy", args = [], options = {}) {
|
|
6
|
+
return async (request, report) => {
|
|
7
|
+
if (request.cwd === undefined) {
|
|
8
|
+
throw new Error("agy Agent requires a working directory");
|
|
9
|
+
}
|
|
10
|
+
if (request.outcomes.length === 0) {
|
|
11
|
+
throw new Error("agy Agent requires at least one allowed outcome");
|
|
12
|
+
}
|
|
13
|
+
const harness = options.harness ?? (command === "agy" ? "agy" : command);
|
|
14
|
+
const env = { ...process.env, ...options.env };
|
|
15
|
+
const model = options.model ?? env.AGY_MODEL ?? env.MACHINES_AGENT_MODEL;
|
|
16
|
+
const effort = options.effort ?? env.AGY_EFFORT ?? env.MACHINES_AGENT_EFFORT;
|
|
17
|
+
const outputMode = options.output ?? "stream";
|
|
18
|
+
report?.({
|
|
19
|
+
type: "identity",
|
|
20
|
+
harness,
|
|
21
|
+
...(model === undefined ? {} : { model }),
|
|
22
|
+
...(effort === undefined ? {} : { thinking: effort }),
|
|
23
|
+
});
|
|
24
|
+
const cliArgs = [
|
|
25
|
+
...args,
|
|
26
|
+
"--print",
|
|
27
|
+
agentPrompt(request),
|
|
28
|
+
"--output-format",
|
|
29
|
+
"stream-json",
|
|
30
|
+
"--disable-slash-commands",
|
|
31
|
+
...(options.dangerouslySkipPermissions === true ? ["--dangerously-skip-permissions"] : []),
|
|
32
|
+
...(model === undefined ? [] : ["--model", model]),
|
|
33
|
+
...(effort === undefined ? [] : ["--effort", effort]),
|
|
34
|
+
];
|
|
35
|
+
const child = spawn(command, cliArgs, {
|
|
36
|
+
cwd: request.cwd,
|
|
37
|
+
env,
|
|
38
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
39
|
+
});
|
|
40
|
+
let stderr = "";
|
|
41
|
+
child.stderr.setEncoding("utf8");
|
|
42
|
+
child.stderr.on("data", (chunk) => {
|
|
43
|
+
stderr += chunk;
|
|
44
|
+
if (outputMode === "stream")
|
|
45
|
+
process.stderr.write(chunk);
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
await waitForSpawn(child);
|
|
49
|
+
return await runSession(child, outputMode, report);
|
|
50
|
+
}
|
|
51
|
+
catch (cause) {
|
|
52
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
53
|
+
const detail = stderr.trim();
|
|
54
|
+
throw new Error(`agy Agent "${command}" failed in "${request.cwd}": ${message}${detail === "" ? "" : `\n${detail}`}`, { cause });
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
child.kill();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function runSession(child, outputMode, report) {
|
|
62
|
+
const exited = new Promise((resolve, reject) => {
|
|
63
|
+
child.once("close", (code, signal) => {
|
|
64
|
+
if (code === 0)
|
|
65
|
+
resolve();
|
|
66
|
+
else
|
|
67
|
+
reject(new Error(signal === null
|
|
68
|
+
? `Process exited with code ${code}`
|
|
69
|
+
: `Process terminated by ${signal}`));
|
|
70
|
+
});
|
|
71
|
+
child.once("error", reject);
|
|
72
|
+
});
|
|
73
|
+
// Observe failures immediately, while stdout may still be draining.
|
|
74
|
+
void exited.catch(() => { });
|
|
75
|
+
const lines = createInterface({ input: child.stdout });
|
|
76
|
+
let displayedOutput = "";
|
|
77
|
+
let finalResponse;
|
|
78
|
+
const emitOutput = (text) => {
|
|
79
|
+
if (text === "")
|
|
80
|
+
return;
|
|
81
|
+
displayedOutput += text;
|
|
82
|
+
report?.({ type: "output", text });
|
|
83
|
+
if (outputMode === "stream")
|
|
84
|
+
process.stdout.write(text);
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
for await (const line of lines) {
|
|
88
|
+
const record = decodeRecord(line);
|
|
89
|
+
if (record === undefined)
|
|
90
|
+
continue;
|
|
91
|
+
if (record.event === "result") {
|
|
92
|
+
finalResponse = record.result.response;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const step = record.step_update;
|
|
96
|
+
if (step.step_type === "agent_response") {
|
|
97
|
+
emitOutput(step.text_delta);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const id = String(step.step_index ?? step.tool_name ?? "tool");
|
|
101
|
+
const title = step.tool_name ?? step.tool_info?.name ?? "tool";
|
|
102
|
+
const status = step.state === "ACTIVE"
|
|
103
|
+
? "in_progress"
|
|
104
|
+
: (step.state === "DONE" ? "completed" : undefined);
|
|
105
|
+
report?.({ type: "tool", id, title, ...(status === undefined ? {} : { status }) });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
await exited;
|
|
110
|
+
// The final response owns the outcome; earlier deltas may contain progress
|
|
111
|
+
// or a preliminary event. Fall back only when no final response was sent.
|
|
112
|
+
const response = finalResponse ?? displayedOutput;
|
|
113
|
+
if (finalResponse !== undefined && !displayedOutput.endsWith(finalResponse)) {
|
|
114
|
+
if (finalResponse.startsWith(displayedOutput)) {
|
|
115
|
+
emitOutput(finalResponse.slice(displayedOutput.length));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const separator = displayedOutput !== "" && !displayedOutput.endsWith("\n") ? "\n" : "";
|
|
119
|
+
emitOutput(separator + finalResponse);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (outputMode === "stream" && displayedOutput !== "" && !displayedOutput.endsWith("\n")) {
|
|
123
|
+
process.stdout.write("\n");
|
|
124
|
+
}
|
|
125
|
+
return readAgentEvent(response, { adapter: "agy", includeMessage: outputMode === "capture" });
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
lines.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Unknown events and malformed records are ignored at the transport boundary.
|
|
132
|
+
// Only these validated fields can reach the harness-neutral AgentUpdate API.
|
|
133
|
+
const recordSchema = v.variant("event", [
|
|
134
|
+
v.object({
|
|
135
|
+
event: v.literal("step_update"),
|
|
136
|
+
step_update: v.variant("step_type", [
|
|
137
|
+
v.object({ step_type: v.literal("agent_response"), text_delta: v.string() }),
|
|
138
|
+
v.object({
|
|
139
|
+
step_type: v.literal("tool"),
|
|
140
|
+
step_index: v.nullish(v.union([v.string(), v.number()])),
|
|
141
|
+
tool_name: v.nullish(v.string()),
|
|
142
|
+
tool_info: v.nullish(v.object({ name: v.nullish(v.string()) })),
|
|
143
|
+
state: v.nullish(v.string()),
|
|
144
|
+
}),
|
|
145
|
+
]),
|
|
146
|
+
}),
|
|
147
|
+
v.object({
|
|
148
|
+
event: v.literal("result"),
|
|
149
|
+
result: v.object({ response: v.string() }),
|
|
150
|
+
}),
|
|
151
|
+
]);
|
|
152
|
+
function decodeRecord(line) {
|
|
153
|
+
let value;
|
|
154
|
+
try {
|
|
155
|
+
value = JSON.parse(line);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const result = v.safeParse(recordSchema, value);
|
|
161
|
+
return result.success ? result.output : undefined;
|
|
162
|
+
}
|
|
163
|
+
function waitForSpawn(child) {
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
child.once("spawn", resolve);
|
|
166
|
+
child.once("error", reject);
|
|
167
|
+
});
|
|
168
|
+
}
|
package/dist/src/catalog.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadAgentBindings } from "./agent-bindings.js";
|
|
2
2
|
import { acpAgent } from "./acp.js";
|
|
3
|
+
import { agyAgent } from "./agy.js";
|
|
3
4
|
import { discoverMachines, machinesUserHome } from "./discovery.js";
|
|
4
5
|
import { loadMachineModule, readAgentRoles, requireMachineDescription } from "./machine-module.js";
|
|
5
6
|
export async function inspectMachines(location = {}) {
|
|
@@ -45,22 +46,31 @@ export async function inspectAgentPresets(location = {}) {
|
|
|
45
46
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
46
47
|
}
|
|
47
48
|
export async function configuredAgentPresets(cwd, home) {
|
|
48
|
-
const configured = await loadAgentBindings(cwd, home, { acpAgent });
|
|
49
|
+
const configured = await loadAgentBindings(cwd, home, { acpAgent, agyAgent });
|
|
49
50
|
const defaultRunner = acpAgent("npx", ["-y", "pi-acp"], {
|
|
50
51
|
harness: "pi-acp",
|
|
51
52
|
output: "capture",
|
|
52
53
|
});
|
|
54
|
+
const agyRunner = agyAgent("agy", [], {
|
|
55
|
+
harness: "agy",
|
|
56
|
+
output: "capture",
|
|
57
|
+
});
|
|
53
58
|
return {
|
|
54
|
-
agents: { default: defaultRunner, ...configured.agents },
|
|
59
|
+
agents: { default: defaultRunner, agy: agyRunner, ...configured.agents },
|
|
55
60
|
presets: {
|
|
56
61
|
default: {
|
|
57
62
|
description: "Runs the current Pi Agent through ACP",
|
|
58
63
|
harness: "pi-acp",
|
|
59
64
|
runner: defaultRunner,
|
|
60
65
|
},
|
|
66
|
+
agy: {
|
|
67
|
+
description: "Runs the Antigravity CLI (agy)",
|
|
68
|
+
harness: "agy",
|
|
69
|
+
runner: agyRunner,
|
|
70
|
+
},
|
|
61
71
|
...configured.presets,
|
|
62
72
|
},
|
|
63
|
-
sources: { default: "built in", ...configured.sources },
|
|
73
|
+
sources: { default: "built in", agy: "built in", ...configured.sources },
|
|
64
74
|
};
|
|
65
75
|
}
|
|
66
76
|
function errorMessage(cause) {
|
package/docs/architecture.md
CHANGED
|
@@ -41,6 +41,8 @@ MCP server ────┘ ↑
|
|
|
41
41
|
| `src/machine-module.ts` | Shared module contract and metadata validation |
|
|
42
42
|
| `src/catalog.ts` | Definition and preset catalog reads |
|
|
43
43
|
| `src/acp.ts` | ACP process/session protocol, bounded Agent result, normalized activity |
|
|
44
|
+
| `src/agy.ts` | Antigravity CLI (agy) process protocol, bounded Agent result, normalized activity |
|
|
45
|
+
| `src/agent-protocol.ts` | Shared Agent prompt instructions and Machines event decoding |
|
|
44
46
|
| `src/discovery.ts` | Project/global definition locations and precedence |
|
|
45
47
|
| `src/launcher.ts` | Definition metadata, Agent bindings, preflight, single-use launch preparation |
|
|
46
48
|
| `src/discovery-worker.ts` | Fresh metadata imports in disposable workers |
|
package/docs/authoring.md
CHANGED
|
@@ -140,7 +140,7 @@ export default ({ acpAgent }: { acpAgent: typeof import("@dna113p/machines/acp")
|
|
|
140
140
|
Install/configure the selected harness and its model credentials first. `acpAgent`
|
|
141
141
|
accepts any compatible ACP command and argument array; the Pi adapter uses ACP as
|
|
142
142
|
its supported execution path. The former `machines/pi` export has been retired;
|
|
143
|
-
use `acpAgent` from
|
|
143
|
+
use `acpAgent` from `@dna113p/machines/acp` for Pi execution.
|
|
144
144
|
|
|
145
145
|
Global presets live in `~/.machines/agents.ts`; project presets replace matching
|
|
146
146
|
global names. Each preset requires a description and runner. Optional `harness`,
|
|
@@ -161,6 +161,45 @@ reports missing bindings or invalid definitions.
|
|
|
161
161
|
`ready` describes valid metadata and resolved roles; it does not verify that a
|
|
162
162
|
real harness is installed, authenticated, or able to run the requested model.
|
|
163
163
|
|
|
164
|
+
### Antigravity (AGY)
|
|
165
|
+
|
|
166
|
+
The built-in `agy` preset runs the installed Antigravity CLI. Select it for the
|
|
167
|
+
default Agent or a declared named role:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
machine run my-workflow --agent default=agy -- "Do the task"
|
|
171
|
+
machine run review-task --agent implementer=agy -- "Implement the task"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The runner keeps AGY's permission checks enabled. It does not forward interactive
|
|
175
|
+
permission prompts through Machines. For an unattended workflow that requires
|
|
176
|
+
automatic approval, configure an explicit preset in `.machines/agents.ts`:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
export default ({ agyAgent }: { agyAgent: typeof import("@dna113p/machines/agy").agyAgent }) => ({
|
|
180
|
+
"agy-unattended": {
|
|
181
|
+
description: "Runs AGY with automatic tool approval for trusted workflows",
|
|
182
|
+
harness: "agy",
|
|
183
|
+
runner: agyAgent("agy", [], {
|
|
184
|
+
dangerouslySkipPermissions: true,
|
|
185
|
+
output: "capture",
|
|
186
|
+
}),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Select that preset with `--agent default=agy-unattended`. The option
|
|
192
|
+
`dangerouslySkipPermissions: true` auto-approves all AGY tool permission requests;
|
|
193
|
+
omitting it or setting it to `false` keeps the runner from adding that flag.
|
|
194
|
+
The `example:agy` demonstration explicitly opts in and asks AGY to work in a
|
|
195
|
+
temporary directory.
|
|
196
|
+
|
|
197
|
+
Runner `model` and `effort` options take precedence over environment settings.
|
|
198
|
+
Otherwise it uses `AGY_MODEL` / `AGY_EFFORT`, falling back to
|
|
199
|
+
`MACHINES_AGENT_MODEL` / `MACHINES_AGENT_EFFORT`. Per-runner `env` values override
|
|
200
|
+
the same variables inherited from the parent process. Effort values are `low`,
|
|
201
|
+
`medium`, or `high`.
|
|
202
|
+
|
|
164
203
|
## Programmatic execution and verification
|
|
165
204
|
|
|
166
205
|
Direct runtime users supply runners through `run(definition, { agents, human })`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dna113p/machines",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Reusable workflows coordinating agents, local operations, and human decisions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"exports": {
|
|
23
23
|
".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
|
|
24
24
|
"./acp": { "types": "./dist/src/acp.d.ts", "import": "./dist/src/acp.js" },
|
|
25
|
+
"./agy": { "types": "./dist/src/agy.d.ts", "import": "./dist/src/agy.js" },
|
|
25
26
|
"./launcher": { "types": "./dist/src/launcher.d.ts", "import": "./dist/src/launcher.js" },
|
|
26
27
|
"./mcp": { "types": "./dist/mcp/server.d.ts", "import": "./dist/mcp/server.js" },
|
|
27
28
|
"./pi-extension": { "types": "./dist/pi-extension/index.d.ts", "import": "./dist/pi-extension/index.js" }
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"check": "npm run typecheck && npm test",
|
|
34
35
|
"example": "node examples/operation.ts",
|
|
35
36
|
"example:agents": "node examples/named-agents.ts",
|
|
37
|
+
"example:agy": "node examples/agy.ts",
|
|
36
38
|
"example:host": "node examples/host.ts",
|
|
37
39
|
"example:pi": "node examples/pi.ts",
|
|
38
40
|
"mcp": "node mcp/server.ts",
|