@livx.cc/agentx 0.99.64 → 0.99.66
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/{Agent-3eJ9N9hh.d.ts → Agent-BpCqDWFG.d.ts} +12 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +161 -133
- package/dist/cli.js.map +1 -1
- package/dist/directives.d.ts +74 -0
- package/dist/directives.js +192 -0
- package/dist/directives.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +156 -127
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -3422,6 +3422,149 @@ function resolveModelSwitch(opts) {
|
|
|
3422
3422
|
};
|
|
3423
3423
|
}
|
|
3424
3424
|
|
|
3425
|
+
// src/permissions.ts
|
|
3426
|
+
init_tools_structured();
|
|
3427
|
+
function commandMatches(glob, cmd) {
|
|
3428
|
+
const pattern = glob.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
3429
|
+
return new RegExp("^" + pattern + "$").test(cmd);
|
|
3430
|
+
}
|
|
3431
|
+
function toolMatches(rule, name) {
|
|
3432
|
+
return rule.includes("*") ? commandMatches(rule, name) : rule === name;
|
|
3433
|
+
}
|
|
3434
|
+
var PermissionOptions = class {
|
|
3435
|
+
rules = [];
|
|
3436
|
+
/** decision when no rule matches. */
|
|
3437
|
+
default = "allow";
|
|
3438
|
+
/** host channel for `ask` (confirm). If absent, `ask` is treated as `deny` (fail-closed). */
|
|
3439
|
+
host;
|
|
3440
|
+
};
|
|
3441
|
+
var PermissionPolicy = class {
|
|
3442
|
+
options;
|
|
3443
|
+
constructor(options) {
|
|
3444
|
+
this.options = { ...new PermissionOptions(), ...options };
|
|
3445
|
+
}
|
|
3446
|
+
/** Resolve the decision for a tool call (first matching rule, else default). */
|
|
3447
|
+
decide(call) {
|
|
3448
|
+
const names = call.name === "McpCall" && typeof call.args?.name === "string" ? [call.name, call.args.name] : [call.name];
|
|
3449
|
+
for (const r of this.options.rules) {
|
|
3450
|
+
if (r.tool && !names.some((n) => toolMatches(r.tool, n))) continue;
|
|
3451
|
+
if (r.pathGlob) {
|
|
3452
|
+
const path = typeof call.args?.path === "string" ? call.args.path : null;
|
|
3453
|
+
const cmd = typeof call.args?.command === "string" ? call.args.command : null;
|
|
3454
|
+
if (path != null) {
|
|
3455
|
+
if (!globToRegExp(r.pathGlob).test(path.startsWith("/") ? path : `/${path}`)) continue;
|
|
3456
|
+
} else if (cmd != null) {
|
|
3457
|
+
if (!commandMatches(r.pathGlob, cmd)) continue;
|
|
3458
|
+
} else continue;
|
|
3459
|
+
}
|
|
3460
|
+
return r.decision;
|
|
3461
|
+
}
|
|
3462
|
+
return this.options.default;
|
|
3463
|
+
}
|
|
3464
|
+
/** The policy's `deny` rules as rule strings (`Tool` / `Tool(glob)`) — for delegated runtimes that run
|
|
3465
|
+
* their OWN tools (claude-code/cursor) and never pass through {@link hooks}. */
|
|
3466
|
+
denyRules(explicitOnly = false) {
|
|
3467
|
+
return this.options.rules.filter((r) => r.decision === "deny" && (!explicitOnly || r.explicit)).map((r) => `${r.tool ?? "*"}${r.pathGlob ? `(${r.pathGlob})` : ""}`);
|
|
3468
|
+
}
|
|
3469
|
+
/** A preToolUse hook enforcing this policy (deny/ask → block; allow → proceed). */
|
|
3470
|
+
hooks() {
|
|
3471
|
+
return {
|
|
3472
|
+
preToolUse: async (call) => {
|
|
3473
|
+
const d = this.decide(call);
|
|
3474
|
+
if (d === "allow") return;
|
|
3475
|
+
if (d === "deny") return { block: true, reason: `denied by permission policy (${call.name})` };
|
|
3476
|
+
if (this.options.ask) {
|
|
3477
|
+
const r = await this.options.ask(call);
|
|
3478
|
+
const decision = r?.decision ?? "deny";
|
|
3479
|
+
const rememberTool2 = call.name === "McpCall" && typeof call.args?.name === "string" ? call.args.name : call.name;
|
|
3480
|
+
if (r?.remember) this.options.rules.unshift({ tool: rememberTool2, decision });
|
|
3481
|
+
return decision === "allow" ? void 0 : { block: true, reason: `not approved (${call.name})` };
|
|
3482
|
+
}
|
|
3483
|
+
const ok = await this.options.host?.confirm?.(`Allow ${call.name}${call.args?.path ? " on " + call.args.path : ""}?`);
|
|
3484
|
+
return ok ? void 0 : { block: true, reason: `not approved (${call.name})` };
|
|
3485
|
+
}
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
};
|
|
3489
|
+
var CC_TOOL_ALIAS = { Shell: "Bash", bash: "Bash" };
|
|
3490
|
+
function claudeCodeDisallowedTools(policy) {
|
|
3491
|
+
const out = /* @__PURE__ */ new Set();
|
|
3492
|
+
for (const rule of policy?.denyRules() ?? []) {
|
|
3493
|
+
const m = /^([^(]+)(\(.*\))?$/.exec(rule);
|
|
3494
|
+
if (m[1] === "*") throw new Error("permission policy denies every tool (`*`) \u2014 claude-code cannot express that; refusing to run.");
|
|
3495
|
+
out.add((CC_TOOL_ALIAS[m[1]] ?? m[1]) + (m[2] ?? ""));
|
|
3496
|
+
}
|
|
3497
|
+
return [...out];
|
|
3498
|
+
}
|
|
3499
|
+
var DEFAULT_MUTATING = ["Write", "Edit", "MultiEdit", "deleteFile", "bash"];
|
|
3500
|
+
function planMode(opts) {
|
|
3501
|
+
const mutating = new Set(opts?.mutating ?? DEFAULT_MUTATING);
|
|
3502
|
+
let approved = false;
|
|
3503
|
+
const tool = {
|
|
3504
|
+
name: "ExitPlanMode",
|
|
3505
|
+
description: "Call when your plan is ready and you want to start making changes. Provide the `plan`. Until this is approved, edits/writes are blocked.",
|
|
3506
|
+
parameters: { type: "object", required: ["plan"], properties: { plan: { type: "string", description: "the concrete steps you will take" } } },
|
|
3507
|
+
async run({ plan }, _ctx) {
|
|
3508
|
+
if (opts?.host?.confirm) {
|
|
3509
|
+
const confirm = opts.host.confirm(`Approve this plan?
|
|
3510
|
+
|
|
3511
|
+
${String(plan ?? "")}`);
|
|
3512
|
+
const ok = await (_ctx?.parkHuman ? _ctx.parkHuman(confirm) : confirm);
|
|
3513
|
+
if (!ok) return "Plan not approved. Revise it and call ExitPlanMode again.";
|
|
3514
|
+
}
|
|
3515
|
+
approved = true;
|
|
3516
|
+
return "Plan approved \u2014 you may now make changes.";
|
|
3517
|
+
}
|
|
3518
|
+
};
|
|
3519
|
+
const hooks = {
|
|
3520
|
+
preToolUse: (call) => !approved && mutating.has(call.name) ? { block: true, reason: "plan mode: present a plan and call ExitPlanMode (approved) before editing." } : void 0
|
|
3521
|
+
};
|
|
3522
|
+
return { hooks, tool };
|
|
3523
|
+
}
|
|
3524
|
+
function composeHooks(...list) {
|
|
3525
|
+
const hooks = list.filter(Boolean);
|
|
3526
|
+
return {
|
|
3527
|
+
async preToolUse(call, meta) {
|
|
3528
|
+
for (const h of hooks) {
|
|
3529
|
+
const d = await h.preToolUse?.(call, meta);
|
|
3530
|
+
if (d?.block) return d;
|
|
3531
|
+
}
|
|
3532
|
+
},
|
|
3533
|
+
async postToolUse(call, result, meta) {
|
|
3534
|
+
for (const h of hooks) await h.postToolUse?.(call, result, meta);
|
|
3535
|
+
},
|
|
3536
|
+
onToolOutput(call, chunk, meta) {
|
|
3537
|
+
for (const h of hooks) h.onToolOutput?.(call, chunk, meta);
|
|
3538
|
+
},
|
|
3539
|
+
onStop(text) {
|
|
3540
|
+
for (const h of hooks) h.onStop?.(text);
|
|
3541
|
+
},
|
|
3542
|
+
// lifecycle: concatenate session-start context; chain prompt-submit transforms; fan out pre-compact.
|
|
3543
|
+
async onSessionStart() {
|
|
3544
|
+
let ctx = "";
|
|
3545
|
+
for (const h of hooks) {
|
|
3546
|
+
const r = await h.onSessionStart?.();
|
|
3547
|
+
if (r) ctx += (ctx ? "\n\n" : "") + r;
|
|
3548
|
+
}
|
|
3549
|
+
return ctx || void 0;
|
|
3550
|
+
},
|
|
3551
|
+
async onUserPromptSubmit(text) {
|
|
3552
|
+
let t = text;
|
|
3553
|
+
for (const h of hooks) {
|
|
3554
|
+
const r = await h.onUserPromptSubmit?.(t);
|
|
3555
|
+
if (typeof r === "string") t = r;
|
|
3556
|
+
}
|
|
3557
|
+
return t;
|
|
3558
|
+
},
|
|
3559
|
+
async onPreCompact(messages) {
|
|
3560
|
+
for (const h of hooks) await h.onPreCompact?.(messages);
|
|
3561
|
+
},
|
|
3562
|
+
async onSubagentStop(summary, info) {
|
|
3563
|
+
for (const h of hooks) await h.onSubagentStop?.(summary, info);
|
|
3564
|
+
}
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3425
3568
|
// src/subagent.ts
|
|
3426
3569
|
init_OverlayFilesystem();
|
|
3427
3570
|
|
|
@@ -3729,6 +3872,8 @@ function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
|
|
|
3729
3872
|
}
|
|
3730
3873
|
const po = opts.providerOptionsFor?.(childModel);
|
|
3731
3874
|
if (po) childOpts.providerOptions = po;
|
|
3875
|
+
const deny = opts.permissions?.options.rules.filter((r) => r.decision === "deny") ?? [];
|
|
3876
|
+
if (deny.length) childOpts.permissions = new PermissionPolicy({ rules: deny, default: "allow" });
|
|
3732
3877
|
if (opts.hooks?.preToolUse) childOpts.hooks = { preToolUse: opts.hooks.preToolUse.bind(opts.hooks) };
|
|
3733
3878
|
return childOpts;
|
|
3734
3879
|
}
|
|
@@ -3948,133 +4093,6 @@ async function loadAgents(fs, dir) {
|
|
|
3948
4093
|
|
|
3949
4094
|
// src/Agent.ts
|
|
3950
4095
|
init_OverlayFilesystem();
|
|
3951
|
-
|
|
3952
|
-
// src/permissions.ts
|
|
3953
|
-
init_tools_structured();
|
|
3954
|
-
function commandMatches(glob, cmd) {
|
|
3955
|
-
const pattern = glob.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
3956
|
-
return new RegExp("^" + pattern + "$").test(cmd);
|
|
3957
|
-
}
|
|
3958
|
-
var PermissionOptions = class {
|
|
3959
|
-
rules = [];
|
|
3960
|
-
/** decision when no rule matches. */
|
|
3961
|
-
default = "allow";
|
|
3962
|
-
/** host channel for `ask` (confirm). If absent, `ask` is treated as `deny` (fail-closed). */
|
|
3963
|
-
host;
|
|
3964
|
-
};
|
|
3965
|
-
var PermissionPolicy = class {
|
|
3966
|
-
options;
|
|
3967
|
-
constructor(options) {
|
|
3968
|
-
this.options = { ...new PermissionOptions(), ...options };
|
|
3969
|
-
}
|
|
3970
|
-
/** Resolve the decision for a tool call (first matching rule, else default). */
|
|
3971
|
-
decide(call) {
|
|
3972
|
-
const names = call.name === "McpCall" && typeof call.args?.name === "string" ? [call.name, call.args.name] : [call.name];
|
|
3973
|
-
for (const r of this.options.rules) {
|
|
3974
|
-
if (r.tool && !names.includes(r.tool)) continue;
|
|
3975
|
-
if (r.pathGlob) {
|
|
3976
|
-
const path = typeof call.args?.path === "string" ? call.args.path : null;
|
|
3977
|
-
const cmd = typeof call.args?.command === "string" ? call.args.command : null;
|
|
3978
|
-
if (path != null) {
|
|
3979
|
-
if (!globToRegExp(r.pathGlob).test(path.startsWith("/") ? path : `/${path}`)) continue;
|
|
3980
|
-
} else if (cmd != null) {
|
|
3981
|
-
if (!commandMatches(r.pathGlob, cmd)) continue;
|
|
3982
|
-
} else continue;
|
|
3983
|
-
}
|
|
3984
|
-
return r.decision;
|
|
3985
|
-
}
|
|
3986
|
-
return this.options.default;
|
|
3987
|
-
}
|
|
3988
|
-
/** A preToolUse hook enforcing this policy (deny/ask → block; allow → proceed). */
|
|
3989
|
-
hooks() {
|
|
3990
|
-
return {
|
|
3991
|
-
preToolUse: async (call) => {
|
|
3992
|
-
const d = this.decide(call);
|
|
3993
|
-
if (d === "allow") return;
|
|
3994
|
-
if (d === "deny") return { block: true, reason: `denied by permission policy (${call.name})` };
|
|
3995
|
-
if (this.options.ask) {
|
|
3996
|
-
const r = await this.options.ask(call);
|
|
3997
|
-
const decision = r?.decision ?? "deny";
|
|
3998
|
-
const rememberTool2 = call.name === "McpCall" && typeof call.args?.name === "string" ? call.args.name : call.name;
|
|
3999
|
-
if (r?.remember) this.options.rules.unshift({ tool: rememberTool2, decision });
|
|
4000
|
-
return decision === "allow" ? void 0 : { block: true, reason: `not approved (${call.name})` };
|
|
4001
|
-
}
|
|
4002
|
-
const ok = await this.options.host?.confirm?.(`Allow ${call.name}${call.args?.path ? " on " + call.args.path : ""}?`);
|
|
4003
|
-
return ok ? void 0 : { block: true, reason: `not approved (${call.name})` };
|
|
4004
|
-
}
|
|
4005
|
-
};
|
|
4006
|
-
}
|
|
4007
|
-
};
|
|
4008
|
-
var DEFAULT_MUTATING = ["Write", "Edit", "MultiEdit", "deleteFile", "bash"];
|
|
4009
|
-
function planMode(opts) {
|
|
4010
|
-
const mutating = new Set(opts?.mutating ?? DEFAULT_MUTATING);
|
|
4011
|
-
let approved = false;
|
|
4012
|
-
const tool = {
|
|
4013
|
-
name: "ExitPlanMode",
|
|
4014
|
-
description: "Call when your plan is ready and you want to start making changes. Provide the `plan`. Until this is approved, edits/writes are blocked.",
|
|
4015
|
-
parameters: { type: "object", required: ["plan"], properties: { plan: { type: "string", description: "the concrete steps you will take" } } },
|
|
4016
|
-
async run({ plan }, _ctx) {
|
|
4017
|
-
if (opts?.host?.confirm) {
|
|
4018
|
-
const confirm = opts.host.confirm(`Approve this plan?
|
|
4019
|
-
|
|
4020
|
-
${String(plan ?? "")}`);
|
|
4021
|
-
const ok = await (_ctx?.parkHuman ? _ctx.parkHuman(confirm) : confirm);
|
|
4022
|
-
if (!ok) return "Plan not approved. Revise it and call ExitPlanMode again.";
|
|
4023
|
-
}
|
|
4024
|
-
approved = true;
|
|
4025
|
-
return "Plan approved \u2014 you may now make changes.";
|
|
4026
|
-
}
|
|
4027
|
-
};
|
|
4028
|
-
const hooks = {
|
|
4029
|
-
preToolUse: (call) => !approved && mutating.has(call.name) ? { block: true, reason: "plan mode: present a plan and call ExitPlanMode (approved) before editing." } : void 0
|
|
4030
|
-
};
|
|
4031
|
-
return { hooks, tool };
|
|
4032
|
-
}
|
|
4033
|
-
function composeHooks(...list) {
|
|
4034
|
-
const hooks = list.filter(Boolean);
|
|
4035
|
-
return {
|
|
4036
|
-
async preToolUse(call, meta) {
|
|
4037
|
-
for (const h of hooks) {
|
|
4038
|
-
const d = await h.preToolUse?.(call, meta);
|
|
4039
|
-
if (d?.block) return d;
|
|
4040
|
-
}
|
|
4041
|
-
},
|
|
4042
|
-
async postToolUse(call, result, meta) {
|
|
4043
|
-
for (const h of hooks) await h.postToolUse?.(call, result, meta);
|
|
4044
|
-
},
|
|
4045
|
-
onToolOutput(call, chunk, meta) {
|
|
4046
|
-
for (const h of hooks) h.onToolOutput?.(call, chunk, meta);
|
|
4047
|
-
},
|
|
4048
|
-
onStop(text) {
|
|
4049
|
-
for (const h of hooks) h.onStop?.(text);
|
|
4050
|
-
},
|
|
4051
|
-
// lifecycle: concatenate session-start context; chain prompt-submit transforms; fan out pre-compact.
|
|
4052
|
-
async onSessionStart() {
|
|
4053
|
-
let ctx = "";
|
|
4054
|
-
for (const h of hooks) {
|
|
4055
|
-
const r = await h.onSessionStart?.();
|
|
4056
|
-
if (r) ctx += (ctx ? "\n\n" : "") + r;
|
|
4057
|
-
}
|
|
4058
|
-
return ctx || void 0;
|
|
4059
|
-
},
|
|
4060
|
-
async onUserPromptSubmit(text) {
|
|
4061
|
-
let t = text;
|
|
4062
|
-
for (const h of hooks) {
|
|
4063
|
-
const r = await h.onUserPromptSubmit?.(t);
|
|
4064
|
-
if (typeof r === "string") t = r;
|
|
4065
|
-
}
|
|
4066
|
-
return t;
|
|
4067
|
-
},
|
|
4068
|
-
async onPreCompact(messages) {
|
|
4069
|
-
for (const h of hooks) await h.onPreCompact?.(messages);
|
|
4070
|
-
},
|
|
4071
|
-
async onSubagentStop(summary, info) {
|
|
4072
|
-
for (const h of hooks) await h.onSubagentStop?.(summary, info);
|
|
4073
|
-
}
|
|
4074
|
-
};
|
|
4075
|
-
}
|
|
4076
|
-
|
|
4077
|
-
// src/Agent.ts
|
|
4078
4096
|
init_logging();
|
|
4079
4097
|
|
|
4080
4098
|
// src/lint.ts
|
|
@@ -4594,6 +4612,8 @@ var Agent = class _Agent {
|
|
|
4594
4612
|
agents,
|
|
4595
4613
|
hooks: o.hooks,
|
|
4596
4614
|
traceDir: o.traceDir,
|
|
4615
|
+
permissions: o.permissions,
|
|
4616
|
+
// a child obeys the parent's allow/deny rules (else Task is a one-call bypass)
|
|
4597
4617
|
tools: [...o.tools, ...this.injectedTools],
|
|
4598
4618
|
systemPrompt: o.systemPrompt,
|
|
4599
4619
|
providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) ? o.providerOptions : void 0)
|
|
@@ -4766,6 +4786,14 @@ var Agent = class _Agent {
|
|
|
4766
4786
|
}
|
|
4767
4787
|
} : void 0;
|
|
4768
4788
|
const claudeSteerPo = o.model.startsWith("claude-code/") ? { steer: () => this.drainPendingSteers() } : void 0;
|
|
4789
|
+
const denyTools = o.model.startsWith("claude-code/") ? claudeCodeDisallowedTools(o.permissions) : [];
|
|
4790
|
+
if (denyTools.length) {
|
|
4791
|
+
const prior = o.providerOptions?.disallowedTools ?? [];
|
|
4792
|
+
claudeSteerPo.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
|
|
4793
|
+
}
|
|
4794
|
+
const cursorDeny = o.model.startsWith("cursor/") ? o.permissions?.denyRules(true) ?? [] : [];
|
|
4795
|
+
if (cursorDeny.length)
|
|
4796
|
+
throw new Error(`cursor/* cannot enforce --disallowedTools (${cursorDeny.join(", ")}): its native tools bypass them. Refusing to run \u2014 use a claude-code/* or API model, or drop --disallowedTools.`);
|
|
4769
4797
|
const reasonOpts = {
|
|
4770
4798
|
...frag,
|
|
4771
4799
|
...o.promptCache ? { promptCache: true } : {},
|
|
@@ -11013,6 +11041,7 @@ export {
|
|
|
11013
11041
|
buildMcpCatalog,
|
|
11014
11042
|
checkpointTool,
|
|
11015
11043
|
checkpointTools,
|
|
11044
|
+
claudeCodeDisallowedTools,
|
|
11016
11045
|
cleanupWorktree,
|
|
11017
11046
|
clearImageRefs,
|
|
11018
11047
|
compileSynthesizedTool,
|