@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/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,