@openape/apes 1.28.12 → 1.29.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/dist/chunk-BA2V3BBO.js +304 -0
- package/dist/chunk-BA2V3BBO.js.map +1 -0
- package/dist/chunk-JXS3KLJ5.js +53 -0
- package/dist/chunk-JXS3KLJ5.js.map +1 -0
- package/dist/chunk-QMMRZPD2.js +47 -0
- package/dist/chunk-QMMRZPD2.js.map +1 -0
- package/dist/{chunk-3COOEDPF.js → chunk-RVAXRDC2.js} +1 -1
- package/dist/chunk-RVAXRDC2.js.map +1 -0
- package/dist/cli.js +26 -397
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -306
- package/dist/index.js +27 -27
- package/dist/index.js.map +1 -1
- package/dist/{orchestrator-BDX3WK7Q.js → orchestrator-P7QFDBBK.js} +8 -6
- package/dist/{orchestrator-BDX3WK7Q.js.map → orchestrator-P7QFDBBK.js.map} +1 -1
- package/dist/{server-OAINN75J.js → server-QN35XDYH.js} +7 -5
- package/dist/server-QN35XDYH.js.map +1 -0
- package/package.json +7 -5
- package/dist/chunk-3COOEDPF.js.map +0 -1
- package/dist/chunk-DYSFQ26B.js +0 -1215
- package/dist/chunk-DYSFQ26B.js.map +0 -1
- package/dist/chunk-ZEUSCNCH.js +0 -1067
- package/dist/chunk-ZEUSCNCH.js.map +0 -1
- package/dist/server-OAINN75J.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -2,18 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CliError,
|
|
4
4
|
CliExit,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
buildIssueGet,
|
|
8
|
-
buildPrCreate,
|
|
9
|
-
buildPrMerge,
|
|
10
|
-
detectForge,
|
|
11
|
-
parseDuration,
|
|
12
|
-
runApeShell,
|
|
13
|
-
runLoop,
|
|
14
|
-
taskTools,
|
|
15
|
-
worktreePathFor
|
|
16
|
-
} from "./chunk-ZEUSCNCH.js";
|
|
5
|
+
parseDuration
|
|
6
|
+
} from "./chunk-QMMRZPD2.js";
|
|
17
7
|
import {
|
|
18
8
|
loadEd25519PrivateKey,
|
|
19
9
|
readPublicKeyComment
|
|
@@ -22,20 +12,15 @@ import {
|
|
|
22
12
|
checkSudoRejection,
|
|
23
13
|
isApesSelfDispatch,
|
|
24
14
|
notifyGrantPending
|
|
25
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-RVAXRDC2.js";
|
|
26
16
|
import {
|
|
27
17
|
GENERIC_OPERATION_ID,
|
|
28
18
|
buildGenericResolved,
|
|
29
19
|
buildStructuredCliGrantRequest,
|
|
30
|
-
createShapesGrant,
|
|
31
|
-
extractOption,
|
|
32
20
|
extractShellCommandString,
|
|
33
|
-
extractWrappedCommand,
|
|
34
|
-
fetchGrantToken,
|
|
35
21
|
fetchRegistry,
|
|
36
22
|
findAdapter,
|
|
37
23
|
findConflictingAdapters,
|
|
38
|
-
findExistingGrant,
|
|
39
24
|
getInstalledDigest,
|
|
40
25
|
installAdapter,
|
|
41
26
|
isInstalled,
|
|
@@ -45,12 +30,19 @@ import {
|
|
|
45
30
|
removeAdapter,
|
|
46
31
|
resolveCapabilityRequest,
|
|
47
32
|
resolveCommand,
|
|
48
|
-
resolveFromGrant,
|
|
49
33
|
resolveGenericOrReject,
|
|
50
|
-
searchAdapters
|
|
34
|
+
searchAdapters
|
|
35
|
+
} from "./chunk-JXS3KLJ5.js";
|
|
36
|
+
import {
|
|
37
|
+
createShapesGrant,
|
|
38
|
+
extractOption,
|
|
39
|
+
extractWrappedCommand,
|
|
40
|
+
fetchGrantToken,
|
|
41
|
+
findExistingGrant,
|
|
42
|
+
resolveFromGrant,
|
|
51
43
|
verifyAndExecute,
|
|
52
44
|
waitForGrantStatus
|
|
53
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-BA2V3BBO.js";
|
|
54
46
|
import {
|
|
55
47
|
ApiError,
|
|
56
48
|
apiFetch,
|
|
@@ -2994,6 +2986,7 @@ import { join as join6 } from "path";
|
|
|
2994
2986
|
import process3 from "process";
|
|
2995
2987
|
import { defineCommand as defineCommand24 } from "citty";
|
|
2996
2988
|
import { consola as consola21 } from "consola";
|
|
2989
|
+
import { taskTools, runApeShell, runCodingTask, buildIssueGet, detectForge, createLlmReviewer, createLlmRiskAssessor, resolveMergePolicy } from "@openape/agent-runtime";
|
|
2997
2990
|
|
|
2998
2991
|
// src/lib/agent-secrets-runtime.ts
|
|
2999
2992
|
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6, watch } from "fs";
|
|
@@ -3073,372 +3066,6 @@ function startSecretsWatcher(opts = {}) {
|
|
|
3073
3066
|
};
|
|
3074
3067
|
}
|
|
3075
3068
|
|
|
3076
|
-
// src/lib/coding/issue-task.ts
|
|
3077
|
-
var DEFAULT_TEMPLATE = "{type}/issue-{number}-{slug}";
|
|
3078
|
-
var DEFAULT_TYPE = "fix";
|
|
3079
|
-
var DEFAULT_SLUG_MAX = 48;
|
|
3080
|
-
var DEFAULT_INSTRUCTIONS = [
|
|
3081
|
-
"Work in the provided worktree. When done:",
|
|
3082
|
-
"- ensure the verification command passes (no PR on red)",
|
|
3083
|
-
"- open a PR that references this issue",
|
|
3084
|
-
"- leave risk-path / agent-judged-risky changes for human approval"
|
|
3085
|
-
].join("\n");
|
|
3086
|
-
var TYPE_RE = /^[a-z]{2,12}$/;
|
|
3087
|
-
function slugify(title, max = DEFAULT_SLUG_MAX) {
|
|
3088
|
-
return title.toLowerCase().normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, max).replace(/-$/, "");
|
|
3089
|
-
}
|
|
3090
|
-
function buildBranchName(issue, naming = {}) {
|
|
3091
|
-
const num = String(issue.number).replace(/\D/g, "");
|
|
3092
|
-
if (!num) throw new Error("issue number required");
|
|
3093
|
-
const type = issue.type && TYPE_RE.test(issue.type) ? issue.type : naming.defaultType ?? DEFAULT_TYPE;
|
|
3094
|
-
const slug = slugify(issue.title, naming.slugMax ?? DEFAULT_SLUG_MAX) || "task";
|
|
3095
|
-
return (naming.template ?? DEFAULT_TEMPLATE).replace(/\{type\}/g, type).replace(/\{number\}/g, num).replace(/\{slug\}/g, slug);
|
|
3096
|
-
}
|
|
3097
|
-
function buildTaskPrompt(issue, framing = {}) {
|
|
3098
|
-
const num = String(issue.number);
|
|
3099
|
-
const parts = [];
|
|
3100
|
-
if (framing.persona?.trim()) parts.push(framing.persona.trim());
|
|
3101
|
-
parts.push(`Issue #${num}: ${issue.title}`);
|
|
3102
|
-
parts.push(issue.body?.trim() ? issue.body.trim() : "(no description provided)");
|
|
3103
|
-
parts.push(framing.instructions?.trim() ? framing.instructions.trim() : DEFAULT_INSTRUCTIONS);
|
|
3104
|
-
return parts.join("\n\n");
|
|
3105
|
-
}
|
|
3106
|
-
|
|
3107
|
-
// src/lib/coding/merge-policy.ts
|
|
3108
|
-
var SECURE_DEFAULT_POLICY = {
|
|
3109
|
-
autoMergeEnabled: false,
|
|
3110
|
-
autoPaths: [],
|
|
3111
|
-
riskPaths: []
|
|
3112
|
-
};
|
|
3113
|
-
function globToRegExp(glob) {
|
|
3114
|
-
let re = "";
|
|
3115
|
-
for (let i = 0; i < glob.length; i++) {
|
|
3116
|
-
const c = glob[i];
|
|
3117
|
-
if (c === "*") {
|
|
3118
|
-
if (glob[i + 1] === "*") {
|
|
3119
|
-
re += ".*";
|
|
3120
|
-
i++;
|
|
3121
|
-
if (glob[i + 1] === "/") i++;
|
|
3122
|
-
} else {
|
|
3123
|
-
re += "[^/]*";
|
|
3124
|
-
}
|
|
3125
|
-
} else if (c === "?") {
|
|
3126
|
-
re += "[^/]";
|
|
3127
|
-
} else if (".+^${}()|[]\\".includes(c)) {
|
|
3128
|
-
re += `\\${c}`;
|
|
3129
|
-
} else {
|
|
3130
|
-
re += c;
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
return new RegExp(`^${re}$`);
|
|
3134
|
-
}
|
|
3135
|
-
function matchesAny(path2, globs) {
|
|
3136
|
-
return globs.some((g) => globToRegExp(g).test(path2));
|
|
3137
|
-
}
|
|
3138
|
-
function classifyChange(paths, policy = SECURE_DEFAULT_POLICY) {
|
|
3139
|
-
if (paths.length === 0) return "code";
|
|
3140
|
-
if (paths.some((p) => matchesAny(p, policy.riskPaths))) return "risk";
|
|
3141
|
-
if (policy.autoPaths.length > 0 && paths.every((p) => matchesAny(p, policy.autoPaths))) return "chore";
|
|
3142
|
-
return "code";
|
|
3143
|
-
}
|
|
3144
|
-
function decideMerge(paths, policy = SECURE_DEFAULT_POLICY, agentRisk) {
|
|
3145
|
-
const globClass = classifyChange(paths, policy);
|
|
3146
|
-
const isRisk = globClass === "risk" || agentRisk?.risky === true;
|
|
3147
|
-
const classification = isRisk ? "risk" : globClass;
|
|
3148
|
-
if (!policy.autoMergeEnabled) {
|
|
3149
|
-
return { classification, autoMerge: false, needsReview: false, needsHuman: true, reason: "auto-merge not enabled for this repo \u2014 human approval required (set autoMergeEnabled in .openape/coding.json to opt in)" };
|
|
3150
|
-
}
|
|
3151
|
-
if (isRisk) {
|
|
3152
|
-
const why = agentRisk?.risky ? `agent judged this risky${agentRisk.reason ? `: ${agentRisk.reason}` : ""}` : "matches a repo/derived risk path";
|
|
3153
|
-
return { classification, autoMerge: false, needsReview: false, needsHuman: true, reason: `${why} \u2014 human approval required` };
|
|
3154
|
-
}
|
|
3155
|
-
if (classification === "chore") {
|
|
3156
|
-
return { classification, autoMerge: true, needsReview: false, needsHuman: false, reason: "chore/docs only \u2014 auto-merge on green CI" };
|
|
3157
|
-
}
|
|
3158
|
-
return { classification, autoMerge: true, needsReview: true, needsHuman: false, reason: "code change \u2014 auto-merge after reviewer-agent approval" };
|
|
3159
|
-
}
|
|
3160
|
-
async function loadMergePolicy(worktreeDir) {
|
|
3161
|
-
const { readFile } = await import("fs/promises");
|
|
3162
|
-
const { join: join21 } = await import("path");
|
|
3163
|
-
try {
|
|
3164
|
-
const raw = await readFile(join21(worktreeDir, ".openape", "coding.json"), "utf8");
|
|
3165
|
-
const parsed = JSON.parse(raw);
|
|
3166
|
-
const mp = parsed.mergePolicy;
|
|
3167
|
-
if (!mp) return SECURE_DEFAULT_POLICY;
|
|
3168
|
-
return {
|
|
3169
|
-
autoMergeEnabled: mp.autoMergeEnabled === true,
|
|
3170
|
-
autoPaths: Array.isArray(mp.autoPaths) ? mp.autoPaths.filter((g) => typeof g === "string") : [],
|
|
3171
|
-
riskPaths: Array.isArray(mp.riskPaths) ? mp.riskPaths.filter((g) => typeof g === "string") : []
|
|
3172
|
-
};
|
|
3173
|
-
} catch {
|
|
3174
|
-
return SECURE_DEFAULT_POLICY;
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
|
|
3178
|
-
// src/lib/coding/review-gate.ts
|
|
3179
|
-
async function gateMerge(decision, req, reviewer) {
|
|
3180
|
-
if (decision.needsHuman) {
|
|
3181
|
-
return { armMerge: false, awaitingHuman: true, reason: "risk-path change \u2014 handed off to human reviewer" };
|
|
3182
|
-
}
|
|
3183
|
-
if (!decision.needsReview) {
|
|
3184
|
-
return { armMerge: true, awaitingHuman: false, reason: "chore change \u2014 no reviewer gate" };
|
|
3185
|
-
}
|
|
3186
|
-
const verdict = await reviewer({ ...req, classification: decision.classification });
|
|
3187
|
-
if (verdict.approved) {
|
|
3188
|
-
return { armMerge: true, awaitingHuman: false, reason: `reviewer approved${verdict.reason ? `: ${verdict.reason}` : ""}` };
|
|
3189
|
-
}
|
|
3190
|
-
return { armMerge: false, awaitingHuman: false, reason: `reviewer blocked${verdict.reason ? `: ${verdict.reason}` : ""}` };
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
|
-
// src/lib/coding/coding-loop.ts
|
|
3194
|
-
var DIFF_CAP = 60 * 1024;
|
|
3195
|
-
function taskIdFromIssue(issue) {
|
|
3196
|
-
const num = String(issue.number).replace(/\D/g, "");
|
|
3197
|
-
return `issue-${num || "x"}`;
|
|
3198
|
-
}
|
|
3199
|
-
async function runCodingTask(input, deps) {
|
|
3200
|
-
const shell = deps.shell ?? (async (cmd, t) => {
|
|
3201
|
-
const r = await runApeShell(cmd, t);
|
|
3202
|
-
return { stdout: r.stdout, stderr: r.stderr, exit_code: r.exit_code };
|
|
3203
|
-
});
|
|
3204
|
-
const loop = deps.runLoopImpl ?? runLoop;
|
|
3205
|
-
const log = deps.log ?? (() => {
|
|
3206
|
-
});
|
|
3207
|
-
const branch = buildBranchName(input.issue, deps.branchNaming);
|
|
3208
|
-
const taskId = taskIdFromIssue(input.issue);
|
|
3209
|
-
const worktree = worktreePathFor(taskId);
|
|
3210
|
-
log(`[coding] creating worktree ${worktree} on ${branch}`);
|
|
3211
|
-
const wt = await shell(buildCreateCommand(input.repo, taskId, branch));
|
|
3212
|
-
if (wt.exit_code !== 0) {
|
|
3213
|
-
return { branch, worktree, runStatus: "error", changedFiles: [], outcome: "run-failed", reason: `worktree create failed: ${wt.stderr.slice(0, 300)}` };
|
|
3214
|
-
}
|
|
3215
|
-
const policy = deps.resolvePolicy ? await deps.resolvePolicy(worktree) : deps.policy ?? SECURE_DEFAULT_POLICY;
|
|
3216
|
-
const prompt = buildTaskPrompt(input.issue, { persona: deps.persona });
|
|
3217
|
-
const run = await loop({
|
|
3218
|
-
config: deps.runtimeConfig,
|
|
3219
|
-
systemPrompt: deps.persona,
|
|
3220
|
-
userMessage: `${prompt}
|
|
3221
|
-
|
|
3222
|
-
Worktree: ${worktree}`,
|
|
3223
|
-
tools: deps.tools,
|
|
3224
|
-
maxSteps: deps.maxSteps,
|
|
3225
|
-
streamAggregate: deps.streamAggregate
|
|
3226
|
-
});
|
|
3227
|
-
if (process.env.OPENAPE_VERBOSE_TRACE === "1") {
|
|
3228
|
-
for (const t of run.trace) {
|
|
3229
|
-
log(`[trace] step=${t.step} type=${t.type}${t.tool ? ` tool=${t.tool}` : ""} ${t.preview}`);
|
|
3230
|
-
}
|
|
3231
|
-
}
|
|
3232
|
-
if (run.status !== "ok") {
|
|
3233
|
-
return { branch, worktree, runStatus: "error", changedFiles: [], outcome: "run-failed", reason: `coding loop errored after ${run.stepCount} steps` };
|
|
3234
|
-
}
|
|
3235
|
-
const namesRes = await shell(`git -C '${worktree}' add -A && git -C '${worktree}' diff --cached --name-only`);
|
|
3236
|
-
const changedFiles = namesRes.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
3237
|
-
if (changedFiles.length === 0) {
|
|
3238
|
-
return { branch, worktree, runStatus: "ok", changedFiles: [], outcome: "run-failed", reason: "no changes produced \u2014 nothing to PR" };
|
|
3239
|
-
}
|
|
3240
|
-
const diffRes = await shell(`git -C '${worktree}' diff --cached`);
|
|
3241
|
-
const diff = diffRes.stdout.slice(0, DIFF_CAP);
|
|
3242
|
-
const agentRisk = await deps.riskAssessor({ paths: changedFiles, diff });
|
|
3243
|
-
const decision = decideMerge(changedFiles, policy, agentRisk);
|
|
3244
|
-
log(`[coding] decision=${decision.classification} (${decision.reason})`);
|
|
3245
|
-
const authorEmail = process.env.GIT_AUTHOR_EMAIL || "coding-agent@openape.ai";
|
|
3246
|
-
const authorName = process.env.GIT_AUTHOR_NAME || "OpenApe Coding Agent";
|
|
3247
|
-
const ident = `-c user.email='${authorEmail.replace(/'/g, "")}' -c user.name='${authorName.replace(/'/g, "")}'`;
|
|
3248
|
-
const commitRes = await shell(`git -C '${worktree}' ${ident} commit -m ${shqMsg(input.issue)}`);
|
|
3249
|
-
if (commitRes.exit_code !== 0) {
|
|
3250
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "run-failed", prRef: branch, reason: `commit failed: ${(commitRes.stderr || commitRes.stdout).slice(0, 300)}` };
|
|
3251
|
-
}
|
|
3252
|
-
const pushRes = await shell(buildPushCommand(input.forge, input.repo, worktree, branch));
|
|
3253
|
-
if (pushRes.exit_code !== 0) {
|
|
3254
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "run-failed", prRef: branch, reason: `push failed: ${(pushRes.stderr || pushRes.stdout).slice(0, 300)}` };
|
|
3255
|
-
}
|
|
3256
|
-
const prCmd = buildPrCreate({ forge: input.forge, title: prTitle(input.issue), body: prBody(input.issue), head: branch });
|
|
3257
|
-
const prRes = await shell(`cd '${worktree}' && ${prCmd}`);
|
|
3258
|
-
if (prRes.exit_code !== 0) {
|
|
3259
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "run-failed", prRef: branch, reason: `pr create failed: ${(prRes.stderr || prRes.stdout).slice(0, 300)}` };
|
|
3260
|
-
}
|
|
3261
|
-
const prRef = prRes.stdout.match(/\/pull\/(\d+)|!(\d+)|\bpr\/(\d+)/i)?.slice(1).find(Boolean) ?? branch;
|
|
3262
|
-
if (decision.needsHuman) {
|
|
3263
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "awaiting-human", prRef, reason: decision.reason };
|
|
3264
|
-
}
|
|
3265
|
-
const gate = await gateMerge(decision, { prRef, diff }, deps.reviewer);
|
|
3266
|
-
if (!gate.armMerge) {
|
|
3267
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "reviewer-blocked", prRef, reason: gate.reason };
|
|
3268
|
-
}
|
|
3269
|
-
const mergeCmd = buildPrMerge({ forge: input.forge, ref: prRef, auto: true, squash: deps.squash, deleteBranch: true });
|
|
3270
|
-
await shell(`cd '${worktree}' && ${mergeCmd}`);
|
|
3271
|
-
return { branch, worktree, runStatus: "ok", changedFiles, decision, outcome: "auto-armed", prRef, reason: gate.reason };
|
|
3272
|
-
}
|
|
3273
|
-
function prTitle(issue) {
|
|
3274
|
-
return `${issue.type ?? "fix"}: ${issue.title} (#${String(issue.number).replace(/\D/g, "")})`;
|
|
3275
|
-
}
|
|
3276
|
-
function prBody(issue) {
|
|
3277
|
-
return `Resolves #${String(issue.number).replace(/\D/g, "")}.
|
|
3278
|
-
|
|
3279
|
-
Automated by the OpenApe coding agent.`;
|
|
3280
|
-
}
|
|
3281
|
-
function shqMsg(issue) {
|
|
3282
|
-
return `'${prTitle(issue).replace(/'/g, "'\\''")}'`;
|
|
3283
|
-
}
|
|
3284
|
-
function buildPushCommand(forge, repo, worktree, branch) {
|
|
3285
|
-
const base = `GIT_TERMINAL_PROMPT=0 git -C '${worktree}' -c credential.helper=`;
|
|
3286
|
-
if (forge === "github") {
|
|
3287
|
-
const slug = repo.replace(/^[a-z]+:\/\/[^/]+\//i, "").replace(/\.git$/, "").replace(/['"\s]/g, "");
|
|
3288
|
-
return `${base} push "https://x-access-token:\${GH_TOKEN}@github.com/${slug}.git" '${branch}'`;
|
|
3289
|
-
}
|
|
3290
|
-
return `${base} push -u origin '${branch}'`;
|
|
3291
|
-
}
|
|
3292
|
-
|
|
3293
|
-
// src/lib/coding/llm-review.ts
|
|
3294
|
-
var DIFF_CAP2 = 48 * 1024;
|
|
3295
|
-
async function jsonCompletion(config, system, user, fetchImpl = fetch) {
|
|
3296
|
-
try {
|
|
3297
|
-
const res = await fetchImpl(`${config.apiBase}/chat/completions`, {
|
|
3298
|
-
method: "POST",
|
|
3299
|
-
headers: { "authorization": `Bearer ${config.apiKey}`, "content-type": "application/json" },
|
|
3300
|
-
body: JSON.stringify({
|
|
3301
|
-
model: config.model,
|
|
3302
|
-
messages: [{ role: "system", content: system }, { role: "user", content: user }],
|
|
3303
|
-
response_format: { type: "json_object" }
|
|
3304
|
-
})
|
|
3305
|
-
});
|
|
3306
|
-
if (!res.ok) return null;
|
|
3307
|
-
const data = await res.json();
|
|
3308
|
-
const content = data.choices?.[0]?.message?.content;
|
|
3309
|
-
if (!content) return null;
|
|
3310
|
-
return JSON.parse(content);
|
|
3311
|
-
} catch {
|
|
3312
|
-
return null;
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
var RISK_SYSTEM = [
|
|
3316
|
-
"You are a security/risk classifier for an autonomous coding agent.",
|
|
3317
|
-
"Given a diff + changed file paths, decide whether merging it WITHOUT a human is risky.",
|
|
3318
|
-
"Risky = touches authentication, authorization, secrets/credentials, payment, data migrations,",
|
|
3319
|
-
"deploy/release/CI config, cryptography, deletion of data, or anything whose failure is hard to",
|
|
3320
|
-
"reverse in production. Routine code/tests/docs/refactors are NOT risky.",
|
|
3321
|
-
'Respond ONLY as JSON: {"risky": boolean, "reason": string}.'
|
|
3322
|
-
].join(" ");
|
|
3323
|
-
function createLlmRiskAssessor(config, fetchImpl) {
|
|
3324
|
-
return async ({ paths, diff }) => {
|
|
3325
|
-
const user = `Changed files:
|
|
3326
|
-
${paths.join("\n")}
|
|
3327
|
-
|
|
3328
|
-
Diff (truncated):
|
|
3329
|
-
${diff.slice(0, DIFF_CAP2)}`;
|
|
3330
|
-
const out = await jsonCompletion(config, RISK_SYSTEM, user, fetchImpl);
|
|
3331
|
-
if (!out || typeof out.risky !== "boolean") {
|
|
3332
|
-
return { risky: true, reason: "risk classifier unavailable/unparseable \u2014 treating as risky (fail-safe)" };
|
|
3333
|
-
}
|
|
3334
|
-
return { risky: out.risky, reason: typeof out.reason === "string" ? out.reason : void 0 };
|
|
3335
|
-
};
|
|
3336
|
-
}
|
|
3337
|
-
var REVIEW_SYSTEM = [
|
|
3338
|
-
"You are a code reviewer for an autonomous coding agent.",
|
|
3339
|
-
"Given a PR diff, decide whether it is correct, safe, and complete enough to auto-merge.",
|
|
3340
|
-
"Approve only if you would be comfortable shipping it without further human review.",
|
|
3341
|
-
"Block if you see bugs, missing tests, security issues, or incomplete work.",
|
|
3342
|
-
'Respond ONLY as JSON: {"approved": boolean, "reason": string}.'
|
|
3343
|
-
].join(" ");
|
|
3344
|
-
function createLlmReviewer(config, fetchImpl) {
|
|
3345
|
-
return async ({ prRef, diff }) => {
|
|
3346
|
-
const user = `PR ${String(prRef)} diff (truncated):
|
|
3347
|
-
${diff.slice(0, DIFF_CAP2)}`;
|
|
3348
|
-
const out = await jsonCompletion(config, REVIEW_SYSTEM, user, fetchImpl);
|
|
3349
|
-
if (!out || typeof out.approved !== "boolean") {
|
|
3350
|
-
return { approved: false, reason: "reviewer unavailable/unparseable \u2014 blocking (fail-safe)" };
|
|
3351
|
-
}
|
|
3352
|
-
return { approved: out.approved, reason: typeof out.reason === "string" ? out.reason : void 0 };
|
|
3353
|
-
};
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
// src/lib/coding/derive-policy.ts
|
|
3357
|
-
function indentOf(line) {
|
|
3358
|
-
return line.length - line.trimStart().length;
|
|
3359
|
-
}
|
|
3360
|
-
function unquote(s) {
|
|
3361
|
-
return s.trim().replace(/^['"]|['"]$/g, "").trim();
|
|
3362
|
-
}
|
|
3363
|
-
function extractWorkflowPaths(yamlText) {
|
|
3364
|
-
const out = [];
|
|
3365
|
-
const lines = yamlText.split("\n");
|
|
3366
|
-
let inBlock = false;
|
|
3367
|
-
let keyIndent = -1;
|
|
3368
|
-
for (const line of lines) {
|
|
3369
|
-
if (inBlock) {
|
|
3370
|
-
const m = /^\s*-\s*(\S.*)$/.exec(line);
|
|
3371
|
-
if (m && indentOf(line) > keyIndent) {
|
|
3372
|
-
out.push(unquote(m[1]));
|
|
3373
|
-
continue;
|
|
3374
|
-
}
|
|
3375
|
-
if (line.trim() !== "") inBlock = false;
|
|
3376
|
-
}
|
|
3377
|
-
const inline = /^\s*paths:\s*\[(.+)\]\s*$/.exec(line);
|
|
3378
|
-
if (inline) {
|
|
3379
|
-
for (const tok of inline[1].split(",")) {
|
|
3380
|
-
const g = unquote(tok);
|
|
3381
|
-
if (g) out.push(g);
|
|
3382
|
-
}
|
|
3383
|
-
continue;
|
|
3384
|
-
}
|
|
3385
|
-
if (/^\s*paths:\s*$/.test(line)) {
|
|
3386
|
-
inBlock = true;
|
|
3387
|
-
keyIndent = indentOf(line);
|
|
3388
|
-
}
|
|
3389
|
-
}
|
|
3390
|
-
return [...new Set(out)];
|
|
3391
|
-
}
|
|
3392
|
-
function parseCodeowners(text) {
|
|
3393
|
-
const out = [];
|
|
3394
|
-
for (const raw of text.split("\n")) {
|
|
3395
|
-
const line = raw.trim();
|
|
3396
|
-
if (!line || line.startsWith("#")) continue;
|
|
3397
|
-
const pattern = line.split(/\s+/)[0];
|
|
3398
|
-
if (!pattern || pattern.startsWith("@")) continue;
|
|
3399
|
-
let g = pattern.replace(/^\//, "");
|
|
3400
|
-
if (g.endsWith("/")) g += "**";
|
|
3401
|
-
if (g) out.push(g);
|
|
3402
|
-
}
|
|
3403
|
-
return [...new Set(out)];
|
|
3404
|
-
}
|
|
3405
|
-
async function deriveRiskGlobs(worktreeDir) {
|
|
3406
|
-
const { readFile, readdir } = await import("fs/promises");
|
|
3407
|
-
const { join: join21 } = await import("path");
|
|
3408
|
-
const globs = /* @__PURE__ */ new Set();
|
|
3409
|
-
try {
|
|
3410
|
-
const wfDir = join21(worktreeDir, ".github", "workflows");
|
|
3411
|
-
const files = await readdir(wfDir);
|
|
3412
|
-
for (const f of files) {
|
|
3413
|
-
if (!/deploy.*\.ya?ml$/i.test(f)) continue;
|
|
3414
|
-
try {
|
|
3415
|
-
const text = await readFile(join21(wfDir, f), "utf8");
|
|
3416
|
-
for (const g of extractWorkflowPaths(text)) globs.add(g);
|
|
3417
|
-
} catch {
|
|
3418
|
-
}
|
|
3419
|
-
}
|
|
3420
|
-
} catch {
|
|
3421
|
-
}
|
|
3422
|
-
for (const loc of [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"]) {
|
|
3423
|
-
try {
|
|
3424
|
-
const text = await readFile(join21(worktreeDir, loc), "utf8");
|
|
3425
|
-
for (const g of parseCodeowners(text)) globs.add(g);
|
|
3426
|
-
break;
|
|
3427
|
-
} catch {
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
return [...globs];
|
|
3431
|
-
}
|
|
3432
|
-
async function resolveMergePolicy(worktreeDir) {
|
|
3433
|
-
const explicit = await loadMergePolicy(worktreeDir).catch(() => SECURE_DEFAULT_POLICY);
|
|
3434
|
-
const derived = await deriveRiskGlobs(worktreeDir).catch(() => []);
|
|
3435
|
-
return {
|
|
3436
|
-
autoMergeEnabled: explicit.autoMergeEnabled,
|
|
3437
|
-
autoPaths: explicit.autoPaths,
|
|
3438
|
-
riskPaths: [.../* @__PURE__ */ new Set([...explicit.riskPaths, ...derived])]
|
|
3439
|
-
};
|
|
3440
|
-
}
|
|
3441
|
-
|
|
3442
3069
|
// src/commands/agents/code.ts
|
|
3443
3070
|
var CliError2 = class extends Error {
|
|
3444
3071
|
};
|
|
@@ -4014,6 +3641,7 @@ import { homedir as homedir7 } from "os";
|
|
|
4014
3641
|
import { join as join9 } from "path";
|
|
4015
3642
|
import { defineCommand as defineCommand28 } from "citty";
|
|
4016
3643
|
import consola25 from "consola";
|
|
3644
|
+
import { taskTools as taskTools2, runLoop } from "@openape/agent-runtime";
|
|
4017
3645
|
var AUTH_PATH = join9(homedir7(), ".config", "apes", "auth.json");
|
|
4018
3646
|
var TASK_CACHE_DIR = join9(homedir7(), ".openape", "agent", "tasks");
|
|
4019
3647
|
function readAuth() {
|
|
@@ -4119,7 +3747,7 @@ var runAgentCommand = defineCommand28({
|
|
|
4119
3747
|
const config = readLitellmConfig2(args.model);
|
|
4120
3748
|
let tools;
|
|
4121
3749
|
try {
|
|
4122
|
-
tools =
|
|
3750
|
+
tools = taskTools2(spec.tools);
|
|
4123
3751
|
} catch (err) {
|
|
4124
3752
|
throw new CliError(`task ${taskId}: ${err.message}`);
|
|
4125
3753
|
}
|
|
@@ -4186,6 +3814,7 @@ import { homedir as homedir8 } from "os";
|
|
|
4186
3814
|
import { join as join10 } from "path";
|
|
4187
3815
|
import { createInterface } from "readline";
|
|
4188
3816
|
import { defineCommand as defineCommand29 } from "citty";
|
|
3817
|
+
import { taskTools as taskTools3, runLoop as runLoop2, RpcSessionMap } from "@openape/agent-runtime";
|
|
4189
3818
|
var AUTH_PATH2 = join10(homedir8(), ".config", "apes", "auth.json");
|
|
4190
3819
|
function readLitellmConfig3(model) {
|
|
4191
3820
|
const envPath = join10(homedir8(), "litellm", ".env");
|
|
@@ -4264,7 +3893,7 @@ var serveAgentCommand = defineCommand29({
|
|
|
4264
3893
|
});
|
|
4265
3894
|
async function handleInbound(msg, sessions) {
|
|
4266
3895
|
const config = readLitellmConfig3(msg.model);
|
|
4267
|
-
const tools =
|
|
3896
|
+
const tools = taskTools3(msg.tools ?? []);
|
|
4268
3897
|
const maxSteps = msg.max_steps ?? 10;
|
|
4269
3898
|
let session = sessions.get(msg.session_id);
|
|
4270
3899
|
if (!session) {
|
|
@@ -4277,7 +3906,7 @@ async function handleInbound(msg, sessions) {
|
|
|
4277
3906
|
};
|
|
4278
3907
|
sessions.put(msg.session_id, session);
|
|
4279
3908
|
}
|
|
4280
|
-
const result = await
|
|
3909
|
+
const result = await runLoop2({
|
|
4281
3910
|
config,
|
|
4282
3911
|
systemPrompt: session.systemPrompt,
|
|
4283
3912
|
userMessage: msg.user_msg,
|
|
@@ -7009,7 +6638,7 @@ var mcpCommand = defineCommand52({
|
|
|
7009
6638
|
if (transport !== "stdio" && transport !== "sse") {
|
|
7010
6639
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
7011
6640
|
}
|
|
7012
|
-
const { startMcpServer } = await import("./server-
|
|
6641
|
+
const { startMcpServer } = await import("./server-QN35XDYH.js");
|
|
7013
6642
|
await startMcpServer(transport, port);
|
|
7014
6643
|
}
|
|
7015
6644
|
});
|
|
@@ -7647,7 +7276,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
7647
7276
|
}
|
|
7648
7277
|
}
|
|
7649
7278
|
async function runHealth(args) {
|
|
7650
|
-
const version = true ? "1.
|
|
7279
|
+
const version = true ? "1.29.0" : "0.0.0";
|
|
7651
7280
|
const auth = loadAuth();
|
|
7652
7281
|
if (!auth) {
|
|
7653
7282
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -7920,10 +7549,10 @@ if (shellRewrite) {
|
|
|
7920
7549
|
if (shellRewrite.action === "rewrite") {
|
|
7921
7550
|
process.argv = shellRewrite.argv;
|
|
7922
7551
|
} else if (shellRewrite.action === "version") {
|
|
7923
|
-
console.log(`ape-shell ${"1.
|
|
7552
|
+
console.log(`ape-shell ${"1.29.0"} (OpenApe DDISA shell wrapper)`);
|
|
7924
7553
|
process.exit(0);
|
|
7925
7554
|
} else if (shellRewrite.action === "help") {
|
|
7926
|
-
console.log(`ape-shell ${"1.
|
|
7555
|
+
console.log(`ape-shell ${"1.29.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
7927
7556
|
console.log("");
|
|
7928
7557
|
console.log("Usage:");
|
|
7929
7558
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -7938,7 +7567,7 @@ if (shellRewrite) {
|
|
|
7938
7567
|
console.log(" --help, -h Show this help message");
|
|
7939
7568
|
process.exit(0);
|
|
7940
7569
|
} else if (shellRewrite.action === "interactive") {
|
|
7941
|
-
const { runInteractiveShell } = await import("./orchestrator-
|
|
7570
|
+
const { runInteractiveShell } = await import("./orchestrator-P7QFDBBK.js");
|
|
7942
7571
|
await runInteractiveShell();
|
|
7943
7572
|
process.exit(0);
|
|
7944
7573
|
} else {
|
|
@@ -7981,7 +7610,7 @@ var configCommand = defineCommand64({
|
|
|
7981
7610
|
var main = defineCommand64({
|
|
7982
7611
|
meta: {
|
|
7983
7612
|
name: "apes",
|
|
7984
|
-
version: "1.
|
|
7613
|
+
version: "1.29.0",
|
|
7985
7614
|
description: "Unified CLI for OpenApe"
|
|
7986
7615
|
},
|
|
7987
7616
|
subCommands: {
|
|
@@ -8039,7 +7668,7 @@ async function maybeRefreshAuth() {
|
|
|
8039
7668
|
}
|
|
8040
7669
|
}
|
|
8041
7670
|
await maybeRefreshAuth();
|
|
8042
|
-
await maybeWarnStaleVersion("1.
|
|
7671
|
+
await maybeWarnStaleVersion("1.29.0").catch(() => {
|
|
8043
7672
|
});
|
|
8044
7673
|
runMain(main).catch((err) => {
|
|
8045
7674
|
if (err instanceof CliExit) {
|