@dna113p/machines 0.2.0 → 0.3.1
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 +115 -11
- package/claude/statusline.mjs +277 -0
- package/dist/examples/chatgpt-web.d.ts +1 -0
- package/dist/examples/chatgpt-web.js +36 -0
- package/dist/examples/claude.d.ts +1 -0
- package/dist/examples/claude.js +30 -0
- package/dist/examples/codex.d.ts +1 -0
- package/dist/examples/codex.js +30 -0
- package/dist/examples/decision.d.ts +1 -0
- package/dist/examples/decision.js +66 -0
- package/dist/examples/deepseek.d.ts +1 -0
- package/dist/examples/deepseek.js +27 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +74 -11
- package/dist/pi-extension/index.js +16 -9
- package/dist/src/acp.js +2 -1
- package/dist/src/agent-bindings.d.ts +6 -0
- package/dist/src/agent-bindings.js +35 -5
- package/dist/src/agent-limit.d.ts +25 -0
- package/dist/src/agent-limit.js +34 -0
- package/dist/src/agent-limits.d.ts +37 -0
- package/dist/src/agent-limits.js +212 -0
- package/dist/src/agent-protocol.js +3 -1
- package/dist/src/catalog.d.ts +3 -0
- package/dist/src/catalog.js +84 -12
- package/dist/src/chatgpt-web.d.ts +143 -0
- package/dist/src/chatgpt-web.js +946 -0
- package/dist/src/claude.d.ts +35 -0
- package/dist/src/claude.js +294 -0
- package/dist/src/cli.js +282 -16
- package/dist/src/codex.d.ts +16 -0
- package/dist/src/codex.js +176 -0
- package/dist/src/conversation.d.ts +74 -0
- package/dist/src/conversation.js +46 -0
- package/dist/src/coordinator-guidance.d.ts +3 -0
- package/dist/src/coordinator-guidance.js +17 -0
- package/dist/src/decision.d.ts +29 -0
- package/dist/src/decision.js +67 -0
- package/dist/src/deepseek.d.ts +12 -0
- package/dist/src/deepseek.js +16 -0
- package/dist/src/host.d.ts +11 -0
- package/dist/src/host.js +153 -16
- package/dist/src/index.d.ts +39 -2
- package/dist/src/index.js +62 -5
- package/dist/src/jev.d.ts +15 -0
- package/dist/src/jev.js +90 -0
- package/dist/src/json.d.ts +6 -0
- package/dist/src/json.js +28 -0
- package/dist/src/launcher.d.ts +11 -1
- package/dist/src/launcher.js +72 -3
- package/dist/src/laya.d.ts +15 -0
- package/dist/src/laya.js +13 -0
- package/dist/src/openrouter.d.ts +16 -0
- package/dist/src/openrouter.js +97 -0
- package/dist/src/run-inbox.d.ts +49 -0
- package/dist/src/run-inbox.js +233 -0
- package/dist/src/run-status.d.ts +121 -0
- package/dist/src/run-status.js +336 -0
- package/dist/src/run-watch.d.ts +24 -0
- package/dist/src/run-watch.js +112 -0
- package/dist/src/session.d.ts +15 -2
- package/dist/src/session.js +47 -8
- package/dist/src/system-one-http.d.ts +25 -0
- package/dist/src/system-one-http.js +146 -0
- package/dist/src/terminal-human.d.ts +8 -1
- package/dist/src/terminal-human.js +36 -8
- package/dist/src/von.d.ts +15 -0
- package/dist/src/von.js +13 -0
- package/docs/architecture.md +74 -3
- package/docs/authoring.md +809 -1
- package/docs/integrations.md +322 -3
- package/docs/local-decisions.md +212 -0
- package/package.json +21 -4
- package/services/laya/server.py +230 -0
- package/skills/machine-builder/SKILL.md +56 -6
- package/skills/machine-delegation/SKILL.md +128 -0
- package/skills/machine-delegation/references/delegation-machine.md +90 -0
- package/skills/machine-project-setup/SKILL.md +181 -0
- package/skills/machine-project-setup/references/project-workflows.md +50 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { AgentLimitError, agentLimitOf } from "./agent-limit.js";
|
|
2
|
+
export const defaultProbeMs = 15 * 60_000;
|
|
3
|
+
// A provider's reset time is when its window ends, not when it accepts requests again.
|
|
4
|
+
const resetMarginMs = 60_000;
|
|
5
|
+
// Short sleeps measured against the clock notice a reset that passed while the computer was suspended.
|
|
6
|
+
const tickMs = 60_000;
|
|
7
|
+
const handoffNote = [
|
|
8
|
+
"Note: a previous Agent working on this assignment was interrupted by a usage limit and may have",
|
|
9
|
+
"left partial work in the workspace. Inspect the current state and continue the same assignment",
|
|
10
|
+
"rather than starting over.",
|
|
11
|
+
].join(" ");
|
|
12
|
+
export function createLimitMemory() {
|
|
13
|
+
return new Map();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs one role's request on the first candidate that is not at a usage limit.
|
|
17
|
+
* A limited candidate hands the same request to the next one; when all are
|
|
18
|
+
* limited the request waits for a reset within the policy, or fails.
|
|
19
|
+
* A request with `limits: "report"` is tried on the bound candidate alone, and
|
|
20
|
+
* its limit is thrown at once.
|
|
21
|
+
*/
|
|
22
|
+
export function limitAwareRunner(role, candidates, memory, policy) {
|
|
23
|
+
const bound = candidates[0];
|
|
24
|
+
if (bound === undefined)
|
|
25
|
+
throw new Error(`Agent role "${role}" needs at least one preset`);
|
|
26
|
+
const now = policy.now ?? Date.now;
|
|
27
|
+
const sleep = policy.sleep ?? timerSleep;
|
|
28
|
+
const named = (name) => name === role ? name : `${name} (role ${role})`;
|
|
29
|
+
const limited = (name, retrying) => {
|
|
30
|
+
const remembered = memory.get(name);
|
|
31
|
+
return remembered !== undefined && remembered.until > now() && retrying?.has(remembered) !== true;
|
|
32
|
+
};
|
|
33
|
+
// The state decides what its limit means, so nothing is swapped, waited for, or reported as an update.
|
|
34
|
+
const reported = async (request, report) => {
|
|
35
|
+
const known = memory.get(bound.name);
|
|
36
|
+
if (known !== undefined && known.until > now()) {
|
|
37
|
+
throw new AgentLimitError(`${named(bound.name)} is at its usage limit (${resetText(known)})`, known.limit);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const event = await bound.runner(request, report);
|
|
41
|
+
if (known !== undefined && memory.get(bound.name) === known)
|
|
42
|
+
memory.delete(bound.name);
|
|
43
|
+
return event;
|
|
44
|
+
}
|
|
45
|
+
catch (cause) {
|
|
46
|
+
const limit = agentLimitOf(cause);
|
|
47
|
+
if (limit === undefined)
|
|
48
|
+
throw cause;
|
|
49
|
+
// The run's other roles skip this preset, and the limit names it.
|
|
50
|
+
throw new AgentLimitError(cause instanceof Error ? cause.message : String(cause), remember(bound, limit).limit, { cause });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return async (request, report) => {
|
|
54
|
+
if (request.limits === "report")
|
|
55
|
+
return reported(request, report);
|
|
56
|
+
// A state is announced after its Agent starts, in the same tick. Observers
|
|
57
|
+
// attach an update to the announced state, so none is reported before then.
|
|
58
|
+
const tell = async (update) => {
|
|
59
|
+
await Promise.resolve();
|
|
60
|
+
report?.(update);
|
|
61
|
+
};
|
|
62
|
+
let interrupted = false;
|
|
63
|
+
let lastFailure;
|
|
64
|
+
let deadline;
|
|
65
|
+
// A wait is reported once, and lasts until a candidate gets past its limit.
|
|
66
|
+
let waiting = false;
|
|
67
|
+
let retrying = new Set();
|
|
68
|
+
for (;;) {
|
|
69
|
+
let passedOver;
|
|
70
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
71
|
+
if (limited(candidate.name, retrying)) {
|
|
72
|
+
passedOver ??= candidate;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (passedOver !== undefined && !interrupted && !waiting) {
|
|
76
|
+
// Another role of this run found the limit, so this one names the preset it runs on instead.
|
|
77
|
+
// After a wait the preset is named when the Agent resumes.
|
|
78
|
+
await tell(swapped(named(passedOver.name), "is at", memory.get(passedOver.name), passedOver, candidate));
|
|
79
|
+
}
|
|
80
|
+
passedOver = undefined;
|
|
81
|
+
const resume = () => {
|
|
82
|
+
if (!waiting)
|
|
83
|
+
return;
|
|
84
|
+
waiting = false;
|
|
85
|
+
report?.({
|
|
86
|
+
type: "limit",
|
|
87
|
+
status: "resumed",
|
|
88
|
+
agent: bound.name,
|
|
89
|
+
...(candidate === bound ? {} : { to: candidate.name }),
|
|
90
|
+
text: `${named(bound.name)} waited for a usage limit to reset; continuing${candidate === bound ? "" : ` with ${candidate.name}`}`,
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const tried = memory.get(candidate.name);
|
|
94
|
+
let limit;
|
|
95
|
+
try {
|
|
96
|
+
const event = await candidate.runner(interrupted ? withHandoffNote(request) : request,
|
|
97
|
+
// A limited turn can still report its identity and the provider's message; a tool call is work.
|
|
98
|
+
!waiting ? report : (update) => {
|
|
99
|
+
if (update.type === "tool")
|
|
100
|
+
resume();
|
|
101
|
+
report?.(update);
|
|
102
|
+
});
|
|
103
|
+
resume();
|
|
104
|
+
// What was remembered for a preset that answers is out of date, also for the run's other roles.
|
|
105
|
+
if (tried !== undefined && memory.get(candidate.name) === tried)
|
|
106
|
+
memory.delete(candidate.name);
|
|
107
|
+
return event;
|
|
108
|
+
}
|
|
109
|
+
catch (cause) {
|
|
110
|
+
limit = agentLimitOf(cause);
|
|
111
|
+
if (limit === undefined)
|
|
112
|
+
throw cause;
|
|
113
|
+
lastFailure = cause;
|
|
114
|
+
}
|
|
115
|
+
interrupted = true;
|
|
116
|
+
const remembered = remember(candidate, limit);
|
|
117
|
+
const next = candidates.slice(index + 1).find((later) => !limited(later.name, retrying));
|
|
118
|
+
// Also while a wait is reported: each probe that moves on to another preset is a swap.
|
|
119
|
+
if (next !== undefined)
|
|
120
|
+
await tell(swapped(named(candidate.name), "reached", remembered, candidate, next));
|
|
121
|
+
}
|
|
122
|
+
const limits = candidates.flatMap((candidate) => {
|
|
123
|
+
const remembered = memory.get(candidate.name);
|
|
124
|
+
return remembered === undefined ? [] : [{ candidate, remembered }];
|
|
125
|
+
});
|
|
126
|
+
const started = now();
|
|
127
|
+
deadline ??= started + policy.maxWaitMs;
|
|
128
|
+
const closes = deadline;
|
|
129
|
+
const wakes = policy.maxWaitMs <= 0 ? [] : limits.flatMap(({ remembered }) => {
|
|
130
|
+
if (remembered.known) {
|
|
131
|
+
return remembered.until <= closes ? [{ remembered, at: remembered.until + resetMarginMs }] : [];
|
|
132
|
+
}
|
|
133
|
+
// The last probe of a limit with no stated reset is at the deadline.
|
|
134
|
+
return started < closes ? [{ remembered, at: Math.min(remembered.until, closes) }] : [];
|
|
135
|
+
});
|
|
136
|
+
if (wakes.length === 0)
|
|
137
|
+
throw exhausted(role, limits, policy.maxWaitMs, lastFailure);
|
|
138
|
+
const wakeAt = Math.min(...wakes.map((wake) => wake.at));
|
|
139
|
+
// A reset that passed while a later candidate was working needs no wait.
|
|
140
|
+
if (!waiting && wakeAt > now()) {
|
|
141
|
+
waiting = true;
|
|
142
|
+
const resets = wakes.filter((wake) => wake.remembered.known).map((wake) => wake.remembered.until);
|
|
143
|
+
const until = timestamp(resets.length === 0 ? closes : Math.min(...resets));
|
|
144
|
+
const others = candidates.slice(1).map((candidate) => candidate.name);
|
|
145
|
+
await tell({
|
|
146
|
+
type: "limit",
|
|
147
|
+
status: "waiting",
|
|
148
|
+
agent: bound.name,
|
|
149
|
+
until,
|
|
150
|
+
text: `${named(bound.name)}${others.length === 0
|
|
151
|
+
? " reached its usage limit"
|
|
152
|
+
: ` and its fallback${others.length === 1 ? "" : "s"} (${others.join(", ")}) reached their usage limits`}; ${resets.length === 0 ? `no reset is stated, retrying until ${until}` : `waiting for the reset at ${until}`}`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
for (let remaining = wakeAt - now(); remaining > 0; remaining = wakeAt - now()) {
|
|
156
|
+
await sleep(Math.min(remaining, tickMs));
|
|
157
|
+
}
|
|
158
|
+
retrying = new Set(wakes.filter((wake) => wake.at <= wakeAt).map((wake) => wake.remembered));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
function remember(candidate, limit) {
|
|
162
|
+
const at = now();
|
|
163
|
+
// A reset that has already passed says nothing about when to try again.
|
|
164
|
+
const known = limit.resetsAt !== undefined && limit.resetsAt > at;
|
|
165
|
+
const remembered = {
|
|
166
|
+
limit: { ...limit, agent: candidate.name },
|
|
167
|
+
until: known ? limit.resetsAt : at + policy.probeMs,
|
|
168
|
+
known,
|
|
169
|
+
};
|
|
170
|
+
memory.set(candidate.name, remembered);
|
|
171
|
+
return remembered;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function withHandoffNote(request) {
|
|
175
|
+
return { ...request, prompt: `${request.prompt}\n\n${handoffNote}` };
|
|
176
|
+
}
|
|
177
|
+
function swapped(subject, verb, remembered, from, to) {
|
|
178
|
+
return {
|
|
179
|
+
type: "limit",
|
|
180
|
+
status: "swapped",
|
|
181
|
+
agent: from.name,
|
|
182
|
+
to: to.name,
|
|
183
|
+
text: `${subject} ${verb} its usage limit (${resetText(remembered)}); continuing with ${to.name}`,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function exhausted(role, limits, maxWaitMs, cause) {
|
|
187
|
+
const described = limits.map(({ candidate, remembered }) => {
|
|
188
|
+
const details = [candidate.harness ?? remembered.limit.harness, remembered.limit.kind, resetText(remembered)];
|
|
189
|
+
return `${candidate.name} (${details.filter((detail) => detail !== undefined).join(", ")})`;
|
|
190
|
+
});
|
|
191
|
+
const reason = maxWaitMs <= 0
|
|
192
|
+
? "waiting for a reset is off"
|
|
193
|
+
: `none is available within the ${formatMinutes(maxWaitMs)} wait limit`;
|
|
194
|
+
const known = limits.filter(({ remembered }) => remembered.known)
|
|
195
|
+
.sort((left, right) => left.remembered.until - right.remembered.until);
|
|
196
|
+
const earliest = (known[0] ?? limits[0])?.remembered.limit ?? { harness: "unknown" };
|
|
197
|
+
return new AgentLimitError(`Agent role "${role}" is at a usage limit on every allowed preset: ${described.join("; ")}; ${reason}`, earliest, { limits: limits.map(({ remembered }) => remembered.limit), ...(cause === undefined ? {} : { cause }) });
|
|
198
|
+
}
|
|
199
|
+
function resetText(remembered) {
|
|
200
|
+
return remembered?.known === true ? `resets ${timestamp(remembered.until)}` : "reset unknown";
|
|
201
|
+
}
|
|
202
|
+
function timestamp(time) {
|
|
203
|
+
return new Date(time).toISOString().replace(/\.\d{3}Z$/u, "Z");
|
|
204
|
+
}
|
|
205
|
+
function formatMinutes(milliseconds) {
|
|
206
|
+
const minutes = Math.round(milliseconds / 6_000) / 10;
|
|
207
|
+
return `${minutes}-minute`;
|
|
208
|
+
}
|
|
209
|
+
// The timer stays referenced: a waiting run is working, and must keep its process alive.
|
|
210
|
+
function timerSleep(milliseconds) {
|
|
211
|
+
return new Promise((resolve) => { setTimeout(resolve, milliseconds); });
|
|
212
|
+
}
|
|
@@ -2,7 +2,9 @@ export function agentPrompt(request) {
|
|
|
2
2
|
return [
|
|
3
3
|
request.prompt,
|
|
4
4
|
"",
|
|
5
|
-
"Complete
|
|
5
|
+
"You are already executing one Agent state inside a Machine. Complete this assigned state using the available tools.",
|
|
6
|
+
"Do not re-delegate this assignment or start the parent workflow again unless the assignment explicitly requires bounded nested work.",
|
|
7
|
+
"Return evidence and an allowed outcome to the owning Machine; it controls verification, transitions, and Human decisions.",
|
|
6
8
|
`Allowed outcome types: ${request.outcomes.join(", ")}.`,
|
|
7
9
|
"When finished, end your final response with exactly one line in this form:",
|
|
8
10
|
'MACHINES_EVENT {"type":"completed"}',
|
package/dist/src/catalog.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface AgentPresetSummary {
|
|
|
14
14
|
readonly harness?: string;
|
|
15
15
|
readonly model?: string;
|
|
16
16
|
readonly thinking?: string;
|
|
17
|
+
/** The presets that take over at a usage limit, in order; absent when none is configured. */
|
|
18
|
+
readonly fallback?: readonly string[];
|
|
17
19
|
readonly source: string;
|
|
18
20
|
}
|
|
19
21
|
export interface LauncherLocation {
|
|
@@ -24,6 +26,7 @@ interface ResolvedAgentPresets {
|
|
|
24
26
|
readonly agents: Readonly<Record<string, AgentRunner>>;
|
|
25
27
|
readonly presets: Readonly<Record<string, AgentPreset>>;
|
|
26
28
|
readonly sources: Readonly<Record<string, string>>;
|
|
29
|
+
readonly fallback?: readonly string[];
|
|
27
30
|
}
|
|
28
31
|
export declare function inspectMachines(location?: LauncherLocation): Promise<readonly MachineSummary[]>;
|
|
29
32
|
export declare function inspectAgentPresets(location?: LauncherLocation): Promise<readonly AgentPresetSummary[]>;
|
package/dist/src/catalog.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import { loadAgentBindings } from "./agent-bindings.js";
|
|
1
|
+
import { fallbackPresets, loadAgentBindings } from "./agent-bindings.js";
|
|
2
2
|
import { acpAgent } from "./acp.js";
|
|
3
|
+
import { AgentLimitError } from "./agent-limit.js";
|
|
3
4
|
import { agyAgent } from "./agy.js";
|
|
5
|
+
import { chatGptWebAgent } from "./chatgpt-web.js";
|
|
6
|
+
import { claudeAgent } from "./claude.js";
|
|
7
|
+
import { codexAgent } from "./codex.js";
|
|
8
|
+
import { deepseekAgent } from "./deepseek.js";
|
|
9
|
+
import { decisionAgent } from "./decision.js";
|
|
10
|
+
import { jevAgent, jevProvider } from "./jev.js";
|
|
11
|
+
import { layaAgent, layaProvider } from "./laya.js";
|
|
12
|
+
import { vonAgent, vonProvider } from "./von.js";
|
|
13
|
+
import { openRouterDecisionAgent, openRouterDecisionProvider } from "./openrouter.js";
|
|
4
14
|
import { discoverMachines, machinesUserHome } from "./discovery.js";
|
|
5
15
|
import { loadMachineModule, readAgentRoles, requireMachineDescription } from "./machine-module.js";
|
|
6
16
|
export async function inspectMachines(location = {}) {
|
|
@@ -35,18 +45,25 @@ export async function inspectAgentPresets(location = {}) {
|
|
|
35
45
|
const home = location.home ?? machinesUserHome();
|
|
36
46
|
const configured = await configuredAgentPresets(cwd, home);
|
|
37
47
|
return Object.entries(configured.presets)
|
|
38
|
-
.map(([name, preset]) =>
|
|
39
|
-
name,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
.map(([name, preset]) => {
|
|
49
|
+
const fallback = fallbackPresets(name, preset, configured.fallback);
|
|
50
|
+
return {
|
|
51
|
+
name,
|
|
52
|
+
description: preset.description,
|
|
53
|
+
...(preset.harness === undefined ? {} : { harness: preset.harness }),
|
|
54
|
+
...(preset.model === undefined ? {} : { model: preset.model }),
|
|
55
|
+
...(preset.thinking === undefined ? {} : { thinking: preset.thinking }),
|
|
56
|
+
...(fallback === undefined ? {} : { fallback }),
|
|
57
|
+
source: configured.sources[name] ?? "unknown",
|
|
58
|
+
};
|
|
59
|
+
})
|
|
46
60
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
47
61
|
}
|
|
48
62
|
export async function configuredAgentPresets(cwd, home) {
|
|
49
|
-
const configured = await loadAgentBindings(cwd, home, {
|
|
63
|
+
const configured = await loadAgentBindings(cwd, home, {
|
|
64
|
+
acpAgent, AgentLimitError, agyAgent, chatGptWebAgent, claudeAgent, codexAgent, deepseekAgent, decisionAgent, jevAgent, jevProvider,
|
|
65
|
+
openRouterDecisionAgent, openRouterDecisionProvider, layaAgent, layaProvider, vonAgent, vonProvider,
|
|
66
|
+
});
|
|
50
67
|
const defaultRunner = acpAgent("npx", ["-y", "pi-acp"], {
|
|
51
68
|
harness: "pi-acp",
|
|
52
69
|
output: "capture",
|
|
@@ -55,8 +72,19 @@ export async function configuredAgentPresets(cwd, home) {
|
|
|
55
72
|
harness: "agy",
|
|
56
73
|
output: "capture",
|
|
57
74
|
});
|
|
75
|
+
const claudeRunner = claudeAgent("claude", [], { output: "capture" });
|
|
76
|
+
const codexRunner = codexAgent("codex", [], { output: "capture" });
|
|
77
|
+
const chatGptWebRunner = chatGptWebAgent({ output: "capture" });
|
|
78
|
+
const deepseekRunner = deepseekAgent("dsh", [], { output: "capture" });
|
|
79
|
+
const jevRunner = jevAgent();
|
|
80
|
+
const openRouterRunner = openRouterDecisionAgent();
|
|
81
|
+
const layaRunner = layaAgent();
|
|
82
|
+
const vonRunner = vonAgent();
|
|
58
83
|
return {
|
|
59
|
-
agents: {
|
|
84
|
+
agents: {
|
|
85
|
+
default: defaultRunner, agy: agyRunner, claude: claudeRunner, codex: codexRunner, "chatgpt-web": chatGptWebRunner, deepseek: deepseekRunner, jev: jevRunner,
|
|
86
|
+
"openrouter-decision": openRouterRunner, laya: layaRunner, von: vonRunner, ...configured.agents,
|
|
87
|
+
},
|
|
60
88
|
presets: {
|
|
61
89
|
default: {
|
|
62
90
|
description: "Runs the current Pi Agent through ACP",
|
|
@@ -68,9 +96,53 @@ export async function configuredAgentPresets(cwd, home) {
|
|
|
68
96
|
harness: "agy",
|
|
69
97
|
runner: agyRunner,
|
|
70
98
|
},
|
|
99
|
+
claude: {
|
|
100
|
+
description: "Runs the Claude Code CLI headlessly with its own login and permission rules",
|
|
101
|
+
harness: "claude",
|
|
102
|
+
runner: claudeRunner,
|
|
103
|
+
},
|
|
104
|
+
codex: {
|
|
105
|
+
description: "Runs the Codex CLI in a read-only sandbox",
|
|
106
|
+
harness: "codex",
|
|
107
|
+
runner: codexRunner,
|
|
108
|
+
},
|
|
109
|
+
"chatgpt-web": {
|
|
110
|
+
description: "Runs a new ChatGPT web conversation that works locally through rig-bridge",
|
|
111
|
+
harness: "chatgpt-web",
|
|
112
|
+
runner: chatGptWebRunner,
|
|
113
|
+
},
|
|
114
|
+
deepseek: {
|
|
115
|
+
description: "Runs DeepSeek Harness (dsh) through ACP",
|
|
116
|
+
harness: "dsh",
|
|
117
|
+
runner: deepseekRunner,
|
|
118
|
+
},
|
|
119
|
+
jev: {
|
|
120
|
+
description: "Classifies supplied evidence with Jev; does not execute tools",
|
|
121
|
+
harness: "jev",
|
|
122
|
+
runner: jevRunner,
|
|
123
|
+
},
|
|
124
|
+
"openrouter-decision": {
|
|
125
|
+
description: "Classifies supplied evidence through OpenRouter Decisions; does not execute tools",
|
|
126
|
+
harness: "openrouter-decision",
|
|
127
|
+
runner: openRouterRunner,
|
|
128
|
+
},
|
|
129
|
+
laya: {
|
|
130
|
+
description: "Classifies supplied evidence through a self-hosted Laya bridge; does not execute tools",
|
|
131
|
+
harness: "laya",
|
|
132
|
+
runner: layaRunner,
|
|
133
|
+
},
|
|
134
|
+
von: {
|
|
135
|
+
description: "Classifies supplied evidence through a self-hosted Von server; does not execute tools",
|
|
136
|
+
harness: "von",
|
|
137
|
+
runner: vonRunner,
|
|
138
|
+
},
|
|
71
139
|
...configured.presets,
|
|
72
140
|
},
|
|
73
|
-
sources: {
|
|
141
|
+
sources: {
|
|
142
|
+
default: "built in", agy: "built in", claude: "built in", codex: "built in", "chatgpt-web": "built in", deepseek: "built in", jev: "built in",
|
|
143
|
+
"openrouter-decision": "built in", laya: "built in", von: "built in", ...configured.sources,
|
|
144
|
+
},
|
|
145
|
+
...(configured.fallback === undefined ? {} : { fallback: configured.fallback }),
|
|
74
146
|
};
|
|
75
147
|
}
|
|
76
148
|
function errorMessage(cause) {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { AgentReporter, AgentRequest, Event } from "./index.ts";
|
|
2
|
+
/** CSS selectors for the ChatGPT page. Override them when the web UI changes. */
|
|
3
|
+
export interface ChatGptWebSelectors {
|
|
4
|
+
readonly composer: string;
|
|
5
|
+
readonly send: string;
|
|
6
|
+
readonly stop: string;
|
|
7
|
+
readonly assistant: string;
|
|
8
|
+
readonly plus: string;
|
|
9
|
+
readonly menuItem: string;
|
|
10
|
+
/** A control only a signed-out page shows. Links and buttons labelled "Log in" or "Sign in" also count. */
|
|
11
|
+
readonly signIn: string;
|
|
12
|
+
}
|
|
13
|
+
/** Each list names the page as it is today and as it was before, for accounts a rollout has not reached. */
|
|
14
|
+
export declare const defaultChatGptWebSelectors: ChatGptWebSelectors;
|
|
15
|
+
export interface ChatGptWebBrowserLaunch {
|
|
16
|
+
/** Chromium-family executable. Defaults to `CHATGPT_WEB_BROWSER`, then a PATH search. */
|
|
17
|
+
readonly executable?: string;
|
|
18
|
+
/** Dedicated browser profile holding the ChatGPT login. */
|
|
19
|
+
readonly userDataDir?: string;
|
|
20
|
+
readonly headless?: boolean;
|
|
21
|
+
readonly args?: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/** Explicit handoff notification recorded by rig-bridge when an assistant finishes a stretch of work. */
|
|
24
|
+
export interface RigBridgeHandoff {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly projectId: string;
|
|
27
|
+
readonly threadId: string;
|
|
28
|
+
readonly runId?: string;
|
|
29
|
+
readonly reason: "completed" | "needs_input" | "blocked" | "failed" | "cancelled" | string;
|
|
30
|
+
readonly summary: string;
|
|
31
|
+
readonly createdAt: number;
|
|
32
|
+
}
|
|
33
|
+
/** Event returned by the ChatGPT Web runner, enriched with handoff and conversation metadata. */
|
|
34
|
+
export interface ChatGptWebEvent extends Event {
|
|
35
|
+
readonly type: string;
|
|
36
|
+
readonly summary?: string;
|
|
37
|
+
readonly message?: string;
|
|
38
|
+
readonly handoff?: RigBridgeHandoff;
|
|
39
|
+
/** The chat's lasting address. Absent when ChatGPT did not show one before the run ended. */
|
|
40
|
+
readonly conversation?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface RigBridgeStatusView {
|
|
43
|
+
readonly version?: string;
|
|
44
|
+
readonly startedAt?: number;
|
|
45
|
+
readonly uptimeSeconds?: number;
|
|
46
|
+
readonly workspacesCount?: number;
|
|
47
|
+
readonly activeCommandsCount?: number;
|
|
48
|
+
readonly workspaces: readonly {
|
|
49
|
+
readonly id: string;
|
|
50
|
+
readonly projectId?: string;
|
|
51
|
+
readonly threadId?: string;
|
|
52
|
+
readonly cwd: string;
|
|
53
|
+
readonly createdAt?: number;
|
|
54
|
+
}[];
|
|
55
|
+
readonly notifications?: readonly RigBridgeHandoff[];
|
|
56
|
+
readonly unreadCount?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ChatGptWebAgentOptions {
|
|
59
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
60
|
+
readonly harness?: string;
|
|
61
|
+
/** Start page: ChatGPT, a Project, or a GPT. Defaults to `CHATGPT_WEB_URL` or https://chatgpt.com/. */
|
|
62
|
+
readonly url?: string;
|
|
63
|
+
/** Model slug appended as `?model=`. Defaults to `CHATGPT_WEB_MODEL`. */
|
|
64
|
+
readonly model?: string;
|
|
65
|
+
/** DevTools endpoint. Defaults to `CHATGPT_WEB_CDP_URL` or http://127.0.0.1:9222. */
|
|
66
|
+
readonly cdpUrl?: string;
|
|
67
|
+
/** Launch a persistent, detached browser when the endpoint is unreachable. Defaults to true. */
|
|
68
|
+
readonly launch?: boolean | ChatGptWebBrowserLaunch;
|
|
69
|
+
/**
|
|
70
|
+
* Name the rig-bridge app has in this person's ChatGPT, matched against the text of the composer's add menu.
|
|
71
|
+
* Defaults to `CHATGPT_WEB_CONNECTOR` or "rig-bridge".
|
|
72
|
+
*/
|
|
73
|
+
readonly connector?: string;
|
|
74
|
+
/** Try to enable the connector from the composer's + menu. Defaults to true. */
|
|
75
|
+
readonly selectConnector?: boolean;
|
|
76
|
+
/** Click visible tool confirmation buttons. Defaults to false. */
|
|
77
|
+
readonly approveToolCalls?: boolean;
|
|
78
|
+
readonly approveLabels?: readonly string[];
|
|
79
|
+
/** rig-bridge status endpoint used for activity, or false. Defaults to `RIG_BRIDGE_STATUS_URL` or :8767. */
|
|
80
|
+
readonly bridgeStatusUrl?: string | false;
|
|
81
|
+
/** Automatically mark handoff notifications read on rig-bridge when completed. Defaults to true. */
|
|
82
|
+
readonly acknowledgeHandoff?: boolean;
|
|
83
|
+
readonly timeoutMs?: number;
|
|
84
|
+
/** How long the composer may take to appear after the tab opens. Defaults to 45 seconds. */
|
|
85
|
+
readonly composerTimeoutMs?: number;
|
|
86
|
+
/** Quiet period after generation stops before the reply is treated as final. */
|
|
87
|
+
readonly settleMs?: number;
|
|
88
|
+
readonly pollMs?: number;
|
|
89
|
+
/** Keep the tab after success. Failed runs always keep their tab for inspection. */
|
|
90
|
+
readonly keepTab?: boolean;
|
|
91
|
+
readonly selectors?: Partial<ChatGptWebSelectors>;
|
|
92
|
+
readonly output?: "capture" | "stream";
|
|
93
|
+
}
|
|
94
|
+
/** Finds a Chromium-family browser able to expose the DevTools protocol. */
|
|
95
|
+
export declare function findChromeExecutable(env?: Readonly<Record<string, string | undefined>>): Promise<string | undefined>;
|
|
96
|
+
export declare function defaultChatGptWebProfile(env?: Readonly<Record<string, string | undefined>>): string;
|
|
97
|
+
/** Builds the conversation opener that points ChatGPT at the local workspace through rig-bridge. */
|
|
98
|
+
export declare function chatGptWebPrompt(request: AgentRequest & {
|
|
99
|
+
readonly cwd: string;
|
|
100
|
+
}, connector: string): string;
|
|
101
|
+
export type ChatGptWebAgentRunner = (request: AgentRequest, report?: AgentReporter) => Promise<ChatGptWebEvent>;
|
|
102
|
+
/** One message for an existing ChatGPT web conversation that works in `cwd` through rig-bridge. */
|
|
103
|
+
export interface ChatGptWebContinueInput {
|
|
104
|
+
/** Conversation URL, such as the `conversation` of an earlier ChatGPT Web event. */
|
|
105
|
+
readonly conversation: string;
|
|
106
|
+
/** Message to send. It is submitted exactly as given. */
|
|
107
|
+
readonly text: string;
|
|
108
|
+
readonly cwd: string;
|
|
109
|
+
/** Only count a handoff from this rig-bridge thread. */
|
|
110
|
+
readonly bridgeThreadId?: string;
|
|
111
|
+
}
|
|
112
|
+
export type ChatGptWebContinueRunner = (input: ChatGptWebContinueInput, report?: AgentReporter) => Promise<ChatGptWebEvent>;
|
|
113
|
+
/** Runs one fresh ChatGPT web conversation that works in the request's directory via rig-bridge. */
|
|
114
|
+
export declare function chatGptWebAgent(options?: ChatGptWebAgentOptions): ChatGptWebAgentRunner;
|
|
115
|
+
/**
|
|
116
|
+
* Sends one message to an existing ChatGPT web conversation and returns the next rig-bridge handoff.
|
|
117
|
+
* The handoff is the whole result: assistant messages are never read from the page.
|
|
118
|
+
*/
|
|
119
|
+
export declare function chatGptWebContinue(options?: ChatGptWebAgentOptions): ChatGptWebContinueRunner;
|
|
120
|
+
/** Fetches the latest bridge status from rig-bridge. */
|
|
121
|
+
export declare function fetchRigBridgeStatus(statusUrl?: string): Promise<RigBridgeStatusView | undefined>;
|
|
122
|
+
/** Finds the most recent handoff notification from rig-bridge matching the given directory or thread. */
|
|
123
|
+
export declare function fetchLatestRigBridgeHandoff(options?: {
|
|
124
|
+
readonly cwd?: string;
|
|
125
|
+
readonly threadId?: string;
|
|
126
|
+
readonly statusUrl?: string;
|
|
127
|
+
readonly since?: number;
|
|
128
|
+
}): Promise<RigBridgeHandoff | undefined>;
|
|
129
|
+
/** Acknowledges / marks read a handoff on rig-bridge. */
|
|
130
|
+
export declare function acknowledgeRigBridgeHandoff(handoffId: string, options?: {
|
|
131
|
+
readonly statusUrl?: string;
|
|
132
|
+
}): Promise<boolean>;
|
|
133
|
+
/**
|
|
134
|
+
* Creates an Operation for a Machine that inspects rig-bridge for the latest handoff notification,
|
|
135
|
+
* returning an Event with `type = handoff.reason` and attaching `summary` and `handoff`.
|
|
136
|
+
*/
|
|
137
|
+
export declare function rigBridgeHandoffOperation(options?: {
|
|
138
|
+
readonly cwd?: string;
|
|
139
|
+
readonly threadId?: string;
|
|
140
|
+
readonly statusUrl?: string;
|
|
141
|
+
readonly since?: number;
|
|
142
|
+
readonly acknowledge?: boolean;
|
|
143
|
+
}): () => Promise<ChatGptWebEvent>;
|