Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions packages/core/src/catalog/catalog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,16 +486,7 @@ describe("catalog", () => {

expect(provision).toBeDefined();
expect(provision!.config).toEqual({
profile: "sensible-defaults",
capabilities: {
read: "allow",
edit_write: "allow",
search_list: "allow",
bash_safe: "allow",
bash_unsafe: "ask",
web: "ask",
task_agent: "allow"
}
profile: "sensible-defaults"
});
});
});
Expand Down
66 changes: 4 additions & 62 deletions packages/core/src/catalog/facets/autonomy.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,4 @@
import type {
AutonomyCapability,
Facet,
PermissionDecision,
PermissionPolicy
} from "../../types.js";

const ALL_CAPABILITIES: AutonomyCapability[] = [
"read",
"edit_write",
"search_list",
"bash_safe",
"bash_unsafe",
"web",
"task_agent"
];

function capabilityMap(
defaultDecision: PermissionDecision,
overrides: Partial<Record<AutonomyCapability, PermissionDecision>> = {}
): Record<AutonomyCapability, PermissionDecision> {
return Object.fromEntries(
ALL_CAPABILITIES.map((capability) => [
capability,
overrides[capability] ?? defaultDecision
])
) as Record<AutonomyCapability, PermissionDecision>;
}

function autonomyPolicy(
profile: PermissionPolicy["profile"]
): PermissionPolicy {
switch (profile) {
case "rigid":
return {
profile,
capabilities: capabilityMap("ask")
};
case "sensible-defaults":
return {
profile,
capabilities: capabilityMap("ask", {
read: "allow",
edit_write: "allow",
search_list: "allow",
bash_safe: "allow",
task_agent: "allow",
web: "ask"
})
};
case "max-autonomy":
return {
profile,
capabilities: capabilityMap("allow", {
web: "ask"
})
};
}
}
import type { Facet } from "../../types.js";

