doer-agent 0.6.3 → 0.6.5
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,161 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { StringCodec } from "nats";
|
|
5
|
+
import { resolveAgentVersion } from "./agent-runtime-utils.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const maintenanceRpcCodec = StringCodec();
|
|
8
|
+
function normalizeAction(value) {
|
|
9
|
+
if (value === "info" || value === "update/check" || value === "update/restart") {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
throw new Error("unsupported action");
|
|
13
|
+
}
|
|
14
|
+
function parseSemver(value) {
|
|
15
|
+
const match = value.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
16
|
+
if (!match) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
20
|
+
}
|
|
21
|
+
function compareSemver(a, b) {
|
|
22
|
+
const left = parseSemver(a);
|
|
23
|
+
const right = parseSemver(b);
|
|
24
|
+
if (!left || !right) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
for (let index = 0; index < 3; index += 1) {
|
|
28
|
+
if (left[index] !== right[index]) {
|
|
29
|
+
return left[index] - right[index];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
async function resolveLatestPackageVersion() {
|
|
35
|
+
try {
|
|
36
|
+
const { stdout } = await execFileAsync("npm", ["view", "doer-agent", "version"], {
|
|
37
|
+
timeout: 15_000,
|
|
38
|
+
maxBuffer: 1024 * 64,
|
|
39
|
+
});
|
|
40
|
+
const version = stdout.trim().split(/\s+/)[0]?.trim() || "";
|
|
41
|
+
return version || null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function resolveUpdateCommand(command) {
|
|
48
|
+
const rawCommand = command?.trim() || "";
|
|
49
|
+
if (!rawCommand) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const replaced = rawCommand.replace(/(^|\s)npx(\s+-y)?\s+(?:doer-agent(?:@[^\s]+)?|\.)\b/, (_match, prefix, yesFlag) => `${prefix}npx${yesFlag ?? " -y"} doer-agent@latest`);
|
|
53
|
+
return replaced !== rawCommand ? replaced : null;
|
|
54
|
+
}
|
|
55
|
+
function shellQuote(value) {
|
|
56
|
+
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
60
|
+
}
|
|
61
|
+
function buildFallbackUpdateCommand() {
|
|
62
|
+
const workspace = process.env.WORKSPACE?.trim() || process.cwd();
|
|
63
|
+
const args = process.argv.slice(2).map(shellQuote).join(" ");
|
|
64
|
+
return [`WORKSPACE=${shellQuote(workspace)}`, "npx -y doer-agent@latest", args].filter(Boolean).join(" ");
|
|
65
|
+
}
|
|
66
|
+
async function resolveMaintenanceInfo(agentPackageJsonPath) {
|
|
67
|
+
const currentVersion = await resolveAgentVersion(agentPackageJsonPath);
|
|
68
|
+
const latestVersion = await resolveLatestPackageVersion();
|
|
69
|
+
const updateComparison = latestVersion ? compareSemver(currentVersion, latestVersion) : null;
|
|
70
|
+
const launchCommand = process.env.DOER_AGENT_LAUNCH_COMMAND?.trim() || process.env.DOER_DAEMON_COMMAND?.trim() || "";
|
|
71
|
+
const updateCommand = resolveUpdateCommand(launchCommand) ?? buildFallbackUpdateCommand();
|
|
72
|
+
return {
|
|
73
|
+
currentVersion,
|
|
74
|
+
latestVersion,
|
|
75
|
+
updateAvailable: updateComparison === null ? null : updateComparison < 0,
|
|
76
|
+
packageName: "doer-agent",
|
|
77
|
+
nodeVersion: process.version,
|
|
78
|
+
command: launchCommand || updateCommand,
|
|
79
|
+
updateCommand,
|
|
80
|
+
cwd: process.env.DOER_AGENT_LAUNCH_CWD?.trim() || process.env.DOER_DAEMON_CWD?.trim() || process.cwd(),
|
|
81
|
+
startedAt: new Date(Number(process.env.DOER_AGENT_STARTED_AT_MS || Date.now())).toISOString(),
|
|
82
|
+
canRestartToLatest: Boolean(updateCommand),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function scheduleRestartToLatest(command, cwd) {
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
const env = { ...process.env };
|
|
88
|
+
delete env.DOER_DAEMON_ID;
|
|
89
|
+
delete env.DOER_DAEMON_COMMAND;
|
|
90
|
+
delete env.DOER_DAEMON_CWD;
|
|
91
|
+
delete env.DOER_DAEMON_STATE_PATH;
|
|
92
|
+
delete env.DOER_DAEMON_EVENTS_PATH;
|
|
93
|
+
delete env.DOER_DAEMON_SHELL_PATH;
|
|
94
|
+
delete env.DOER_AGENT_LAUNCH_COMMAND;
|
|
95
|
+
delete env.DOER_AGENT_LAUNCH_CWD;
|
|
96
|
+
delete env.DOER_AGENT_LAUNCH_SHELL;
|
|
97
|
+
const child = spawn(command, {
|
|
98
|
+
cwd,
|
|
99
|
+
env,
|
|
100
|
+
shell: process.env.DOER_AGENT_LAUNCH_SHELL || process.env.DOER_DAEMON_SHELL_PATH || process.env.SHELL || true,
|
|
101
|
+
detached: true,
|
|
102
|
+
stdio: "ignore",
|
|
103
|
+
});
|
|
104
|
+
child.unref();
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}, 250);
|
|
108
|
+
}, 250);
|
|
109
|
+
}
|
|
110
|
+
function publicMaintenanceInfo(info) {
|
|
111
|
+
return {
|
|
112
|
+
...info,
|
|
113
|
+
command: null,
|
|
114
|
+
updateCommand: null,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export async function handleMaintenanceRpcMessage(args) {
|
|
118
|
+
let requestId = "unknown";
|
|
119
|
+
try {
|
|
120
|
+
const request = JSON.parse(maintenanceRpcCodec.decode(args.msg.data));
|
|
121
|
+
requestId = typeof request.requestId === "string" ? request.requestId : "unknown";
|
|
122
|
+
const action = normalizeAction(request.action);
|
|
123
|
+
const info = await resolveMaintenanceInfo(args.agentPackageJsonPath);
|
|
124
|
+
if (action === "update/restart") {
|
|
125
|
+
if (!info.updateCommand) {
|
|
126
|
+
throw new Error("agent was not started with a supported npx doer-agent command");
|
|
127
|
+
}
|
|
128
|
+
scheduleRestartToLatest(info.updateCommand, info.cwd);
|
|
129
|
+
}
|
|
130
|
+
args.msg.respond(maintenanceRpcCodec.encode(JSON.stringify({
|
|
131
|
+
requestId,
|
|
132
|
+
ok: true,
|
|
133
|
+
action,
|
|
134
|
+
info: publicMaintenanceInfo(info),
|
|
135
|
+
restartScheduled: action === "update/restart",
|
|
136
|
+
})));
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
args.onError?.(`maintenance rpc failed requestId=${requestId} error=${message}`);
|
|
141
|
+
args.msg.respond(maintenanceRpcCodec.encode(JSON.stringify({ requestId, ok: false, error: message })));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export function subscribeToMaintenanceRpc(args) {
|
|
145
|
+
args.nc.subscribe(args.subject, {
|
|
146
|
+
callback: (error, msg) => {
|
|
147
|
+
if (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
149
|
+
args.onError(`maintenance rpc subscription error: ${message}`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
void handleMaintenanceRpcMessage({
|
|
153
|
+
msg,
|
|
154
|
+
nc: args.nc,
|
|
155
|
+
agentPackageJsonPath: args.agentPackageJsonPath,
|
|
156
|
+
onError: args.onError,
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
args.onInfo(`maintenance rpc subscribed subject=${args.subject}`);
|
|
161
|
+
}
|
|
@@ -25,6 +25,9 @@ export function buildAgentFsRpcSubject(userId, agentId) {
|
|
|
25
25
|
export function buildAgentDaemonRpcSubject(userId, agentId) {
|
|
26
26
|
return `doer.agent.daemon.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
27
27
|
}
|
|
28
|
+
export function buildAgentMaintenanceRpcSubject(userId, agentId) {
|
|
29
|
+
return `doer.agent.maintenance.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
30
|
+
}
|
|
28
31
|
export function parseBootstrapTaskConfig(value) {
|
|
29
32
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
30
33
|
return null;
|
package/dist/agent-settings.js
CHANGED
|
@@ -16,6 +16,7 @@ export function createDefaultAgentSettingsConfig() {
|
|
|
16
16
|
},
|
|
17
17
|
codex: {
|
|
18
18
|
model: "gpt-5.5",
|
|
19
|
+
reasoningEffort: "medium",
|
|
19
20
|
authMode: "api_key",
|
|
20
21
|
computerUseEnabled: false,
|
|
21
22
|
browserUseEnabled: false,
|
|
@@ -54,6 +55,16 @@ function normalizeNullableString(value) {
|
|
|
54
55
|
function normalizeCodexPersonality(value, fallback) {
|
|
55
56
|
return value === "friendly" || value === "pragmatic" ? value : fallback;
|
|
56
57
|
}
|
|
58
|
+
function normalizeReasoningEffort(value, fallback) {
|
|
59
|
+
return value === "none" ||
|
|
60
|
+
value === "minimal" ||
|
|
61
|
+
value === "low" ||
|
|
62
|
+
value === "medium" ||
|
|
63
|
+
value === "high" ||
|
|
64
|
+
value === "xhigh"
|
|
65
|
+
? value
|
|
66
|
+
: fallback;
|
|
67
|
+
}
|
|
57
68
|
function normalizeEnvVarName(value) {
|
|
58
69
|
if (typeof value !== "string") {
|
|
59
70
|
return null;
|
|
@@ -110,6 +121,7 @@ export function normalizeAgentSettingsConfig(value, fallback) {
|
|
|
110
121
|
},
|
|
111
122
|
codex: {
|
|
112
123
|
model: typeof codex.model === "string" && codex.model.trim() ? codex.model.trim() : base.codex.model,
|
|
124
|
+
reasoningEffort: normalizeReasoningEffort(codex.reasoningEffort, base.codex.reasoningEffort),
|
|
113
125
|
authMode: codex.authMode === "chatgpt" ? "chatgpt" : codex.authMode === "api_key" ? "api_key" : base.codex.authMode,
|
|
114
126
|
computerUseEnabled: typeof codex.computerUseEnabled === "boolean" ? codex.computerUseEnabled : base.codex.computerUseEnabled,
|
|
115
127
|
browserUseEnabled: typeof codex.browserUseEnabled === "boolean" ? codex.browserUseEnabled : base.codex.browserUseEnabled,
|
|
@@ -189,6 +201,7 @@ export async function toAgentSettingsPublic(args) {
|
|
|
189
201
|
},
|
|
190
202
|
codex: {
|
|
191
203
|
model: args.config.codex.model,
|
|
204
|
+
reasoningEffort: args.config.codex.reasoningEffort,
|
|
192
205
|
authMode: args.config.codex.authMode,
|
|
193
206
|
computerUseEnabled: args.config.codex.computerUseEnabled,
|
|
194
207
|
browserUseEnabled: args.config.codex.browserUseEnabled,
|
|
@@ -246,6 +259,7 @@ export function normalizeAgentSettingsPatch(value) {
|
|
|
246
259
|
};
|
|
247
260
|
move("personality", "general", "personality");
|
|
248
261
|
move("codexModel", "codex", "model");
|
|
262
|
+
move("codexReasoningEffort", "codex", "reasoningEffort");
|
|
249
263
|
move("codexAuthMode", "codex", "authMode");
|
|
250
264
|
move("computerUseEnabled", "codex", "computerUseEnabled");
|
|
251
265
|
move("browserUseEnabled", "codex", "browserUseEnabled");
|
package/dist/agent.js
CHANGED
|
@@ -12,8 +12,9 @@ import { createLocalCodexCliTools } from "./agent-codex-cli.js";
|
|
|
12
12
|
import { connectBootstrapWithRetry } from "./agent-jetstream.js";
|
|
13
13
|
import { runConnectedAgentSession } from "./agent-session-loop.js";
|
|
14
14
|
import { subscribeToSkillRpc } from "./agent-skill-rpc.js";
|
|
15
|
+
import { subscribeToMaintenanceRpc } from "./agent-maintenance-rpc.js";
|
|
15
16
|
import { sendSignalToTaskProcess } from "./agent-task-execution.js";
|
|
16
|
-
import { buildAgentCodexAppEventsSubject, buildAgentCodexAppRpcSubject, buildAgentDaemonRpcSubject, buildAgentFsRpcSubject, buildAgentGitRpcSubject, buildAgentSettingsRpcSubject, buildAgentSkillRpcSubject, formatLocalTimestamp, parseArgs, resolveAgentVersion, resolveArgOrEnv, resolveContainerReachableServerBaseUrl, sanitizeUserId, sleep, } from "./agent-runtime-utils.js";
|
|
17
|
+
import { buildAgentCodexAppEventsSubject, buildAgentCodexAppRpcSubject, buildAgentDaemonRpcSubject, buildAgentFsRpcSubject, buildAgentGitRpcSubject, buildAgentMaintenanceRpcSubject, buildAgentSettingsRpcSubject, buildAgentSkillRpcSubject, formatLocalTimestamp, parseArgs, resolveAgentVersion, resolveArgOrEnv, resolveContainerReachableServerBaseUrl, sanitizeUserId, sleep, } from "./agent-runtime-utils.js";
|
|
17
18
|
import { createRuntimeEnvHelpers } from "./agent-runtime-env.js";
|
|
18
19
|
import { createEventPersistenceHelpers, heartbeatAgentSession, postJson, } from "./agent-runtime-io.js";
|
|
19
20
|
import { handleSettingsRpcMessage } from "./agent-settings-rpc.js";
|
|
@@ -162,6 +163,7 @@ async function main() {
|
|
|
162
163
|
process.chdir(startupWorkspaceRoot);
|
|
163
164
|
process.env.WORKSPACE = startupWorkspaceRoot;
|
|
164
165
|
process.env.CODEX_HOME = path.join(startupWorkspaceRoot, ".codex");
|
|
166
|
+
process.env.DOER_AGENT_STARTED_AT_MS = String(Date.now());
|
|
165
167
|
await mkdir(process.env.CODEX_HOME, { recursive: true }).catch(() => undefined);
|
|
166
168
|
const serverBaseUrlRaw = resolveArgOrEnv(args, ["server", "url"], ["DOER_AGENT_SERVER"], DEFAULT_SERVER_BASE_URL);
|
|
167
169
|
const requestedServerBaseUrl = serverBaseUrlRaw.replace(/\/$/, "");
|
|
@@ -293,6 +295,13 @@ async function main() {
|
|
|
293
295
|
onInfo: writeAgentInfo,
|
|
294
296
|
onError: writeAgentError,
|
|
295
297
|
});
|
|
298
|
+
subscribeToMaintenanceRpc({
|
|
299
|
+
nc: jetstream.nc,
|
|
300
|
+
subject: buildAgentMaintenanceRpcSubject(userId, initialAgentId),
|
|
301
|
+
agentPackageJsonPath: AGENT_PACKAGE_JSON_PATH,
|
|
302
|
+
onInfo: writeAgentInfo,
|
|
303
|
+
onError: writeAgentError,
|
|
304
|
+
});
|
|
296
305
|
},
|
|
297
306
|
onInfraError: writeAgentInfraError,
|
|
298
307
|
sleep,
|
|
@@ -13,6 +13,7 @@ function buildFeatureArg(enabled, name) {
|
|
|
13
13
|
async function buildCodexAppServerArgs(args) {
|
|
14
14
|
const configArgs = [
|
|
15
15
|
...buildConfigArg("model", toTomlStringLiteral(args.settings.codex.model)),
|
|
16
|
+
...buildConfigArg("model_reasoning_effort", toTomlStringLiteral(args.settings.codex.reasoningEffort)),
|
|
16
17
|
...buildConfigArg("personality", toTomlStringLiteral(args.settings.general.personality)),
|
|
17
18
|
...buildConfigArg("approval_policy", toTomlStringLiteral("never")),
|
|
18
19
|
...buildConfigArg("sandbox_mode", toTomlStringLiteral("danger-full-access")),
|
|
@@ -58,7 +59,7 @@ export function createCodexAppServerManager(args) {
|
|
|
58
59
|
resolveCodexHomePath: args.resolveCodexHomePath,
|
|
59
60
|
settings,
|
|
60
61
|
});
|
|
61
|
-
args.onLog?.(`starting codex app-server model=${settings.codex.model} personality=${settings.general.personality} computerUse=${settings.codex.computerUseEnabled} browserUse=${settings.codex.browserUseEnabled}`);
|
|
62
|
+
args.onLog?.(`starting codex app-server model=${settings.codex.model} reasoningEffort=${settings.codex.reasoningEffort} personality=${settings.general.personality} computerUse=${settings.codex.computerUseEnabled} browserUse=${settings.codex.browserUseEnabled}`);
|
|
62
63
|
return new CodexAppServerClient({
|
|
63
64
|
cwd: args.workspaceRoot,
|
|
64
65
|
args: appServerArgs,
|
|
@@ -62,6 +62,9 @@ async function main() {
|
|
|
62
62
|
const cwd = readRequiredEnv("DOER_DAEMON_CWD");
|
|
63
63
|
const shellPath = readRequiredEnv("DOER_DAEMON_SHELL_PATH");
|
|
64
64
|
const childEnv = { ...process.env };
|
|
65
|
+
childEnv.DOER_AGENT_LAUNCH_COMMAND = command;
|
|
66
|
+
childEnv.DOER_AGENT_LAUNCH_CWD = cwd;
|
|
67
|
+
childEnv.DOER_AGENT_LAUNCH_SHELL = shellPath;
|
|
65
68
|
delete childEnv.DOER_DAEMON_STATE_PATH;
|
|
66
69
|
delete childEnv.DOER_DAEMON_EVENTS_PATH;
|
|
67
70
|
delete childEnv.DOER_DAEMON_COMMAND;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doer-agent",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Reverse-polling agent runtime for doer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/agent.js",
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
"prepublishOnly": "npm run build"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
28
|
-
"@openai/codex": "^0.
|
|
29
|
-
"@openai/codex-sdk": "^0.
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
+
"@openai/codex": "^0.130.0",
|
|
29
|
+
"@openai/codex-sdk": "^0.130.0",
|
|
30
30
|
"nats": "^2.29.3",
|
|
31
|
-
"tar": "^7.5.
|
|
31
|
+
"tar": "^7.5.15"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@types/node": "^20",
|
|
34
|
+
"@types/node": "^20.19.40",
|
|
35
35
|
"tsx": "^4.21.0",
|
|
36
36
|
"typescript": "^5"
|
|
37
37
|
}
|