auto-mode 3.1.3 → 3.1.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.
- package/dist/cli.js +6 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/{parse-action-request-Czvdr5DS.js → parse-action-request-CHuF6LYV.js} +204 -78
- package/dist/{start-judge-server-CDYdRR4I.js → start-judge-server-BaeD4mmu.js} +1 -1
- package/dist/start-judge-session-B65PMvQ6.js +1 -0
- package/dist/{start-judge-session-vhUxe9-V.js → start-judge-session-C_k8lacU.js} +17 -12
- package/mods/auto-mode/contract/types.ts +1 -1
- package/mods/auto-mode/hooks/build-prompt-context.ts +5 -2
- package/package.json +2 -1
- package/dist/start-judge-session-C0eRJFlg.js +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as pickJudgeLane, E as updateJSONFile, S as CLI_LIMITS, T as resolveXDGDir, _ as readCheckout, a as resolveSessionScopePath, b as loadConfig, c as splitGitCommand, d as loadCheckoutFacts, f as writeActionDiagnostic, g as isUnresolvedWord, h as resolveShellPath, i as classifyAction, l as loadPolicy, m as collectShellCommands, n as readRuntime, o as loadSessionScope, p as toRepositorySlug, s as EMPTY_SESSION_SCOPE, t as parseActionRequest, u as resolveShippedPolicyDir, v as readGitEntry, w as M, x as runCapped, y as DEFAULT_SCOPE_SOURCES } from "./parse-action-request-
|
|
3
|
-
import { a as literal, d as string, i as discriminatedUnion, r as boolean, s as number, u as strictObject } from "./schemas-B80Yxv6m.js";
|
|
2
|
+
import { C as pickJudgeLane, E as updateJSONFile, S as CLI_LIMITS, T as resolveXDGDir, _ as readCheckout, a as resolveSessionScopePath, b as loadConfig, c as splitGitCommand, d as loadCheckoutFacts, f as writeActionDiagnostic, g as isUnresolvedWord, h as resolveShellPath, i as classifyAction, l as loadPolicy, m as collectShellCommands, n as readRuntime, o as loadSessionScope, p as toRepositorySlug, s as EMPTY_SESSION_SCOPE, t as parseActionRequest, u as resolveShippedPolicyDir, v as readGitEntry, w as M, x as runCapped, y as DEFAULT_SCOPE_SOURCES } from "./parse-action-request-CHuF6LYV.js";
|
|
3
|
+
import { a as literal, d as string, i as discriminatedUnion, l as record, p as unknown, r as boolean, s as number, u as strictObject } from "./schemas-B80Yxv6m.js";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { chmod, mkdir, open, readFile, realpath, stat } from "node:fs/promises";
|
|
6
6
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
@@ -2665,13 +2665,15 @@ function startDetachedProcess(file, args, env) {
|
|
|
2665
2665
|
}
|
|
2666
2666
|
//#endregion
|
|
2667
2667
|
//#region src/judge/judge-server-response-schema.ts
|
|
2668
|
+
const modelUsageSchema = record(string(), unknown());
|
|
2668
2669
|
const judgeServerResponseSchema = discriminatedUnion("kind", [strictObject({
|
|
2669
2670
|
kind: literal("reply"),
|
|
2670
2671
|
text: string(),
|
|
2671
2672
|
model: string(),
|
|
2672
2673
|
safetyStops: number().nullable(),
|
|
2673
2674
|
fallback: boolean(),
|
|
2674
|
-
warm: boolean().nullable()
|
|
2675
|
+
warm: boolean().nullable(),
|
|
2676
|
+
modelUsage: modelUsageSchema.nullable()
|
|
2675
2677
|
}), strictObject({
|
|
2676
2678
|
kind: literal("failure"),
|
|
2677
2679
|
aborted: boolean(),
|
|
@@ -3283,7 +3285,7 @@ async function runJudgeServer(io, socketPath) {
|
|
|
3283
3285
|
io.stderr.write(`auto-mode: judge-server needs a socket path\n\n${USAGE}`);
|
|
3284
3286
|
return 2;
|
|
3285
3287
|
}
|
|
3286
|
-
await (await (await import("./start-judge-server-
|
|
3288
|
+
await (await (await import("./start-judge-server-BaeD4mmu.js")).startJudgeServer({
|
|
3287
3289
|
socketPath,
|
|
3288
3290
|
env: io.runtime.host.env,
|
|
3289
3291
|
idleMs: JUDGE_IDLE_MS
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ interface JudgeMessageReply {
|
|
|
20
20
|
readonly safetyStops: number | null;
|
|
21
21
|
readonly fallback: boolean;
|
|
22
22
|
readonly warm: boolean | null;
|
|
23
|
+
readonly modelUsage: Readonly<Record<string, unknown>> | null;
|
|
23
24
|
}
|
|
24
25
|
interface JudgeSession {
|
|
25
26
|
readonly review: (model: string, request: Readonly<ModelRequest>, signal: Readonly<AbortSignal>) => Promise<JudgeMessageReply>;
|
|
@@ -30,6 +31,7 @@ interface JudgeDiagnostics {
|
|
|
30
31
|
readonly status: JudgeStatus;
|
|
31
32
|
readonly failureReason: JudgeFailure | null;
|
|
32
33
|
readonly overturnBasis: OverturnBasis | null;
|
|
34
|
+
readonly overturnReason: string | null;
|
|
33
35
|
readonly model: string;
|
|
34
36
|
readonly answeringModel: string | null;
|
|
35
37
|
readonly safetyStops: number | null;
|
|
@@ -145,10 +147,10 @@ type MessageOrigin = (typeof MESSAGE_ORIGINS)[number];
|
|
|
145
147
|
interface UserTask {
|
|
146
148
|
readonly text: string;
|
|
147
149
|
readonly origin: MessageOrigin;
|
|
150
|
+
readonly position?: 'opening' | undefined;
|
|
148
151
|
}
|
|
149
152
|
interface DirectUserMessage extends UserTask {
|
|
150
153
|
readonly freshness?: 'stale' | undefined;
|
|
151
|
-
readonly position?: 'opening' | undefined;
|
|
152
154
|
}
|
|
153
155
|
interface DelegatedTask {
|
|
154
156
|
readonly text: string;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as loadConfig$1, i as classifyAction$1, n as readRuntime, r as readHostEnvironment, t as parseActionRequest } from "./parse-action-request-
|
|
1
|
+
import { b as loadConfig$1, i as classifyAction$1, n as readRuntime, r as readHostEnvironment, t as parseActionRequest } from "./parse-action-request-CHuF6LYV.js";
|
|
2
2
|
//#region src/index.ts
|
|
3
3
|
function classifyAction(request, config, options = {}) {
|
|
4
4
|
return classifyAction$1(request, config, {
|
|
@@ -251,6 +251,25 @@ async function tryUpdateDenialBudget(stderr, path, plan) {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
//#endregion
|
|
254
|
+
//#region src/scope/find-reply-channel.ts
|
|
255
|
+
function findReplyChannel(tool, input, channels) {
|
|
256
|
+
return channels.find((channel) => channel.tool === tool && input[channel.field] === channel.recipient) ?? null;
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/bypass/classify-reply-call.ts
|
|
260
|
+
async function classifyReplyCall(call, scope, hasConfiguredDenies) {
|
|
261
|
+
const channel = scope === null ? null : findReplyChannel(call.toolName, call.toolInput, scope.replyChannels);
|
|
262
|
+
if (channel === null) return { kind: "none" };
|
|
263
|
+
if ((await import("./classify-reply-message-CdHLlNXA.js")).classifyReplyMessage(call.toolInput).kind !== "bypass") return { kind: "none" };
|
|
264
|
+
return hasConfiguredDenies ? {
|
|
265
|
+
kind: "exempt",
|
|
266
|
+
rules: ["Outbound Communication"]
|
|
267
|
+
} : {
|
|
268
|
+
kind: "bypass",
|
|
269
|
+
channel
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
254
273
|
//#region src/state/resolve-xdg-dir.ts
|
|
255
274
|
const BASE_DIRS = {
|
|
256
275
|
config: {
|
|
@@ -1098,11 +1117,6 @@ const SCRATCH_PATHS = [
|
|
|
1098
1117
|
"/dev/stderr"
|
|
1099
1118
|
];
|
|
1100
1119
|
//#endregion
|
|
1101
|
-
//#region src/scope/find-reply-channel.ts
|
|
1102
|
-
function findReplyChannel(tool, input, channels) {
|
|
1103
|
-
return channels.find((channel) => channel.tool === tool && input[channel.field] === channel.recipient) ?? null;
|
|
1104
|
-
}
|
|
1105
|
-
//#endregion
|
|
1106
1120
|
//#region src/shell/is-unresolved-word.ts
|
|
1107
1121
|
function isUnresolvedWord(word) {
|
|
1108
1122
|
return /[$`]/u.test(word);
|
|
@@ -3091,7 +3105,7 @@ async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
|
3091
3105
|
const agentKey = agentID === null ? payload.sessionID : `${payload.sessionID}\0${agentID}`;
|
|
3092
3106
|
const isReviewed = entry.judge !== void 0 || entry.grant !== void 0;
|
|
3093
3107
|
const record = {
|
|
3094
|
-
schemaVersion:
|
|
3108
|
+
schemaVersion: 9,
|
|
3095
3109
|
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3096
3110
|
invocationID: entry.invocationID,
|
|
3097
3111
|
sessionHash: toHash(payload.sessionID),
|
|
@@ -3103,9 +3117,10 @@ async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
|
3103
3117
|
denials: entry.denials ?? null,
|
|
3104
3118
|
escalation: entry.escalation ?? false,
|
|
3105
3119
|
diagnostics: entry.diagnostics ?? null,
|
|
3106
|
-
judge: entry.judge
|
|
3107
|
-
grant: entry.grant
|
|
3108
|
-
action: isReviewed ? buildLoggedAction(payload) : null
|
|
3120
|
+
judge: entry.judge === void 0 ? null : buildLoggedReview(entry.judge),
|
|
3121
|
+
grant: entry.grant === void 0 ? null : buildLoggedReview(entry.grant),
|
|
3122
|
+
action: isReviewed ? buildLoggedAction(payload) : null,
|
|
3123
|
+
taskEvidence: buildTaskEvidence(entry.taskContext ?? payload.decisionContext)
|
|
3109
3124
|
};
|
|
3110
3125
|
try {
|
|
3111
3126
|
await mkdir(dirname(path), {
|
|
@@ -3120,6 +3135,17 @@ async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
|
3120
3135
|
function toHash(value) {
|
|
3121
3136
|
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
3122
3137
|
}
|
|
3138
|
+
function buildLoggedReview(review) {
|
|
3139
|
+
if (review.overturnReason === null) return review;
|
|
3140
|
+
const reason = buildSecretFreeText(review.overturnReason, () => "[secret]");
|
|
3141
|
+
return {
|
|
3142
|
+
...review,
|
|
3143
|
+
overturnReason: findSecret({
|
|
3144
|
+
text: reason,
|
|
3145
|
+
path: null
|
|
3146
|
+
}) === null ? reason : null
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3123
3149
|
const LOGGED_INPUT_MAX_CHARS = 2e3;
|
|
3124
3150
|
function buildLoggedAction(payload) {
|
|
3125
3151
|
const redacted = buildSecretFreeValue(payload.toolInput);
|
|
@@ -3148,6 +3174,34 @@ function hasSecret(value) {
|
|
|
3148
3174
|
if (typeof value === "object" && value !== null) return Object.values(value).some((item) => hasSecret(item));
|
|
3149
3175
|
return false;
|
|
3150
3176
|
}
|
|
3177
|
+
function buildTaskEvidence(context) {
|
|
3178
|
+
if (context === void 0) return null;
|
|
3179
|
+
const message = context.lastDirectUserMessage;
|
|
3180
|
+
return {
|
|
3181
|
+
userMessage: message === null ? null : message.freshness ?? "current",
|
|
3182
|
+
originalUserTask: context.originalUserTask?.origin ?? null,
|
|
3183
|
+
delegatedTask: context.delegatedTask !== null
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
//#endregion
|
|
3187
|
+
//#region src/diagnostics/write-judge-usage.ts
|
|
3188
|
+
async function writeJudgeUsage(usage, host, warnings) {
|
|
3189
|
+
const path = host.env["AUTO_MODE_DEBUG_PATH"];
|
|
3190
|
+
if (path === void 0 || path === "") return;
|
|
3191
|
+
const line = {
|
|
3192
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3193
|
+
...usage
|
|
3194
|
+
};
|
|
3195
|
+
try {
|
|
3196
|
+
await mkdir(dirname(path), {
|
|
3197
|
+
recursive: true,
|
|
3198
|
+
mode: 448
|
|
3199
|
+
});
|
|
3200
|
+
await appendFile(path, `${JSON.stringify(line)}\n`, { mode: 384 });
|
|
3201
|
+
} catch {
|
|
3202
|
+
warnings.write("auto-mode: judge debug line unavailable\n");
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3151
3205
|
//#endregion
|
|
3152
3206
|
//#region src/git/build-repository-context.ts
|
|
3153
3207
|
function buildRepositoryContext(facts) {
|
|
@@ -3571,18 +3625,24 @@ function buildJudgedVerdict(rule, basis, reply, options) {
|
|
|
3571
3625
|
if (reply === null || reply.kind === "unreadable") return {
|
|
3572
3626
|
verdict: template,
|
|
3573
3627
|
status: "failed",
|
|
3574
|
-
overturnBasis: null
|
|
3628
|
+
overturnBasis: null,
|
|
3629
|
+
overturnReason: null
|
|
3575
3630
|
};
|
|
3576
3631
|
if (reply.kind === "overturn") {
|
|
3577
3632
|
const clearsHardRule = reply.basis === "misread" && options.misreadClears === true;
|
|
3578
|
-
|
|
3633
|
+
const isHeld = rule.tier === "hard" && !clearsHardRule || reply.basis === "misread" && options.overturns === "consent" || reply.basis === "consent" && !options.hasUserMessage;
|
|
3634
|
+
const overturn = {
|
|
3635
|
+
overturnBasis: reply.basis,
|
|
3636
|
+
overturnReason: formatJudgeText(buildSecretFreeText(reply.reason, () => "[secret]"))
|
|
3637
|
+
};
|
|
3638
|
+
return isHeld ? {
|
|
3579
3639
|
verdict: template,
|
|
3580
3640
|
status: "held",
|
|
3581
|
-
|
|
3641
|
+
...overturn
|
|
3582
3642
|
} : {
|
|
3583
3643
|
verdict: { kind: "allow" },
|
|
3584
3644
|
status: "overturned",
|
|
3585
|
-
|
|
3645
|
+
...overturn
|
|
3586
3646
|
};
|
|
3587
3647
|
}
|
|
3588
3648
|
return {
|
|
@@ -3592,7 +3652,8 @@ function buildJudgedVerdict(rule, basis, reply, options) {
|
|
|
3592
3652
|
reason: `The reviewer confirmed the ${rule.name} deny: ${formatJudgeText(reply.reason)}`
|
|
3593
3653
|
},
|
|
3594
3654
|
status: "confirmed",
|
|
3595
|
-
overturnBasis: null
|
|
3655
|
+
overturnBasis: null,
|
|
3656
|
+
overturnReason: null
|
|
3596
3657
|
};
|
|
3597
3658
|
}
|
|
3598
3659
|
function formatJudgeText(text) {
|
|
@@ -3808,7 +3869,7 @@ function sendJudgeMessage(provider, request, transport, signal) {
|
|
|
3808
3869
|
}
|
|
3809
3870
|
async function sendToAgentSDK(provider, request, transport, signal) {
|
|
3810
3871
|
if (transport.session !== void 0) return transport.session.review(provider.model, request, signal);
|
|
3811
|
-
const session = (await import("./start-judge-session-
|
|
3872
|
+
const session = (await import("./start-judge-session-B65PMvQ6.js")).startJudgeSession(transport.env);
|
|
3812
3873
|
try {
|
|
3813
3874
|
return await session.review(provider.model, request, signal);
|
|
3814
3875
|
} finally {
|
|
@@ -3822,7 +3883,8 @@ async function sendToMessagesAPI(provider, request, transport, signal) {
|
|
|
3822
3883
|
model: provider.model,
|
|
3823
3884
|
safetyStops: null,
|
|
3824
3885
|
fallback: false,
|
|
3825
|
-
warm: null
|
|
3886
|
+
warm: null,
|
|
3887
|
+
modelUsage: null
|
|
3826
3888
|
};
|
|
3827
3889
|
}
|
|
3828
3890
|
//#endregion
|
|
@@ -3872,6 +3934,11 @@ async function classifyWithJudge(payload, evidence, judge, options) {
|
|
|
3872
3934
|
throw error;
|
|
3873
3935
|
});
|
|
3874
3936
|
replies = parseJudgeReplies(answered.text, evidence.map((entry) => entry.rule.name));
|
|
3937
|
+
if (answered.modelUsage !== null) await options.onUsage?.({
|
|
3938
|
+
model: judge.model,
|
|
3939
|
+
answeringModel: answered.model,
|
|
3940
|
+
modelUsage: answered.modelUsage
|
|
3941
|
+
});
|
|
3875
3942
|
failure = null;
|
|
3876
3943
|
} catch {}
|
|
3877
3944
|
const elapsedMs = Math.round(performance.now() - start);
|
|
@@ -3890,6 +3957,7 @@ async function classifyWithJudge(payload, evidence, judge, options) {
|
|
|
3890
3957
|
status: verdict.status,
|
|
3891
3958
|
failureReason: failure ?? (reply?.kind === "unreadable" ? "unreadable" : null),
|
|
3892
3959
|
overturnBasis: verdict.overturnBasis,
|
|
3960
|
+
overturnReason: verdict.overturnReason,
|
|
3893
3961
|
model: judge.model,
|
|
3894
3962
|
answeringModel: answered?.model ?? null,
|
|
3895
3963
|
safetyStops: answered?.safetyStops ?? null,
|
|
@@ -3902,6 +3970,31 @@ async function classifyWithJudge(payload, evidence, judge, options) {
|
|
|
3902
3970
|
});
|
|
3903
3971
|
}
|
|
3904
3972
|
//#endregion
|
|
3973
|
+
//#region src/model/build-budget-omitted-context.ts
|
|
3974
|
+
function buildBudgetOmittedContext(context, field) {
|
|
3975
|
+
return {
|
|
3976
|
+
...context,
|
|
3977
|
+
[field]: null,
|
|
3978
|
+
omittedTaskContext: [...context.omittedTaskContext.filter((item) => item.field !== field), {
|
|
3979
|
+
field,
|
|
3980
|
+
reason: "budget"
|
|
3981
|
+
}]
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
//#endregion
|
|
3985
|
+
//#region src/model/build-task-context.ts
|
|
3986
|
+
function buildTaskContext(context) {
|
|
3987
|
+
let result = {
|
|
3988
|
+
...context,
|
|
3989
|
+
omittedTaskContext: [...context.omittedTaskContext]
|
|
3990
|
+
};
|
|
3991
|
+
for (const field of ["originalUserTask", "delegatedTask"]) {
|
|
3992
|
+
const task = result[field];
|
|
3993
|
+
if (task !== null && Buffer.byteLength(task.text) > 4096) result = buildBudgetOmittedContext(result, field);
|
|
3994
|
+
}
|
|
3995
|
+
return result;
|
|
3996
|
+
}
|
|
3997
|
+
//#endregion
|
|
3905
3998
|
//#region src/model/build-task-scope-summary.ts
|
|
3906
3999
|
function buildTaskScopeSummary(scope) {
|
|
3907
4000
|
return {
|
|
@@ -4186,31 +4279,6 @@ function resolveHooksPath(path, worktree, home) {
|
|
|
4186
4279
|
return path.startsWith("~/") ? join(home, path.slice(2)) : resolve(worktree, path);
|
|
4187
4280
|
}
|
|
4188
4281
|
//#endregion
|
|
4189
|
-
//#region src/model/build-budget-omitted-context.ts
|
|
4190
|
-
function buildBudgetOmittedContext(context, field) {
|
|
4191
|
-
return {
|
|
4192
|
-
...context,
|
|
4193
|
-
[field]: null,
|
|
4194
|
-
omittedTaskContext: [...context.omittedTaskContext.filter((item) => item.field !== field), {
|
|
4195
|
-
field,
|
|
4196
|
-
reason: "budget"
|
|
4197
|
-
}]
|
|
4198
|
-
};
|
|
4199
|
-
}
|
|
4200
|
-
//#endregion
|
|
4201
|
-
//#region src/model/build-task-context.ts
|
|
4202
|
-
function buildTaskContext(context) {
|
|
4203
|
-
let result = {
|
|
4204
|
-
...context,
|
|
4205
|
-
omittedTaskContext: [...context.omittedTaskContext]
|
|
4206
|
-
};
|
|
4207
|
-
for (const field of ["originalUserTask", "delegatedTask"]) {
|
|
4208
|
-
const task = result[field];
|
|
4209
|
-
if (task !== null && Buffer.byteLength(task.text) > 4096) result = buildBudgetOmittedContext(result, field);
|
|
4210
|
-
}
|
|
4211
|
-
return result;
|
|
4212
|
-
}
|
|
4213
|
-
//#endregion
|
|
4214
4282
|
//#region src/git/parse-config-parameters.ts
|
|
4215
4283
|
function parseConfigParameters(text) {
|
|
4216
4284
|
const parameters = [];
|
|
@@ -4754,6 +4822,7 @@ function buildDecisionRequest(payload, policy, options) {
|
|
|
4754
4822
|
const rulesSource = options.rulesSource ?? "replacement";
|
|
4755
4823
|
const repositoryContext = options.repositoryContext ?? null;
|
|
4756
4824
|
const mcpServers = options.mcpServers ?? [];
|
|
4825
|
+
const exemptRules = options.exemptRules ?? [];
|
|
4757
4826
|
const rules = {};
|
|
4758
4827
|
let tier = null;
|
|
4759
4828
|
for (const section of policy.split(/(?=^## |^### )/mu)) if (section.startsWith("## HARD BLOCK rules")) tier = "hard";
|
|
@@ -4764,6 +4833,7 @@ function buildDecisionRequest(payload, policy, options) {
|
|
|
4764
4833
|
const end = newline === -1 ? section.length : newline;
|
|
4765
4834
|
const name = section.slice(4, end).trim();
|
|
4766
4835
|
if (name === "" || Object.values(rules).some((rule) => rule.name === name)) throw new Error("Policy has an empty or duplicate rule");
|
|
4836
|
+
if (rulesSource === "shipped" && exemptRules.includes(name)) continue;
|
|
4767
4837
|
rules[`rule_${Object.keys(rules).length}`] = {
|
|
4768
4838
|
name,
|
|
4769
4839
|
tier,
|
|
@@ -4989,6 +5059,30 @@ function classifyFailure(attempt, timeoutMs) {
|
|
|
4989
5059
|
};
|
|
4990
5060
|
}
|
|
4991
5061
|
//#endregion
|
|
5062
|
+
//#region src/model/build-sized-decision-input.ts
|
|
5063
|
+
function buildSizedDecisionInput(model, request) {
|
|
5064
|
+
return buildFittedInput(buildFittedInput(buildInput(model, request.state, request.questions), "delegatedTask"), "originalUserTask");
|
|
5065
|
+
}
|
|
5066
|
+
function buildInput(model, state, questions) {
|
|
5067
|
+
const input = {
|
|
5068
|
+
model,
|
|
5069
|
+
state,
|
|
5070
|
+
questions
|
|
5071
|
+
};
|
|
5072
|
+
return {
|
|
5073
|
+
input,
|
|
5074
|
+
body: JSON.stringify(input)
|
|
5075
|
+
};
|
|
5076
|
+
}
|
|
5077
|
+
function buildFittedInput(sized, field) {
|
|
5078
|
+
const context = sized.input.state.taskContext;
|
|
5079
|
+
if (Buffer.byteLength(sized.body) <= 1e5 || context === void 0 || context[field] === null) return sized;
|
|
5080
|
+
return buildInput(sized.input.model, {
|
|
5081
|
+
...sized.input.state,
|
|
5082
|
+
taskContext: buildBudgetOmittedContext(context, field)
|
|
5083
|
+
}, sized.input.questions);
|
|
5084
|
+
}
|
|
5085
|
+
//#endregion
|
|
4992
5086
|
//#region src/model/collect-config-file-paths.ts
|
|
4993
5087
|
function collectConfigFilePaths(command) {
|
|
4994
5088
|
const values = splitShellCommand(command).segments.flatMap((segment) => splitShellTokens(segment.text).tokens.flatMap((token) => toWordValues(token))).flatMap((word) => {
|
|
@@ -5025,18 +5119,7 @@ function buildDecisionResponseSchema(choices) {
|
|
|
5025
5119
|
//#endregion
|
|
5026
5120
|
//#region src/model/send-decision.ts
|
|
5027
5121
|
async function sendDecision(provider, apiKey, request, options) {
|
|
5028
|
-
const
|
|
5029
|
-
model: provider.model,
|
|
5030
|
-
state: { ...request.state },
|
|
5031
|
-
questions: request.questions
|
|
5032
|
-
};
|
|
5033
|
-
let body = JSON.stringify(input);
|
|
5034
|
-
for (const field of ["delegatedTask", "originalUserTask"]) {
|
|
5035
|
-
const context = input.state.taskContext;
|
|
5036
|
-
if (Buffer.byteLength(body) <= 1e5 || context === void 0 || context[field] === null) continue;
|
|
5037
|
-
input.state.taskContext = buildBudgetOmittedContext(context, field);
|
|
5038
|
-
body = JSON.stringify(input);
|
|
5039
|
-
}
|
|
5122
|
+
const body = buildSizedDecisionInput(provider.model, request).body;
|
|
5040
5123
|
const requestBytes = Buffer.byteLength(body);
|
|
5041
5124
|
if (requestBytes > 1e5) throw new DecisionRequestError("request-too-large", requestBytes, "Decision input exceeds 100000 bytes; refusing to truncate the action or user message");
|
|
5042
5125
|
let response;
|
|
@@ -5121,7 +5204,8 @@ async function classifyWithJev(payload, config, options) {
|
|
|
5121
5204
|
repositoryContext,
|
|
5122
5205
|
mcpServers,
|
|
5123
5206
|
localHookConfig,
|
|
5124
|
-
hookFacts
|
|
5207
|
+
hookFacts,
|
|
5208
|
+
exemptRules: options.exemptRules
|
|
5125
5209
|
});
|
|
5126
5210
|
const signal = createDeadlineSignal(config.provider.timeoutMs, options);
|
|
5127
5211
|
if (signal === null) throw new DOMException("Evaluation deadline expired", "AbortError");
|
|
@@ -5131,6 +5215,7 @@ async function classifyWithJev(payload, config, options) {
|
|
|
5131
5215
|
fetch: runtime.fetch
|
|
5132
5216
|
});
|
|
5133
5217
|
stage = "response";
|
|
5218
|
+
options.onTaskContext?.(buildSizedDecisionInput(config.provider.model, request).input.state.taskContext);
|
|
5134
5219
|
return buildJevOutcome({
|
|
5135
5220
|
kind: "reply",
|
|
5136
5221
|
request,
|
|
@@ -5937,31 +6022,65 @@ async function probeLauncherSpawner(location, readTextFile) {
|
|
|
5937
6022
|
//#region src/scope/resolve-opening-prompt.ts
|
|
5938
6023
|
async function resolveOpeningPrompt(request, runtime) {
|
|
5939
6024
|
const context = request.decisionContext;
|
|
5940
|
-
const
|
|
5941
|
-
|
|
5942
|
-
const
|
|
6025
|
+
const task = context?.originalUserTask ?? null;
|
|
6026
|
+
const message = context?.lastDirectUserMessage ?? null;
|
|
6027
|
+
const opening = task?.position === "opening" ? task : message;
|
|
6028
|
+
if (context === void 0 || opening?.position !== "opening") return request;
|
|
5943
6029
|
const isSpawned = await probeLauncherSpawner(runtime.host.env["AUTO_MODE_SCOPE_FILE"], runtime.readTextFile);
|
|
5944
6030
|
return {
|
|
5945
6031
|
...request,
|
|
5946
|
-
decisionContext: {
|
|
6032
|
+
decisionContext: isSpawned ? buildSpawnedContext(context, opening.text) : {
|
|
5947
6033
|
...context,
|
|
5948
|
-
|
|
6034
|
+
originalUserTask: task === null ? null : toUnmarked(task),
|
|
6035
|
+
lastDirectUserMessage: message === null ? null : toUnmarked(message)
|
|
5949
6036
|
}
|
|
5950
6037
|
};
|
|
5951
6038
|
}
|
|
6039
|
+
function buildSpawnedContext(context, openingText) {
|
|
6040
|
+
const isTaskOpening = context.originalUserTask?.position === "opening";
|
|
6041
|
+
const message = context.lastDirectUserMessage;
|
|
6042
|
+
const isOmitted = context.omittedTaskContext.some((omission) => omission.field === "originalUserTask");
|
|
6043
|
+
return {
|
|
6044
|
+
...context,
|
|
6045
|
+
originalUserTask: isTaskOpening ? null : context.originalUserTask,
|
|
6046
|
+
delegatedTask: context.agentID === null ? context.delegatedTask ?? {
|
|
6047
|
+
text: openingText,
|
|
6048
|
+
origin: "agent.spawn"
|
|
6049
|
+
} : context.delegatedTask,
|
|
6050
|
+
lastDirectUserMessage: message?.position === "opening" ? null : message,
|
|
6051
|
+
omittedTaskContext: isTaskOpening && !isOmitted ? [...context.omittedTaskContext, {
|
|
6052
|
+
field: "originalUserTask",
|
|
6053
|
+
reason: "unavailable"
|
|
6054
|
+
}] : context.omittedTaskContext
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
function toUnmarked(value) {
|
|
6058
|
+
const { position: _, ...unmarked } = value;
|
|
6059
|
+
return unmarked;
|
|
6060
|
+
}
|
|
5952
6061
|
//#endregion
|
|
5953
6062
|
//#region src/classify-action.ts
|
|
5954
6063
|
async function classifyAction(request, config, options) {
|
|
5955
|
-
const stderr = options.stderr;
|
|
5956
|
-
const host = options.runtime.host;
|
|
5957
6064
|
const invocationID = randomUUID();
|
|
5958
|
-
if (options.actionLog === true) await writeActionDiagnostic(request, {
|
|
5959
|
-
invocationID,
|
|
5960
|
-
status: "started"
|
|
5961
|
-
}, host, stderr);
|
|
5962
6065
|
const resolved = await resolveOpeningPrompt(request, options.runtime);
|
|
5963
|
-
const
|
|
5964
|
-
if (options.actionLog === true) await writeActionDiagnostic(
|
|
6066
|
+
const offered = resolved.decisionContext === void 0 ? void 0 : buildTaskContext(resolved.decisionContext);
|
|
6067
|
+
if (options.actionLog === true) await writeActionDiagnostic(resolved, {
|
|
6068
|
+
invocationID,
|
|
6069
|
+
status: "started",
|
|
6070
|
+
taskContext: offered
|
|
6071
|
+
}, options.runtime.host, options.stderr);
|
|
6072
|
+
const received = { context: offered };
|
|
6073
|
+
const onTaskContext = (context) => {
|
|
6074
|
+
received.context = context;
|
|
6075
|
+
};
|
|
6076
|
+
const outcome = options.budget === true ? await classifyWithBudget(resolved, config, {
|
|
6077
|
+
...options,
|
|
6078
|
+
onTaskContext
|
|
6079
|
+
}) : await classifyStages(resolved, config, {
|
|
6080
|
+
...options,
|
|
6081
|
+
onTaskContext
|
|
6082
|
+
});
|
|
6083
|
+
if (options.actionLog === true) await writeActionDiagnostic(resolved, {
|
|
5965
6084
|
invocationID,
|
|
5966
6085
|
status: outcome.status,
|
|
5967
6086
|
verdict: outcome.verdict?.kind ?? "defer",
|
|
@@ -5970,8 +6089,9 @@ async function classifyAction(request, config, options) {
|
|
|
5970
6089
|
...outcome.escalation === void 0 ? {} : { escalation: outcome.escalation },
|
|
5971
6090
|
...outcome.diagnostics === void 0 ? {} : { diagnostics: outcome.diagnostics },
|
|
5972
6091
|
...outcome.judge === void 0 ? {} : { judge: outcome.judge },
|
|
5973
|
-
...outcome.grant === void 0 ? {} : { grant: outcome.grant }
|
|
5974
|
-
|
|
6092
|
+
...outcome.grant === void 0 ? {} : { grant: outcome.grant },
|
|
6093
|
+
taskContext: received.context
|
|
6094
|
+
}, options.runtime.host, options.stderr);
|
|
5975
6095
|
return outcome;
|
|
5976
6096
|
}
|
|
5977
6097
|
async function classifyWithBudget(request, config, options) {
|
|
@@ -6008,7 +6128,8 @@ async function classifyStages(request, config, options) {
|
|
|
6008
6128
|
local: { kind: "escalate" },
|
|
6009
6129
|
checkout: null,
|
|
6010
6130
|
scope: null,
|
|
6011
|
-
grant: null
|
|
6131
|
+
grant: null,
|
|
6132
|
+
exemptRules: []
|
|
6012
6133
|
};
|
|
6013
6134
|
const reviewed = await classifyWithGrant(ctx, await runStages(ctx));
|
|
6014
6135
|
if (reviewed.verdict?.kind !== "deny") return reviewed;
|
|
@@ -6192,9 +6313,12 @@ async function classifyEditBypass(ctx) {
|
|
|
6192
6313
|
};
|
|
6193
6314
|
}
|
|
6194
6315
|
async function classifyReplyChannel(ctx) {
|
|
6195
|
-
const
|
|
6196
|
-
if (
|
|
6197
|
-
|
|
6316
|
+
const reply = await classifyReplyCall(ctx.request, ctx.scope, ctx.hasConfiguredDenies);
|
|
6317
|
+
if (reply.kind !== "bypass") return {
|
|
6318
|
+
kind: "pass",
|
|
6319
|
+
facts: { exemptRules: reply.kind === "exempt" ? reply.rules : [] }
|
|
6320
|
+
};
|
|
6321
|
+
const channel = reply.channel;
|
|
6198
6322
|
return {
|
|
6199
6323
|
kind: "decided",
|
|
6200
6324
|
outcome: {
|
|
@@ -6223,7 +6347,8 @@ async function classifyJev(ctx) {
|
|
|
6223
6347
|
taskScope: ctx.scope === null ? void 0 : buildTaskScopeSummary(ctx.scope),
|
|
6224
6348
|
claudeSettings: ctx.claudeSettings,
|
|
6225
6349
|
checkout: ctx.checkout ?? void 0,
|
|
6226
|
-
readCheckout: ctx.readCheckout
|
|
6350
|
+
readCheckout: ctx.readCheckout,
|
|
6351
|
+
exemptRules: ctx.exemptRules
|
|
6227
6352
|
});
|
|
6228
6353
|
return {
|
|
6229
6354
|
...outcome,
|
|
@@ -6241,7 +6366,8 @@ async function classifyWithGrant(ctx, decided) {
|
|
|
6241
6366
|
const judged = await classifyWithJudge(ctx.request, evidence, judge, {
|
|
6242
6367
|
...ctx.options,
|
|
6243
6368
|
rulesPath: ctx.config.rulesPath,
|
|
6244
|
-
overturns: ctx.config.judgeOverturns ?? "any"
|
|
6369
|
+
overturns: ctx.config.judgeOverturns ?? "any",
|
|
6370
|
+
onUsage: (usage) => writeJudgeUsage(usage, ctx.options.runtime.host, ctx.options.stderr)
|
|
6245
6371
|
});
|
|
6246
6372
|
const granted = grant === null ? null : judged[0] ?? null;
|
|
6247
6373
|
const reviewed = denied === null ? null : judged.at(-1) ?? null;
|
|
@@ -6314,12 +6440,12 @@ const userTaskSchema = strictObject({
|
|
|
6314
6440
|
"composer",
|
|
6315
6441
|
"bridge",
|
|
6316
6442
|
"sdk"
|
|
6317
|
-
])
|
|
6443
|
+
]),
|
|
6444
|
+
position: literal("opening").optional()
|
|
6318
6445
|
});
|
|
6319
6446
|
const directUserMessageSchema = strictObject({
|
|
6320
6447
|
...userTaskSchema.shape,
|
|
6321
|
-
freshness: literal("stale").optional()
|
|
6322
|
-
position: literal("opening").optional()
|
|
6448
|
+
freshness: literal("stale").optional()
|
|
6323
6449
|
});
|
|
6324
6450
|
const delegatedTaskSchema = strictObject({
|
|
6325
6451
|
text: string(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as string, s as number, u as strictObject } from "./schemas-B80Yxv6m.js";
|
|
2
|
-
import { t as startJudgeSession } from "./start-judge-session-
|
|
2
|
+
import { t as startJudgeSession } from "./start-judge-session-C_k8lacU.js";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { chmod, mkdir, rename, rm, stat } from "node:fs/promises";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as startJudgeSession } from "./start-judge-session-C_k8lacU.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as object, d as string, l as record, s as number } from "./schemas-B80Yxv6m.js";
|
|
1
|
+
import { c as object, d as string, l as record, p as unknown, s as number } from "./schemas-B80Yxv6m.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import ut, { delimiter, isAbsolute, join } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
@@ -39277,7 +39277,7 @@ function startJudgeSession(env) {
|
|
|
39277
39277
|
current.reviews += 1;
|
|
39278
39278
|
if (turn.sessionFallback) stop();
|
|
39279
39279
|
else current.cleared = sendClear(current.send, current.stop);
|
|
39280
|
-
return buildReply(turn
|
|
39280
|
+
return buildReply(turn, model, warm);
|
|
39281
39281
|
} catch (error) {
|
|
39282
39282
|
if (signal.aborted) throw new DOMException("Judge review aborted", "AbortError");
|
|
39283
39283
|
throw error;
|
|
@@ -39343,13 +39343,18 @@ function startLiveProcess(model, system, key, env) {
|
|
|
39343
39343
|
waiter.sessionFallback ||= message.scope !== "local";
|
|
39344
39344
|
return;
|
|
39345
39345
|
}
|
|
39346
|
+
if (message.type === "assistant" && message.parent_tool_use_id === null && message.error === void 0) {
|
|
39347
|
+
waiter.replyModel = message.message.model;
|
|
39348
|
+
return;
|
|
39349
|
+
}
|
|
39346
39350
|
if (message.type === "result") {
|
|
39347
39351
|
const done = waiter;
|
|
39348
39352
|
waiter = null;
|
|
39349
39353
|
done.resolve({
|
|
39350
39354
|
result: message,
|
|
39351
39355
|
fallbacks: done.fallbacks,
|
|
39352
|
-
sessionFallback: done.sessionFallback
|
|
39356
|
+
sessionFallback: done.sessionFallback,
|
|
39357
|
+
replyModel: done.replyModel
|
|
39353
39358
|
});
|
|
39354
39359
|
}
|
|
39355
39360
|
};
|
|
@@ -39371,7 +39376,8 @@ function startLiveProcess(model, system, key, env) {
|
|
|
39371
39376
|
resolve,
|
|
39372
39377
|
reject,
|
|
39373
39378
|
fallbacks: 0,
|
|
39374
|
-
sessionFallback: false
|
|
39379
|
+
sessionFallback: false,
|
|
39380
|
+
replyModel: null
|
|
39375
39381
|
};
|
|
39376
39382
|
input.push(text);
|
|
39377
39383
|
})
|
|
@@ -39445,23 +39451,22 @@ async function sendClear(send, stop) {
|
|
|
39445
39451
|
}
|
|
39446
39452
|
}
|
|
39447
39453
|
const MODEL_USAGE = record(string(), object({ outputTokens: number() }));
|
|
39454
|
+
const RAW_MODEL_USAGE = record(string(), unknown());
|
|
39448
39455
|
const SAFETY_STOPS = object({ safety_stops: number() });
|
|
39449
|
-
function buildReply(
|
|
39456
|
+
function buildReply(turn, requested, warm) {
|
|
39457
|
+
const result = turn.result;
|
|
39450
39458
|
if (result.subtype !== "success" || result.is_error) throw new Error(`the judge turn ended in ${result.subtype}`);
|
|
39451
39459
|
const usage = MODEL_USAGE.safeParse(result.modelUsage).data;
|
|
39452
39460
|
const stops = SAFETY_STOPS.safeParse(result).data?.safety_stops;
|
|
39453
39461
|
return {
|
|
39454
39462
|
text: result.result,
|
|
39455
|
-
model:
|
|
39463
|
+
model: turn.replyModel ?? requested,
|
|
39456
39464
|
safetyStops: stops ?? null,
|
|
39457
|
-
fallback: fallbacks > 0 || isFallbackReply(usage, stops),
|
|
39458
|
-
warm
|
|
39465
|
+
fallback: turn.fallbacks > 0 || isFallbackReply(usage, stops),
|
|
39466
|
+
warm,
|
|
39467
|
+
modelUsage: RAW_MODEL_USAGE.safeParse(result.modelUsage).data ?? null
|
|
39459
39468
|
};
|
|
39460
39469
|
}
|
|
39461
|
-
function pickAnsweringModel(usage, requested) {
|
|
39462
|
-
const [top] = Object.entries(usage ?? {}).filter(([, entry]) => entry.outputTokens > 0).toSorted(([, left], [, right]) => right.outputTokens - left.outputTokens);
|
|
39463
|
-
return top === void 0 ? requested : top[0];
|
|
39464
|
-
}
|
|
39465
39470
|
function isFallbackReply(usage, stops) {
|
|
39466
39471
|
if (stops !== void 0) return stops > 0;
|
|
39467
39472
|
const entries = Object.values(usage ?? {});
|
|
@@ -5,11 +5,11 @@ export type MessageOrigin = (typeof MESSAGE_ORIGINS)[number];
|
|
|
5
5
|
export interface UserTask {
|
|
6
6
|
readonly text: string;
|
|
7
7
|
readonly origin: MessageOrigin;
|
|
8
|
+
readonly position?: 'opening' | undefined;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export interface DirectUserMessage extends UserTask {
|
|
11
12
|
readonly freshness?: 'stale' | undefined;
|
|
12
|
-
readonly position?: 'opening' | undefined;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
interface DelegatedTask {
|
|
@@ -36,13 +36,16 @@ export function buildPromptContext(
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const message: UserTask = { text: input.text, origin };
|
|
40
39
|
const isOpeningPrompt = previous?.awaitsOpeningPrompt ?? true;
|
|
41
40
|
|
|
41
|
+
const message: UserTask = isOpeningPrompt
|
|
42
|
+
? { text: input.text, origin, position: 'opening' }
|
|
43
|
+
: { text: input.text, origin };
|
|
44
|
+
|
|
42
45
|
return {
|
|
43
46
|
originalUserTask:
|
|
44
47
|
previous?.originalUserTask ?? (previous?.canCaptureOriginal === true ? message : null),
|
|
45
|
-
lastDirectUserMessage:
|
|
48
|
+
lastDirectUserMessage: message,
|
|
46
49
|
canCaptureOriginal: previous?.canCaptureOriginal ?? false,
|
|
47
50
|
awaitsOpeningPrompt: false,
|
|
48
51
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auto-mode",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.5",
|
|
4
4
|
"description": "A permission classifier library and Claude Code mod, so agents that are not Claude can run unattended",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"eval:question-shape": "bun --no-env-file --conditions=auto-mode-eval evals/runners/run-question-shape-evaluation.ts",
|
|
53
53
|
"eval:relay-consent": "bun --no-env-file --conditions=auto-mode-eval evals/runners/run-relay-consent-evaluation.ts",
|
|
54
54
|
"eval:second-judge": "bun --no-env-file --conditions=auto-mode-eval evals/runners/run-second-judge-evaluation.ts",
|
|
55
|
+
"eval:spawned-task": "bun --no-env-file --conditions=auto-mode-eval evals/runners/run-spawned-task-evaluation.ts",
|
|
55
56
|
"eval:stale-consent": "bun --no-env-file --conditions=auto-mode-eval evals/runners/run-stale-consent-evaluation.ts",
|
|
56
57
|
"format": "oxfmt . && format-codemod --quiet .",
|
|
57
58
|
"format:check": "oxfmt --check . && format-codemod --check --quiet .",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { t as startJudgeSession } from "./start-judge-session-vhUxe9-V.js";
|