auto-mode 0.12.0 → 0.13.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/cli.js +1 -1
- package/dist/index.d.ts +52 -43
- package/dist/index.js +1 -1
- package/dist/{render-verdict-FhSKxlO9.js → render-verdict-De8aTmg-.js} +103 -32
- package/package.json +1 -1
- package/policy/classifier.md +4 -1
- package/policy/decision.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as string, E as strictObject, T as number, _ as splitCommandWords, a as resolveSessionScopePath, b as DEFAULT_SCOPE_SOURCES, c as
|
|
2
|
+
import { D as string, E as strictObject, T as number, _ as splitCommandWords, a as resolveSessionScopePath, b as DEFAULT_SCOPE_SOURCES, c as findCheckout, f as loadRepositoryContext, g as splitShellCommand, h as toRepositorySlug, i as resolveStateDir, m as loadPolicy, n as parseActionRequest, o as loadSessionScope, p as loadCheckoutRemotes, r as classifyAction, s as EMPTY_SESSION_SCOPE, t as renderVerdict, x as loadConfig, y as DEFAULT_DENIAL_BUDGET } from "./render-verdict-De8aTmg-.js";
|
|
3
3
|
import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
package/dist/index.d.ts
CHANGED
|
@@ -4,49 +4,6 @@ interface DenialBudget {
|
|
|
4
4
|
readonly perSession: number;
|
|
5
5
|
}
|
|
6
6
|
//#endregion
|
|
7
|
-
//#region src/config/types.d.ts
|
|
8
|
-
interface EvaluationOptions {
|
|
9
|
-
readonly deadlineAt?: number | undefined;
|
|
10
|
-
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
11
|
-
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/config/presets.d.ts
|
|
14
|
-
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/config/config.d.ts
|
|
17
|
-
interface ProviderConfig {
|
|
18
|
-
readonly protocol?: 'messages' | 'system-one';
|
|
19
|
-
readonly baseURL: string;
|
|
20
|
-
readonly model: string;
|
|
21
|
-
readonly apiKeyEnv?: string | undefined;
|
|
22
|
-
readonly apiKeyCommand?: string | undefined;
|
|
23
|
-
readonly reasoning: boolean;
|
|
24
|
-
readonly maxTokens: number;
|
|
25
|
-
readonly timeoutMs: number;
|
|
26
|
-
}
|
|
27
|
-
interface ScopeSource {
|
|
28
|
-
readonly kind: ScopeSourceKind;
|
|
29
|
-
readonly paths?: readonly string[];
|
|
30
|
-
}
|
|
31
|
-
type ScopeSourceKind = (typeof SCOPE_SOURCE_KINDS)[number];
|
|
32
|
-
declare const SCOPE_SOURCE_KINDS: readonly ['cwd', 'session', 'globs', 'atc'];
|
|
33
|
-
interface Config {
|
|
34
|
-
readonly provider: ProviderConfig;
|
|
35
|
-
readonly judge?: ProviderConfig | null;
|
|
36
|
-
readonly scopeSources?: Readonly<Record<string, ScopeSource>>;
|
|
37
|
-
readonly classifierPath?: string | undefined;
|
|
38
|
-
readonly rulesPath?: string | undefined;
|
|
39
|
-
readonly onFailure: 'defer' | 'deny';
|
|
40
|
-
readonly claudeSettingsPath?: string | null | undefined;
|
|
41
|
-
readonly minConfidence?: number | undefined;
|
|
42
|
-
readonly denialBudget?: DenialBudget;
|
|
43
|
-
readonly warnings?: readonly string[];
|
|
44
|
-
}
|
|
45
|
-
export declare const DEFAULT_CONFIG: Config;
|
|
46
|
-
export declare function resolveConfigPath(): string;
|
|
47
|
-
export declare function loadConfig(path?: string): Promise<Config>;
|
|
48
|
-
export declare function resolveApiKey(provider: ProviderConfig, options?: EvaluationOptions): Promise<string | null>;
|
|
49
|
-
//#endregion
|
|
50
7
|
//#region src/request/types.d.ts
|
|
51
8
|
interface UserTask {
|
|
52
9
|
readonly text: string;
|
|
@@ -88,6 +45,14 @@ interface DecisionRule {
|
|
|
88
45
|
readonly source: 'shipped' | 'replacement' | 'configured';
|
|
89
46
|
readonly text: string;
|
|
90
47
|
}
|
|
48
|
+
interface TaskScopeSummary {
|
|
49
|
+
readonly worktrees: readonly string[];
|
|
50
|
+
readonly branches: readonly string[];
|
|
51
|
+
readonly pullRequests: readonly {
|
|
52
|
+
readonly repository: string;
|
|
53
|
+
readonly number: number;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
91
56
|
interface DecisionAnswer {
|
|
92
57
|
readonly type: 'choice';
|
|
93
58
|
readonly choice: 'allow' | 'block' | 'ask';
|
|
@@ -114,6 +79,50 @@ interface DecisionDiagnostics {
|
|
|
114
79
|
}[];
|
|
115
80
|
}
|
|
116
81
|
//#endregion
|
|
82
|
+
//#region src/config/types.d.ts
|
|
83
|
+
interface EvaluationOptions {
|
|
84
|
+
readonly deadlineAt?: number | undefined;
|
|
85
|
+
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
86
|
+
readonly taskScope?: TaskScopeSummary | undefined;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/config/presets.d.ts
|
|
90
|
+
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/config/config.d.ts
|
|
93
|
+
interface ProviderConfig {
|
|
94
|
+
readonly protocol?: 'messages' | 'system-one';
|
|
95
|
+
readonly baseURL: string;
|
|
96
|
+
readonly model: string;
|
|
97
|
+
readonly apiKeyEnv?: string | undefined;
|
|
98
|
+
readonly apiKeyCommand?: string | undefined;
|
|
99
|
+
readonly reasoning: boolean;
|
|
100
|
+
readonly maxTokens: number;
|
|
101
|
+
readonly timeoutMs: number;
|
|
102
|
+
}
|
|
103
|
+
interface ScopeSource {
|
|
104
|
+
readonly kind: ScopeSourceKind;
|
|
105
|
+
readonly paths?: readonly string[];
|
|
106
|
+
}
|
|
107
|
+
type ScopeSourceKind = (typeof SCOPE_SOURCE_KINDS)[number];
|
|
108
|
+
declare const SCOPE_SOURCE_KINDS: readonly ['cwd', 'session', 'globs', 'atc'];
|
|
109
|
+
interface Config {
|
|
110
|
+
readonly provider: ProviderConfig;
|
|
111
|
+
readonly judge?: ProviderConfig | null;
|
|
112
|
+
readonly scopeSources?: Readonly<Record<string, ScopeSource>>;
|
|
113
|
+
readonly classifierPath?: string | undefined;
|
|
114
|
+
readonly rulesPath?: string | undefined;
|
|
115
|
+
readonly onFailure: 'defer' | 'deny';
|
|
116
|
+
readonly claudeSettingsPath?: string | null | undefined;
|
|
117
|
+
readonly minConfidence?: number | undefined;
|
|
118
|
+
readonly denialBudget?: DenialBudget;
|
|
119
|
+
readonly warnings?: readonly string[];
|
|
120
|
+
}
|
|
121
|
+
export declare const DEFAULT_CONFIG: Config;
|
|
122
|
+
export declare function resolveConfigPath(): string;
|
|
123
|
+
export declare function loadConfig(path?: string): Promise<Config>;
|
|
124
|
+
export declare function resolveApiKey(provider: ProviderConfig, options?: EvaluationOptions): Promise<string | null>;
|
|
125
|
+
//#endregion
|
|
117
126
|
//#region src/classify-action.d.ts
|
|
118
127
|
type DecidingStage = 'local' | 'containment' | 'bypass' | 'jev' | 'messages' | 'retry';
|
|
119
128
|
interface ActionOutcome {
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as resolveConfigPath, S as resolveApiKey, d as
|
|
1
|
+
import { C as resolveConfigPath, S as resolveApiKey, d as parseModelVerdict, l as classifyLocally, m as loadPolicy, n as parseActionRequest, r as classifyAction, t as renderVerdict, u as classifyWithModel, v as DEFAULT_CONFIG, w as PRESETS, x as loadConfig } from "./render-verdict-De8aTmg-.js";
|
|
2
2
|
export { DEFAULT_CONFIG, PRESETS, classifyAction, classifyLocally, classifyWithModel, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath };
|
|
@@ -6507,6 +6507,18 @@ function formatList(one, many, values) {
|
|
|
6507
6507
|
return `the ${values.length === 1 ? one : many} ${values.join(", ")}`;
|
|
6508
6508
|
}
|
|
6509
6509
|
//#endregion
|
|
6510
|
+
//#region src/model/build-task-scope-summary.ts
|
|
6511
|
+
function buildTaskScopeSummary(scope) {
|
|
6512
|
+
return {
|
|
6513
|
+
worktrees: scope.worktrees,
|
|
6514
|
+
branches: scope.branches,
|
|
6515
|
+
pullRequests: scope.pullRequests.map((pull) => ({
|
|
6516
|
+
repository: pull.repository,
|
|
6517
|
+
number: pull.number
|
|
6518
|
+
}))
|
|
6519
|
+
};
|
|
6520
|
+
}
|
|
6521
|
+
//#endregion
|
|
6510
6522
|
//#region src/policy/resolve-shipped-policy-dir.ts
|
|
6511
6523
|
function resolveShippedPolicyDir() {
|
|
6512
6524
|
let dir = import.meta.dirname;
|
|
@@ -6610,7 +6622,7 @@ function readResult(body) {
|
|
|
6610
6622
|
//#endregion
|
|
6611
6623
|
//#region src/model/build-request.ts
|
|
6612
6624
|
const MAX_INPUT_CHARS = 4e3;
|
|
6613
|
-
function buildUserMessage(payload, transcript, reasoning) {
|
|
6625
|
+
function buildUserMessage(payload, transcript, reasoning, repositoryContext = null) {
|
|
6614
6626
|
const history = transcript.length === 0 ? "(no transcript available from this harness)" : transcript.map((entry) => `${entry.role}: ${entry.text}`).join("\n\n");
|
|
6615
6627
|
const input = JSON.stringify(payload.toolInput, null, 2).slice(0, MAX_INPUT_CHARS);
|
|
6616
6628
|
const closing = reasoning ? "Work through the classification process, then end your reply with the output contract tags." : "Reply with the output contract tags and nothing else.";
|
|
@@ -6618,7 +6630,7 @@ function buildUserMessage(payload, transcript, reasoning) {
|
|
|
6618
6630
|
${history}
|
|
6619
6631
|
</transcript>
|
|
6620
6632
|
|
|
6621
|
-
<action>
|
|
6633
|
+
${repositoryContext === null ? "" : `<repository>\n${JSON.stringify(repositoryContext, null, 2)}\n</repository>\n\n`}<action>
|
|
6622
6634
|
tool: ${payload.toolName}
|
|
6623
6635
|
cwd: ${payload.cwd}
|
|
6624
6636
|
input:
|
|
@@ -6775,6 +6787,46 @@ function formatClassifierNote(note, apiKey) {
|
|
|
6775
6787
|
return apiKey === null || apiKey === "" ? note : note.split(apiKey).join("[redacted]");
|
|
6776
6788
|
}
|
|
6777
6789
|
//#endregion
|
|
6790
|
+
//#region src/scope/load-checkout-remotes.ts
|
|
6791
|
+
async function loadCheckoutRemotes(commonDir) {
|
|
6792
|
+
const config = await readFile(join(commonDir, "config"), "utf8").catch(() => "");
|
|
6793
|
+
const remotes = [];
|
|
6794
|
+
let section = null;
|
|
6795
|
+
for (const line of config.split("\n")) {
|
|
6796
|
+
const header = /^\s*\[remote "(?<name>[^"]+)"\]/u.exec(line)?.groups?.["name"];
|
|
6797
|
+
if (header !== void 0) section = header;
|
|
6798
|
+
else if (/^\s*\[/u.test(line)) section = null;
|
|
6799
|
+
else if (section !== null) {
|
|
6800
|
+
const value = /^\s*(?:push)?url\s*=(?<value>.*)$/iu.exec(line)?.groups?.["value"];
|
|
6801
|
+
const url = value === void 0 ? "" : parseConfigValue(value);
|
|
6802
|
+
if (url !== "") remotes.push({
|
|
6803
|
+
name: section,
|
|
6804
|
+
url
|
|
6805
|
+
});
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
return remotes;
|
|
6809
|
+
}
|
|
6810
|
+
function parseConfigValue(raw) {
|
|
6811
|
+
let value = "";
|
|
6812
|
+
let isQuoted = false;
|
|
6813
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
6814
|
+
const char = raw[index] ?? "";
|
|
6815
|
+
if (char === "\\") {
|
|
6816
|
+
const next = raw[index + 1] ?? "";
|
|
6817
|
+
value += {
|
|
6818
|
+
n: "\n",
|
|
6819
|
+
t: " ",
|
|
6820
|
+
b: ""
|
|
6821
|
+
}[next] ?? next;
|
|
6822
|
+
index += 1;
|
|
6823
|
+
} else if (char === "\"") isQuoted = !isQuoted;
|
|
6824
|
+
else if (!isQuoted && (char === "#" || char === ";")) break;
|
|
6825
|
+
else value += char;
|
|
6826
|
+
}
|
|
6827
|
+
return value.trim();
|
|
6828
|
+
}
|
|
6829
|
+
//#endregion
|
|
6778
6830
|
//#region src/model/load-repository-context.ts
|
|
6779
6831
|
async function loadRepositoryContext(cwd) {
|
|
6780
6832
|
if ([
|
|
@@ -6822,10 +6874,18 @@ async function loadRepositoryContext(cwd) {
|
|
|
6822
6874
|
} catch {
|
|
6823
6875
|
defaultBranch = null;
|
|
6824
6876
|
}
|
|
6877
|
+
const remotes = await loadCheckoutRemotes(commonDir);
|
|
6825
6878
|
return {
|
|
6826
6879
|
cwd: resolve(cwd),
|
|
6827
6880
|
branch: parseReference(head, "refs/heads/"),
|
|
6828
|
-
defaultBranch
|
|
6881
|
+
defaultBranch,
|
|
6882
|
+
remotes: remotes.flatMap((remote) => {
|
|
6883
|
+
const url = normalizeRemoteURL(remote.url);
|
|
6884
|
+
return url === null ? [] : [{
|
|
6885
|
+
name: remote.name,
|
|
6886
|
+
url
|
|
6887
|
+
}];
|
|
6888
|
+
})
|
|
6829
6889
|
};
|
|
6830
6890
|
}
|
|
6831
6891
|
}
|
|
@@ -6836,6 +6896,35 @@ function parseReference(content, prefix) {
|
|
|
6836
6896
|
const branch = value.slice(marker.length);
|
|
6837
6897
|
return branch !== "" && branch.length <= 256 && !/\s/u.test(branch) ? branch : null;
|
|
6838
6898
|
}
|
|
6899
|
+
function normalizeRemoteURL(url) {
|
|
6900
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//iu.test(url)) {
|
|
6901
|
+
const parsed = URL.parse(url);
|
|
6902
|
+
if (parsed === null) return null;
|
|
6903
|
+
parsed.username = "";
|
|
6904
|
+
parsed.password = "";
|
|
6905
|
+
parsed.search = "";
|
|
6906
|
+
parsed.hash = "";
|
|
6907
|
+
return parsed.href;
|
|
6908
|
+
}
|
|
6909
|
+
const slash = url.indexOf("/");
|
|
6910
|
+
const end = slash === -1 ? url.length : slash;
|
|
6911
|
+
const at = url.lastIndexOf("@", end);
|
|
6912
|
+
return at === -1 ? url : url.slice(at + 1);
|
|
6913
|
+
}
|
|
6914
|
+
//#endregion
|
|
6915
|
+
//#region src/model/load-repository-evidence.ts
|
|
6916
|
+
async function loadRepositoryEvidence(cwd, taskScope) {
|
|
6917
|
+
const context = await loadRepositoryContext(cwd);
|
|
6918
|
+
if (taskScope === void 0) return context;
|
|
6919
|
+
return {
|
|
6920
|
+
...context ?? {
|
|
6921
|
+
cwd: resolve(cwd),
|
|
6922
|
+
branch: null,
|
|
6923
|
+
defaultBranch: null
|
|
6924
|
+
},
|
|
6925
|
+
taskScope
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6839
6928
|
//#endregion
|
|
6840
6929
|
//#region node_modules/.bun/ts-pattern@5.9.0/node_modules/ts-pattern/dist/index.js
|
|
6841
6930
|
const t = Symbol.for("@ts-pattern/matcher");
|
|
@@ -7347,17 +7436,13 @@ async function classifyWithJev(payload, config, options = {}) {
|
|
|
7347
7436
|
if (key === null) throw new Error("no API key: set the configured environment variable or key command");
|
|
7348
7437
|
stage = "evidence";
|
|
7349
7438
|
const directUserText = payload.decisionContext?.agentID === null ? payload.decisionContext.lastDirectUserMessage?.text ?? null : null;
|
|
7350
|
-
const command = payload.toolInput["command"] ?? payload.toolInput["cmd"];
|
|
7351
|
-
const hasGitCommand = typeof command === "string" && /\bgit\s/u.test(command);
|
|
7352
|
-
const isFileEdit = payload.toolName === "Write" || payload.toolName === "Edit";
|
|
7353
|
-
const needsRepositoryContext = hasGitCommand || isFileEdit;
|
|
7354
7439
|
const [policy, rules, repositoryContext] = await Promise.all([
|
|
7355
7440
|
loadPolicy({
|
|
7356
7441
|
classifierPath: config.classifierPath,
|
|
7357
7442
|
rulesPath: config.rulesPath
|
|
7358
7443
|
}, "decision.md"),
|
|
7359
7444
|
loadClaudeRules(config.claudeSettingsPath),
|
|
7360
|
-
|
|
7445
|
+
loadRepositoryEvidence(payload.cwd, options.taskScope).catch(() => null)
|
|
7361
7446
|
]);
|
|
7362
7447
|
const request = buildDecisionRequest(payload, policy, rules, directUserText, config.rulesPath === void 0 ? "shipped" : "replacement", repositoryContext);
|
|
7363
7448
|
const remainingMs = options.deadlineAt === void 0 ? config.provider.timeoutMs : options.deadlineAt - Date.now();
|
|
@@ -7465,10 +7550,12 @@ async function classifyWithModel(payload, config, options = {}) {
|
|
|
7465
7550
|
return buildFailure(config, `policy unreadable: ${toMessage(error)}`);
|
|
7466
7551
|
}
|
|
7467
7552
|
const directMessage = payload.decisionContext?.agentID === null ? payload.decisionContext.lastDirectUserMessage : null;
|
|
7468
|
-
const
|
|
7553
|
+
const transcript = directMessage === null ? [] : [{
|
|
7469
7554
|
role: "user",
|
|
7470
7555
|
text: directMessage.text
|
|
7471
|
-
}]
|
|
7556
|
+
}];
|
|
7557
|
+
const repositoryContext = await loadRepositoryEvidence(payload.cwd, options.taskScope).catch(() => null);
|
|
7558
|
+
const user = buildUserMessage(payload, transcript, config.provider.reasoning, repositoryContext);
|
|
7472
7559
|
try {
|
|
7473
7560
|
const result = await sendMessage(config.provider, apiKey, {
|
|
7474
7561
|
system,
|
|
@@ -7750,26 +7837,6 @@ const DEFAULT_NAMES = /* @__PURE__ */ new Set([
|
|
|
7750
7837
|
"develop"
|
|
7751
7838
|
]);
|
|
7752
7839
|
//#endregion
|
|
7753
|
-
//#region src/scope/load-checkout-remotes.ts
|
|
7754
|
-
async function loadCheckoutRemotes(commonDir) {
|
|
7755
|
-
const config = await readFile(join(commonDir, "config"), "utf8").catch(() => "");
|
|
7756
|
-
const remotes = [];
|
|
7757
|
-
let section = null;
|
|
7758
|
-
for (const line of config.split("\n")) {
|
|
7759
|
-
const header = /^\s*\[remote "(?<name>[^"]+)"\]/u.exec(line)?.groups?.["name"];
|
|
7760
|
-
if (header !== void 0) section = header;
|
|
7761
|
-
else if (/^\s*\[/u.test(line)) section = null;
|
|
7762
|
-
else if (section !== null) {
|
|
7763
|
-
const url = /^\s*(?:push)?url\s*=\s*(?<url>\S+)/u.exec(line)?.groups?.["url"];
|
|
7764
|
-
if (url !== void 0) remotes.push({
|
|
7765
|
-
name: section,
|
|
7766
|
-
url
|
|
7767
|
-
});
|
|
7768
|
-
}
|
|
7769
|
-
}
|
|
7770
|
-
return remotes;
|
|
7771
|
-
}
|
|
7772
|
-
//#endregion
|
|
7773
7840
|
//#region src/scope/types.ts
|
|
7774
7841
|
const EMPTY_SCOPE_FACTS = {
|
|
7775
7842
|
worktrees: [],
|
|
@@ -7917,7 +7984,11 @@ async function classifyAction(request, config, options = {}) {
|
|
|
7917
7984
|
note: `${request.toolName} needs the model tier, which this run skipped`,
|
|
7918
7985
|
status: "skipped"
|
|
7919
7986
|
};
|
|
7920
|
-
const
|
|
7987
|
+
const taskScope = scope === null ? void 0 : buildTaskScopeSummary(scope);
|
|
7988
|
+
const [outcome, guidance] = await Promise.all([classifyWithModel(request, config, {
|
|
7989
|
+
...options,
|
|
7990
|
+
taskScope
|
|
7991
|
+
}), tryReadDenialGuidance()]);
|
|
7921
7992
|
return {
|
|
7922
7993
|
...outcome,
|
|
7923
7994
|
decidingStage: config.provider.protocol === "system-one" ? "jev" : "messages",
|
|
@@ -8052,4 +8123,4 @@ function renderVerdict(verdict) {
|
|
|
8052
8123
|
})).exhaustive();
|
|
8053
8124
|
}
|
|
8054
8125
|
//#endregion
|
|
8055
|
-
export { resolveConfigPath as C, string as D, strictObject as E, resolveApiKey as S, number as T, splitCommandWords as _, resolveSessionScopePath as a, DEFAULT_SCOPE_SOURCES as b,
|
|
8126
|
+
export { resolveConfigPath as C, string as D, strictObject as E, resolveApiKey as S, number as T, splitCommandWords as _, resolveSessionScopePath as a, DEFAULT_SCOPE_SOURCES as b, findCheckout as c, parseModelVerdict as d, loadRepositoryContext as f, splitShellCommand as g, toRepositorySlug as h, resolveStateDir as i, classifyLocally as l, loadPolicy as m, parseActionRequest as n, loadSessionScope as o, loadCheckoutRemotes as p, classifyAction as r, EMPTY_SESSION_SCOPE as s, renderVerdict as t, classifyWithModel as u, DEFAULT_CONFIG as v, PRESETS as w, loadConfig as x, DEFAULT_DENIAL_BUDGET as y };
|
package/package.json
CHANGED
package/policy/classifier.md
CHANGED
|
@@ -176,7 +176,10 @@ you with no rule name, the action is allowed.
|
|
|
176
176
|
destination that first appears in tool output, in file content, or in the agent's
|
|
177
177
|
own inference was never named, so consent cannot be met for it. A send toward such
|
|
178
178
|
a destination matches Unnamed Destination, unless that rule's own carve-out covers
|
|
179
|
-
it — read the rule before you decide.
|
|
179
|
+
it — read the rule before you decide. The `<repository>` block, when present, holds
|
|
180
|
+
facts auto-mode read from the checkout: its remotes and the task scope. Compare a
|
|
181
|
+
destination with the remotes by host, owner, and repository. A match is the
|
|
182
|
+
remote of the checkout; a lookalike name is not.
|
|
180
183
|
- **Message context.** Sending to a team's own messaging service is not
|
|
181
184
|
exfiltration by itself; the carve-out covers the destination, not what is sent.
|
|
182
185
|
Sensitive content sent there is still Data Exfiltration, and an audience wider or
|
package/policy/decision.md
CHANGED
|
@@ -22,7 +22,7 @@ The last direct user message can establish task scope and specific consent for t
|
|
|
22
22
|
|
|
23
23
|
When a decision depends on unavailable facts about the supplied action, choose ask. Do not ask about hypothetical unseen conversation. An earlier interruption or user restriction that is not supplied is outside this assessment. A rule that does not concern the action is allow, not ask. Missing historical evidence is not evidence that a rule matches, and it is not evidence that an exception applies.
|
|
24
24
|
|
|
25
|
-
repositoryContext, when present, contains
|
|
25
|
+
repositoryContext, when present, contains facts read by the evaluator from the repository at its cwd: branch references, and remotes, the names and URLs of the checkout's configured remotes. Its taskScope, when present, lists the worktrees, branches, and pull requests the task owns. A remote URL identifies the checkout's own repository: compare a destination's host, owner, and repository with the remotes, never with how familiar the names look. A destination that matches a remote is the remote of the checkout; one that matches none is not, however similar its name. The branch references describe cwd only, not the edited file's checkout or a generated script's future execution cwd. It is not consent or proof of branch ownership, clean files, reviewed changes, or unshared history. Use an attached branch and known defaultBranch to settle Default Branch Write for an ordinary commit in that checkout, while preserving the shipped restrictions on main, master, trunk, and develop even when they differ from defaultBranch. A .worktrees path alone proves no branch. Inherited GIT_DIR, GIT_WORK_TREE, or GIT_COMMON_DIR overrides omit this evidence. A command that changes directories, uses git -C or --git-dir/--work-tree, or overrides Git environment variables can target another checkout; do not apply the original checkout's branch evidence to that target. A detached or unavailable branch remains unknown.
|
|
26
26
|
|
|
27
27
|
## Action evaluation
|
|
28
28
|
|