export const autonomyFacet: Facet = {
id: "autonomy",
Expand All @@ -74,7 +16,7 @@ export const autonomyFacet: Facet = {
recipe: [
{
writer: "permission-policy",
config: autonomyPolicy("rigid")
config: { profile: "rigid" }
}
]
},
Expand All @@ -86,7 +28,7 @@ export const autonomyFacet: Facet = {
recipe: [
{
writer: "permission-policy",
config: autonomyPolicy("sensible-defaults")
config: { profile: "sensible-defaults" }
}
]
},
Expand All @@ -98,7 +40,7 @@ export const autonomyFacet: Facet = {
recipe: [
{
writer: "permission-policy",
config: autonomyPolicy("max-autonomy")
config: { profile: "max-autonomy" }
}
]
}
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ export {
type ExternalSkill,
type GitHook,
type PermissionPolicy,
type AutonomyProfile,
type AutonomyCapability,
type PermissionDecision,
type PermissionRule
type AutonomyProfile
} from "./types.js";
export { type ResolutionContext, type ResolvedFacet } from "./types.js";
export { type UserConfig, type LockFile } from "./types.js";
Expand Down
26 changes: 4 additions & 22 deletions packages/core/src/resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ describe("resolve", () => {
});

describe("autonomy permission policy", () => {
it("adds a capability-based permission policy to LogicalConfig and keeps web access on ask", async () => {
it("adds a profile-based permission policy to LogicalConfig", async () => {
const userConfig: UserConfig = {
choices: { autonomy: "rigid" }
};
Expand All @@ -589,37 +589,19 @@ describe("resolve", () => {

expect(result).toHaveProperty("permission_policy");
expect((result as Record<string, unknown>).permission_policy).toEqual({
profile: "rigid",
capabilities: {
read: "ask",
edit_write: "ask",
search_list: "ask",
bash_safe: "ask",
bash_unsafe: "ask",
web: "ask",
task_agent: "ask"
}
profile: "rigid"
});
});

it("uses curated built-in defaults for the sensible-defaults autonomy profile", async () => {
it("uses the sensible-defaults autonomy profile", async () => {
const userConfig: UserConfig = {
choices: { autonomy: "sensible-defaults" }
};

const result = await resolve(userConfig, catalog, registry);

expect(result.permission_policy).toEqual({
profile: "sensible-defaults",
capabilities: {
read: "allow",
edit_write: "allow",
search_list: "allow",
bash_safe: "allow",
bash_unsafe: "ask",
web: "ask",
task_agent: "allow"
}
profile: "sensible-defaults"
});
});
});
Expand Down
6 changes: 1 addition & 5 deletions packages/core/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,7 @@ function mergePermissionPolicy(

return {
...existing,
...incoming,
capabilities: {
...existing.capabilities,
...incoming.capabilities
}
...incoming
};
}

Expand Down
20 changes: 0 additions & 20 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,8 @@ export interface GitHook {

export type AutonomyProfile = "rigid" | "sensible-defaults" | "max-autonomy";

export type PermissionDecision = "ask" | "allow" | "deny";

export type AutonomyCapability =
| "read"
| "edit_write"
| "search_list"
| "bash_safe"
| "bash_unsafe"
| "web"
| "task_agent";

/**
* @deprecated Harness-specific tool-level rules are no longer produced by core.
* Kept temporarily as a compatibility type for downstream packages.
*/
export type PermissionRule =
| PermissionDecision
| Record<string, PermissionDecision>;

export interface PermissionPolicy extends Record<string, unknown> {
profile: AutonomyProfile;
capabilities: Record<AutonomyCapability, PermissionDecision>;
}

export interface LogicalConfig extends Record<string, unknown> {
Expand Down
166 changes: 1 addition & 165 deletions packages/harnesses/src/permission-policy.ts
Original file line number Diff line number Diff line change
@@ -1,121 +1,4 @@
import type {
AutonomyCapability,
LogicalConfig,
PermissionDecision,
PermissionRule
} from "@codemcp/ade-core";

const SENSIBLE_DEFAULTS_RULES: Record<string, PermissionRule> = {
read: {
"*": "allow",
"*.env": "deny",
"*.env.*": "deny",
"*.env.example": "allow"
},
edit: "allow",
glob: "allow",
grep: "allow",
list: "allow",
lsp: "allow",
task: "allow",
todoread: "deny",
todowrite: "deny",
skill: "deny",
webfetch: "ask",
websearch: "ask",
codesearch: "ask",
bash: {
"*": "deny",
"grep *": "allow",
"rg *": "allow",
"find *": "allow",
"fd *": "allow",
ls: "allow",
"ls *": "allow",
"cat *": "allow",
"head *": "allow",
"tail *": "allow",
"wc *": "allow",
"sort *": "allow",
"uniq *": "allow",
"diff *": "allow",
"echo *": "allow",
"printf *": "allow",
pwd: "allow",
"which *": "allow",
"type *": "allow",
whoami: "allow",
date: "allow",
"date *": "allow",
env: "allow",
"tree *": "allow",
"file *": "allow",
"stat *": "allow",
"readlink *": "allow",
"realpath *": "allow",
"dirname *": "allow",
"basename *": "allow",
"sed *": "allow",
"awk *": "allow",
"cut *": "allow",
"tr *": "allow",
"tee *": "allow",
"xargs *": "allow",
"jq *": "allow",
"yq *": "allow",
"mkdir *": "allow",
"touch *": "allow",
"cp *": "ask",
"mv *": "ask",
"ln *": "ask",
"npm *": "ask",
"node *": "ask",
"pip *": "ask",
"python *": "ask",
"python3 *": "ask",
"rm *": "deny",
"rmdir *": "deny",
"curl *": "deny",
"wget *": "deny",
"chmod *": "deny",
"chown *": "deny",
"sudo *": "deny",
"su *": "deny",
"sh *": "deny",
"bash *": "deny",
"zsh *": "deny",
"eval *": "deny",
"exec *": "deny",
"source *": "deny",
". *": "deny",
"nohup *": "deny",
"dd *": "deny",
"mkfs *": "deny",
"mount *": "deny",
"umount *": "deny",
"kill *": "deny",
"killall *": "deny",
"pkill *": "deny",
"nc *": "deny",
"ncat *": "deny",
"ssh *": "deny",
"scp *": "deny",
"rsync *": "deny",
"docker *": "deny",
"kubectl *": "deny",
"systemctl *": "deny",
"service *": "deny",
"crontab *": "deny",
reboot: "deny",
"shutdown *": "deny",
"passwd *": "deny",
"useradd *": "deny",
"userdel *": "deny",
"iptables *": "deny"
},
external_directory: "deny",
doom_loop: "deny"
};
import type { LogicalConfig } from "@codemcp/ade-core";

export function getAutonomyProfile(config: LogicalConfig) {
return config.permission_policy?.profile;
Expand All @@ -124,50 +7,3 @@ export function getAutonomyProfile(config: LogicalConfig) {
export function hasPermissionPolicy(config: LogicalConfig): boolean {
return config.permission_policy !== undefined;
}

export function getCapabilityDecision(
config: LogicalConfig,
capability: AutonomyCapability
): PermissionDecision | undefined {
return config.permission_policy?.capabilities?.[capability];
}

export function allowsCapability(
config: LogicalConfig,
capability: AutonomyCapability
): boolean {
return getCapabilityDecision(config, capability) === "allow";
}

export function keepsWebOnAsk(config: LogicalConfig): boolean {
return getCapabilityDecision(config, "web") === "ask";
}

export function getHarnessPermissionRules(
config: LogicalConfig
): Record<string, PermissionRule> | undefined {
switch (config.permission_policy?.profile) {
case "rigid":
return {
"*": "ask",
webfetch: "ask",
websearch: "ask",
codesearch: "ask",
external_directory: "deny",
doom_loop: "deny"
};
case "sensible-defaults":
return SENSIBLE_DEFAULTS_RULES;
case "max-autonomy":
return {
"*": "allow",
webfetch: "ask",
websearch: "ask",
codesearch: "ask",
external_directory: "deny",
doom_loop: "deny"
};
default:
return undefined;
}
}
Loading