@element47/ag 4.5.1 → 4.5.3

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.
Files changed (77) hide show
  1. package/README.md +184 -8
  2. package/dist/cli/__tests__/repl-rawmode.test.d.ts +2 -0
  3. package/dist/cli/__tests__/repl-rawmode.test.d.ts.map +1 -0
  4. package/dist/cli/__tests__/repl-rawmode.test.js +46 -0
  5. package/dist/cli/__tests__/repl-rawmode.test.js.map +1 -0
  6. package/dist/cli/repl.d.ts +7 -2
  7. package/dist/cli/repl.d.ts.map +1 -1
  8. package/dist/cli/repl.js +203 -25
  9. package/dist/cli/repl.js.map +1 -1
  10. package/dist/cli.js +10 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/core/__tests__/agent.test.js +42 -1
  13. package/dist/core/__tests__/agent.test.js.map +1 -1
  14. package/dist/core/__tests__/context.test.js +81 -0
  15. package/dist/core/__tests__/context.test.js.map +1 -1
  16. package/dist/core/__tests__/events.test.d.ts +2 -0
  17. package/dist/core/__tests__/events.test.d.ts.map +1 -0
  18. package/dist/core/__tests__/events.test.js +131 -0
  19. package/dist/core/__tests__/events.test.js.map +1 -0
  20. package/dist/core/__tests__/extensions.test.d.ts +2 -0
  21. package/dist/core/__tests__/extensions.test.d.ts.map +1 -0
  22. package/dist/core/__tests__/extensions.test.js +59 -0
  23. package/dist/core/__tests__/extensions.test.js.map +1 -0
  24. package/dist/core/__tests__/guardrails.test.d.ts +2 -0
  25. package/dist/core/__tests__/guardrails.test.d.ts.map +1 -0
  26. package/dist/core/__tests__/guardrails.test.js +418 -0
  27. package/dist/core/__tests__/guardrails.test.js.map +1 -0
  28. package/dist/core/__tests__/permission-manager.test.d.ts +2 -0
  29. package/dist/core/__tests__/permission-manager.test.d.ts.map +1 -0
  30. package/dist/core/__tests__/permission-manager.test.js +246 -0
  31. package/dist/core/__tests__/permission-manager.test.js.map +1 -0
  32. package/dist/core/__tests__/streaming.test.js +8 -1
  33. package/dist/core/__tests__/streaming.test.js.map +1 -1
  34. package/dist/core/agent.d.ts +27 -0
  35. package/dist/core/agent.d.ts.map +1 -1
  36. package/dist/core/agent.js +263 -121
  37. package/dist/core/agent.js.map +1 -1
  38. package/dist/core/context.d.ts.map +1 -1
  39. package/dist/core/context.js +14 -6
  40. package/dist/core/context.js.map +1 -1
  41. package/dist/core/events.d.ts +62 -0
  42. package/dist/core/events.d.ts.map +1 -0
  43. package/dist/core/events.js +23 -0
  44. package/dist/core/events.js.map +1 -0
  45. package/dist/core/extensions.d.ts +10 -0
  46. package/dist/core/extensions.d.ts.map +1 -0
  47. package/dist/core/extensions.js +66 -0
  48. package/dist/core/extensions.js.map +1 -0
  49. package/dist/core/guardrails.d.ts +32 -0
  50. package/dist/core/guardrails.d.ts.map +1 -0
  51. package/dist/core/guardrails.js +149 -0
  52. package/dist/core/guardrails.js.map +1 -0
  53. package/dist/core/loader.d.ts +6 -2
  54. package/dist/core/loader.d.ts.map +1 -1
  55. package/dist/core/loader.js +23 -9
  56. package/dist/core/loader.js.map +1 -1
  57. package/dist/core/permissions.d.ts +60 -0
  58. package/dist/core/permissions.d.ts.map +1 -0
  59. package/dist/core/permissions.js +252 -0
  60. package/dist/core/permissions.js.map +1 -0
  61. package/dist/core/registry.d.ts.map +1 -1
  62. package/dist/core/registry.js +16 -0
  63. package/dist/core/registry.js.map +1 -1
  64. package/dist/core/skills.d.ts.map +1 -1
  65. package/dist/core/skills.js +26 -3
  66. package/dist/core/skills.js.map +1 -1
  67. package/dist/core/types.d.ts +15 -1
  68. package/dist/core/types.d.ts.map +1 -1
  69. package/dist/memory/__tests__/memory.test.js +28 -0
  70. package/dist/memory/__tests__/memory.test.js.map +1 -1
  71. package/dist/memory/memory.js +1 -1
  72. package/dist/memory/memory.js.map +1 -1
  73. package/dist/tools/__tests__/bash.test.js +4 -0
  74. package/dist/tools/__tests__/bash.test.js.map +1 -1
  75. package/dist/tools/__tests__/file.test.js +7 -1
  76. package/dist/tools/__tests__/file.test.js.map +1 -1
  77. package/package.json +1 -4
@@ -0,0 +1,149 @@
1
+ export const GUARDRAIL_PATTERNS = [
2
+ // Direct injection — attempts to override system instructions
3
+ { pattern: /ignore\s+(all\s+)?previous\s+instructions/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "ignore previous instructions"' },
4
+ { pattern: /you\s+are\s+now\s+(in\s+)?developer\s+mode/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "developer mode" activation' },
5
+ { pattern: /system\s+override/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "system override"' },
6
+ { pattern: /reveal\s+(your\s+)?(system\s+)?prompt/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: prompt exfiltration attempt' },
7
+ { pattern: /disregard\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions|rules|guidelines)/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "disregard previous instructions"' },
8
+ { pattern: /forget\s+(all\s+)?(previous|prior|your)\s+(instructions|rules|guidelines)/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "forget instructions"' },
9
+ { pattern: /new\s+instructions?\s*:/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "new instructions" directive' },
10
+ { pattern: /act\s+as\s+(if\s+you\s+are\s+|a\s+)?(unrestricted|jailbroken|unfiltered)/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: jailbreak attempt' },
11
+ { pattern: /do\s+not\s+follow\s+(any\s+)?(your\s+)?(previous|original)\s+(instructions|rules)/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: "do not follow" directive' },
12
+ { pattern: /pretend\s+(you\s+are|to\s+be)\s+(a\s+)?(different|new|unrestricted|unfiltered)/i, severity: 'block', category: 'direct-injection', message: 'prompt injection: identity override' },
13
+ // Suspicious override — language specifically targeting the agent's security/permission system
14
+ { pattern: /\b(always|never|must)\s+(ignore|override|skip|bypass)\s+(permission|security|guard|confirmation|prompt|rule)/i, severity: 'warn', category: 'suspicious-override', message: 'suspicious instruction-like language targeting agent security' },
15
+ { pattern: /\boverride\s+(system\s+prompt|security|permission|guardrail)/i, severity: 'warn', category: 'suspicious-override', message: 'suspicious "override system/security" language' },
16
+ { pattern: /\b(bypass|disable|skip)\s+(permission|security|confirmation|guard)/i, severity: 'warn', category: 'suspicious-override', message: 'suspicious "bypass security" language', skipInCodeBlocks: true },
17
+ { pattern: /\brun\s+without\s+(permission|confirmation|asking)/i, severity: 'warn', category: 'suspicious-override', message: 'suspicious "run without permission" language' },
18
+ { pattern: /\bauto[\s-]?approve\s+(all|every|tool|action|command)/i, severity: 'warn', category: 'suspicious-override', message: 'suspicious "auto-approve" language' },
19
+ // Hidden content — invisible payloads
20
+ { pattern: /<!--[\s\S]*?\b(ignore|override|instruction|disregard|forget|reveal)\b[\s\S]*?-->/i, severity: 'block', category: 'hidden-content', message: 'HTML comment contains suspicious instruction' },
21
+ { pattern: /[\x00-\x08\x0E-\x1F]/, severity: 'block', category: 'hidden-content', message: 'content contains control characters' },
22
+ // Exfiltration — data exfil attempts in descriptions
23
+ { pattern: /\b(fetch|curl|wget|axios)\s*\(/i, severity: 'block', category: 'exfiltration', message: 'description contains network call invocation' },
24
+ { pattern: /https?:\/\/[^\s"')<>]{10,}/i, severity: 'warn', category: 'exfiltration', message: 'description contains URL', skipInCodeBlocks: true },
25
+ { pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/i, severity: 'warn', category: 'exfiltration', message: 'description contains email address', skipInCodeBlocks: true },
26
+ { pattern: /\bwebhook\b/i, severity: 'warn', category: 'exfiltration', message: 'description references webhook', skipInCodeBlocks: true },
27
+ // Encoded payload — HTML entities that could hide instructions
28
+ { pattern: /&#x?[0-9a-fA-F]+;.*&#x?[0-9a-fA-F]+;/i, severity: 'warn', category: 'encoded-payload', message: 'content contains HTML entity encoding' },
29
+ ];
30
+ // Zero-width and bidirectional characters
31
+ const ZERO_WIDTH_RE = /[\u200B\u200C\u200D\uFEFF\u2060\u200E\u200F\u202A-\u202E]/;
32
+ // Base64: 40+ chars from the base64 alphabet, optionally ending with = or ==
33
+ const BASE64_RE = /[A-Za-z0-9+/]{40,}={0,2}/g;
34
+ // Suspicious tool names
35
+ const SUSPICIOUS_TOOL_NAME_RE = /\b(system_override|admin_override|sudo|root_access)\b/i;
36
+ // Fenced code block pattern — used to strip code blocks before exfiltration checks
37
+ const FENCED_CODE_BLOCK_RE = /```[\s\S]*?```/g;
38
+ // Inline code pattern
39
+ const INLINE_CODE_RE = /`[^`]+`/g;
40
+ /** Strip fenced and inline code blocks from content */
41
+ export function stripCodeBlocks(content) {
42
+ return content.replace(FENCED_CODE_BLOCK_RE, '').replace(INLINE_CODE_RE, '');
43
+ }
44
+ // ── Helpers ──────────────────────────────────────────────────────────────────
45
+ export function decodeBase64Safe(str) {
46
+ try {
47
+ const buf = Buffer.from(str, 'base64');
48
+ // Reject if it doesn't round-trip (not valid base64)
49
+ if (buf.toString('base64').replace(/=+$/, '') !== str.replace(/=+$/, ''))
50
+ return null;
51
+ const decoded = buf.toString('utf-8');
52
+ // Reject binary content (contains null bytes)
53
+ if (decoded.includes('\0'))
54
+ return null;
55
+ return decoded;
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ export function hasZeroWidthChars(content) {
62
+ return ZERO_WIDTH_RE.test(content);
63
+ }
64
+ export function mergeResults(...results) {
65
+ const findings = [];
66
+ for (const r of results)
67
+ findings.push(...r.findings);
68
+ return { ok: findings.every(f => f.severity !== 'block'), findings };
69
+ }
70
+ // ── Core Scanning ────────────────────────────────────────────────────────────
71
+ export function scanContent(content, context, _depth = 0) {
72
+ if (!content)
73
+ return { ok: true, findings: [] };
74
+ const findings = [];
75
+ const strippedContent = stripCodeBlocks(content);
76
+ // Run all regex patterns
77
+ for (const { pattern, severity, category, message, skipInCodeBlocks } of GUARDRAIL_PATTERNS) {
78
+ const target = skipInCodeBlocks ? strippedContent : content;
79
+ const match = target.match(pattern);
80
+ if (match) {
81
+ findings.push({ severity, category, message: `${context}: ${message}`, matched: match[0] });
82
+ }
83
+ }
84
+ // Check for zero-width characters
85
+ if (hasZeroWidthChars(content)) {
86
+ findings.push({
87
+ severity: 'block',
88
+ category: 'hidden-content',
89
+ message: `${context}: content contains zero-width or bidirectional characters`,
90
+ matched: content.match(ZERO_WIDTH_RE)[0],
91
+ });
92
+ }
93
+ // Check for base64-encoded payloads (one level of recursion)
94
+ if (_depth === 0) {
95
+ const b64Matches = content.match(BASE64_RE);
96
+ if (b64Matches) {
97
+ for (const b64 of b64Matches) {
98
+ const decoded = decodeBase64Safe(b64);
99
+ if (decoded) {
100
+ const inner = scanContent(decoded, `${context} (decoded base64)`, 1);
101
+ if (!inner.ok) {
102
+ findings.push({
103
+ severity: 'block',
104
+ category: 'encoded-payload',
105
+ message: `${context}: base64-encoded content contains injection`,
106
+ matched: b64.slice(0, 60) + (b64.length > 60 ? '...' : ''),
107
+ });
108
+ findings.push(...inner.findings);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return { ok: findings.every(f => f.severity !== 'block'), findings };
115
+ }
116
+ export function scanTool(tool) {
117
+ const name = tool.function.name;
118
+ const results = [];
119
+ // Scan tool description
120
+ results.push(scanContent(tool.function.description, `tool "${name}" description`));
121
+ // Scan parameter descriptions
122
+ const props = tool.function.parameters?.properties;
123
+ if (props) {
124
+ for (const [paramName, paramDef] of Object.entries(props)) {
125
+ const desc = paramDef.description;
126
+ if (desc) {
127
+ results.push(scanContent(desc, `tool "${name}" param "${paramName}"`));
128
+ }
129
+ }
130
+ }
131
+ // Check for suspicious tool names
132
+ if (SUSPICIOUS_TOOL_NAME_RE.test(name)) {
133
+ const finding = {
134
+ severity: 'warn',
135
+ category: 'suspicious-override',
136
+ message: `tool "${name}": suspicious tool name`,
137
+ matched: name,
138
+ };
139
+ results.push({ ok: true, findings: [finding] });
140
+ }
141
+ return mergeResults(...results);
142
+ }
143
+ export function scanSkill(skill) {
144
+ const results = [];
145
+ results.push(scanContent(skill.content, `skill "${skill.name}"`));
146
+ results.push(scanContent(skill.description, `skill "${skill.name}" description`));
147
+ return mergeResults(...results);
148
+ }
149
+ //# sourceMappingURL=guardrails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAqCA,MAAM,CAAC,MAAM,kBAAkB,GAAuB;IACpD,8DAA8D;IAC9D,EAAE,OAAO,EAAE,4CAA4C,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,kDAAkD,EAAE;IACvK,EAAE,OAAO,EAAE,6CAA6C,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,+CAA+C,EAAE;IACrK,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,qCAAqC,EAAE;IAClI,EAAE,OAAO,EAAE,wCAAwC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,+CAA+C,EAAE;IAChK,EAAE,OAAO,EAAE,wFAAwF,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,qDAAqD,EAAE;IACtN,EAAE,OAAO,EAAE,4EAA4E,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,yCAAyC,EAAE;IAC9L,EAAE,OAAO,EAAE,0BAA0B,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,gDAAgD,EAAE;IACnJ,EAAE,OAAO,EAAE,2EAA2E,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,qCAAqC,EAAE;IACzL,EAAE,OAAO,EAAE,oFAAoF,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,6CAA6C,EAAE;IAC1M,EAAE,OAAO,EAAE,iFAAiF,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,qCAAqC,EAAE;IAE/L,+FAA+F;IAC/F,EAAE,OAAO,EAAE,+GAA+G,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,+DAA+D,EAAE;IACzP,EAAE,OAAO,EAAE,+DAA+D,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,gDAAgD,EAAE;IAC1L,EAAE,OAAO,EAAE,qEAAqE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,uCAAuC,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAC/M,EAAE,OAAO,EAAE,qDAAqD,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,8CAA8C,EAAE;IAC9K,EAAE,OAAO,EAAE,wDAAwD,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,OAAO,EAAE,oCAAoC,EAAE;IAEvK,sCAAsC;IACtC,EAAE,OAAO,EAAE,mFAAmF,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,8CAA8C,EAAE;IACxM,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,qCAAqC,EAAE;IAElI,qDAAqD;IACrD,EAAE,OAAO,EAAE,iCAAiC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,8CAA8C,EAAE;IACpJ,EAAE,OAAO,EAAE,6BAA6B,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACnJ,EAAE,OAAO,EAAE,iDAAiD,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,oCAAoC,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACjL,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAE1I,+DAA+D;IAC/D,EAAE,OAAO,EAAE,uCAAuC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,uCAAuC,EAAE;CACtJ,CAAC;AAEF,0CAA0C;AAC1C,MAAM,aAAa,GAAG,2DAA2D,CAAC;AAElF,6EAA6E;AAC7E,MAAM,SAAS,GAAG,2BAA2B,CAAC;AAE9C,wBAAwB;AACxB,MAAM,uBAAuB,GAAG,wDAAwD,CAAC;AAEzF,mFAAmF;AACnF,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAE/C,sBAAsB;AACtB,MAAM,cAAc,GAAG,UAAU,CAAC;AAElC,uDAAuD;AACvD,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACvC,qDAAqD;QACrD,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QACtF,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACtC,8CAA8C;QAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACxC,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAG,OAAqB;IACnD,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtD,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;AACvE,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,OAAe,EAAE,MAAM,GAAG,CAAC;IACtE,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAEhD,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAEjD,yBAAyB;IACzB,KAAK,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE,IAAI,kBAAkB,EAAE,CAAC;QAC5F,MAAM,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,KAAK,EAAE,CAAC;YACV,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,KAAK,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,IAAI,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,gBAAgB;YAC1B,OAAO,EAAE,GAAG,OAAO,2DAA2D;YAC9E,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,aAAa,CAAE,CAAC,CAAC,CAAC;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,6DAA6D;IAC7D,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,OAAO,mBAAmB,EAAE,CAAC,CAAC,CAAC;oBACrE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;wBACd,QAAQ,CAAC,IAAI,CAAC;4BACZ,QAAQ,EAAE,OAAO;4BACjB,QAAQ,EAAE,iBAAiB;4BAC3B,OAAO,EAAE,GAAG,OAAO,6CAA6C;4BAChE,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;yBAC3D,CAAC,CAAC;wBACH,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;oBACnC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAU;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAChC,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,wBAAwB;IACxB,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,IAAI,eAAe,CAAC,CAAC,CAAC;IAEnF,8BAA8B;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IACnD,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,GAAI,QAAqC,CAAC,WAAW,CAAC;YAChE,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,IAAI,YAAY,SAAS,GAAG,CAAC,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAgB;YAC3B,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,qBAAqB;YAC/B,OAAO,EAAE,SAAS,IAAI,yBAAyB;YAC/C,OAAO,EAAE,IAAI;SACd,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,YAAY,CAAC,GAAG,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAgB;IACxC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC;IAClF,OAAO,YAAY,CAAC,GAAG,OAAO,CAAC,CAAC;AAClC,CAAC"}
@@ -1,3 +1,7 @@
1
- import { Tool } from './types.js';
2
- export declare function loadUserTools(cwd: string): Promise<Tool[]>;
1
+ import { Tool, ToolLoadFailure } from './types.js';
2
+ export interface LoadUserToolsResult {
3
+ tools: Tool[];
4
+ failures: ToolLoadFailure[];
5
+ }
6
+ export declare function loadUserTools(cwd: string): Promise<LoadUserToolsResult>;
3
7
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAwBlC,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAShE"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAwCnD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAY7E"}
@@ -2,38 +2,52 @@ import { existsSync, readdirSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { AG_DIR } from './constants.js';
5
+ import { scanTool } from './guardrails.js';
5
6
  async function loadToolsFromDir(dir) {
6
7
  if (!existsSync(dir))
7
- return [];
8
+ return { tools: [], failures: [] };
8
9
  const files = readdirSync(dir).filter(f => f.endsWith('.mjs'));
9
10
  const tools = [];
11
+ const failures = [];
10
12
  for (const file of files) {
11
13
  try {
12
14
  const mod = await import(pathToFileURL(join(dir, file)).href);
13
15
  const tool = mod.default;
14
16
  if (tool?.type === 'function' && tool?.function?.name && typeof tool?.execute === 'function') {
17
+ if (tool.permissionKey && typeof tool.permissionKey?.qualifier !== 'string') {
18
+ tool.permissionKey = undefined;
19
+ }
20
+ const scan = scanTool(tool);
21
+ if (!scan.ok) {
22
+ const reasons = scan.findings.filter(f => f.severity === 'block').map(f => f.message).join('; ');
23
+ failures.push({ file, name: tool.function.name, reason: reasons });
24
+ continue;
25
+ }
15
26
  tools.push(tool);
16
27
  }
17
28
  else {
18
- process.stderr.write(`Warning: ${file} skipped (invalid tool format)\n`);
29
+ failures.push({ file, reason: 'invalid tool format' });
19
30
  }
20
31
  }
21
32
  catch (e) {
22
33
  const msg = e instanceof Error ? e.message : String(e);
23
- process.stderr.write(`Warning: ${file} failed to load: ${msg}\n`);
34
+ failures.push({ file, reason: msg });
24
35
  }
25
36
  }
26
- return tools;
37
+ return { tools, failures };
27
38
  }
28
39
  export async function loadUserTools(cwd) {
29
- const globalTools = await loadToolsFromDir(join(AG_DIR, 'tools'));
30
- const localTools = await loadToolsFromDir(join(cwd, '.ag', 'tools'));
40
+ const global = await loadToolsFromDir(join(AG_DIR, 'tools'));
41
+ const local = await loadToolsFromDir(join(cwd, '.ag', 'tools'));
31
42
  // Local tools override global if same name
32
43
  const byName = new Map();
33
- for (const t of globalTools)
44
+ for (const t of global.tools)
34
45
  byName.set(t.function.name, t);
35
- for (const t of localTools)
46
+ for (const t of local.tools)
36
47
  byName.set(t.function.name, t);
37
- return Array.from(byName.values());
48
+ return {
49
+ tools: Array.from(byName.values()),
50
+ failures: [...global.failures, ...local.failures],
51
+ };
38
52
  }
39
53
  //# sourceMappingURL=loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,KAAK,UAAU,gBAAgB,CAAC,GAAW;IACzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;YACzB,IAAI,IAAI,EAAE,IAAI,KAAK,UAAU,IAAI,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,OAAO,IAAI,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC7F,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,kCAAkC,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,oBAAoB,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,WAAW,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAErE,2CAA2C;IAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAgB,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,WAAW;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5D,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { PermissionKey } from './types.js';
2
+ export type PermissionEffect = 'allow' | 'deny';
3
+ export type PermissionScope = 'session' | 'project' | 'global';
4
+ export interface PermissionRule {
5
+ pattern: string;
6
+ effect: PermissionEffect;
7
+ }
8
+ export interface PermissionFile {
9
+ allow?: string[];
10
+ deny?: string[];
11
+ }
12
+ export interface ParsedPattern {
13
+ tool: string;
14
+ qualifier: string;
15
+ glob: string | null;
16
+ }
17
+ /** Hand-rolled glob matcher — supports *, **, ? with no dependencies */
18
+ export declare function globMatch(pattern: string, value: string): boolean;
19
+ /**
20
+ * Parse a pattern string like "bash(npm:*)" into structured form.
21
+ * Supports: "Tool(qualifier:glob)", "Tool(qualifier)", "Tool(*)", "*"
22
+ */
23
+ export declare function parsePattern(str: string): ParsedPattern;
24
+ interface MatchKey {
25
+ qualifier: string;
26
+ value: string;
27
+ }
28
+ /** Extract qualifier + value from a tool call for pattern matching */
29
+ export declare function extractMatchKey(toolName: string, args: Record<string, unknown>, permissionKey?: PermissionKey): MatchKey;
30
+ /** Generate a reasonable permission pattern from a concrete tool call */
31
+ export declare function inferPattern(toolName: string, args: Record<string, unknown>, permissionKey?: PermissionKey): string;
32
+ export declare class PermissionManager {
33
+ private readonly cwd;
34
+ private readonly projectPath;
35
+ private readonly globalPath;
36
+ private sessionRules;
37
+ private projectRules;
38
+ private globalRules;
39
+ constructor(cwd: string);
40
+ /** Reload project + global rules from disk */
41
+ reload(): void;
42
+ /** Check whether a tool call is allowed, denied, or needs prompting */
43
+ check(toolName: string, args: Record<string, unknown>, permissionKey?: PermissionKey): 'allow' | 'deny' | 'ask';
44
+ /** Add a permission rule to a scope */
45
+ addRule(rule: PermissionRule, scope: PermissionScope): void;
46
+ /** Remove a rule by pattern from a scope */
47
+ removeRule(pattern: string, scope: PermissionScope): boolean;
48
+ /** Get rules, optionally filtered by scope */
49
+ getRules(scope?: PermissionScope): Array<PermissionRule & {
50
+ scope: PermissionScope;
51
+ }>;
52
+ /** Persist rules to disk */
53
+ save(scope: 'project' | 'global'): void;
54
+ /** Clear rules for a scope */
55
+ clear(scope: PermissionScope): void;
56
+ private rulesForScope;
57
+ private loadFile;
58
+ }
59
+ export {};
60
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAID,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAejE;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAsBvD;AAID,UAAU,QAAQ;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,CAAC,EAAE,aAAa,GAC5B,QAAQ,CAmCV;AAID,yEAAyE;AACzE,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,CAAC,EAAE,aAAa,GAC5B,MAAM,CAwCR;AAgBD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,WAAW,CAAwB;gBAE/B,GAAG,EAAE,MAAM;IAOvB,8CAA8C;IAC9C,MAAM,IAAI,IAAI;IAKd,uEAAuE;IACvE,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,GAAG,MAAM,GAAG,KAAK;IAqB3B,uCAAuC;IACvC,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAQ3D,4CAA4C;IAC5C,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO;IAO5D,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,KAAK,CAAC,cAAc,GAAG;QAAE,KAAK,EAAE,eAAe,CAAA;KAAE,CAAC;IAWrF,4BAA4B;IAC5B,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,IAAI;IAYvC,8BAA8B;IAC9B,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAQnC,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,QAAQ;CAWjB"}
@@ -0,0 +1,252 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { AG_DIR } from './constants.js';
4
+ // ── Glob Matching ───────────────────────────────────────────────────────────
5
+ /** Hand-rolled glob matcher — supports *, **, ? with no dependencies */
6
+ export function globMatch(pattern, value) {
7
+ if (pattern === '*')
8
+ return true;
9
+ // Convert glob to regex:
10
+ // 1. Escape regex-special chars (except our glob chars)
11
+ // 2. Replace ** → any sequence (including /)
12
+ // 3. Replace * → any sequence (excluding /)
13
+ // 4. Replace ? → single char
14
+ const SENTINEL = '\0GLOBSTAR\0';
15
+ const re = pattern
16
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape regex specials
17
+ .replace(/\*\*/g, SENTINEL) // protect **
18
+ .replace(/\*/g, '[^/]*') // * = non-slash seq
19
+ .replace(new RegExp(SENTINEL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '.*') // ** = any seq
20
+ .replace(/\?/g, '.'); // ? = single char
21
+ return new RegExp(`^${re}$`).test(value);
22
+ }
23
+ // ── Pattern Parsing ─────────────────────────────────────────────────────────
24
+ /**
25
+ * Parse a pattern string like "bash(npm:*)" into structured form.
26
+ * Supports: "Tool(qualifier:glob)", "Tool(qualifier)", "Tool(*)", "*"
27
+ */
28
+ export function parsePattern(str) {
29
+ const s = str.trim();
30
+ if (s === '*')
31
+ return { tool: '*', qualifier: '*', glob: null };
32
+ const parenIdx = s.indexOf('(');
33
+ if (parenIdx === -1) {
34
+ // Bare tool name: "bash" → bash(*)
35
+ return { tool: s.toLowerCase(), qualifier: '*', glob: null };
36
+ }
37
+ const tool = s.slice(0, parenIdx).toLowerCase();
38
+ const inner = s.slice(parenIdx + 1, s.endsWith(')') ? s.length - 1 : s.length);
39
+ const colonIdx = inner.indexOf(':');
40
+ if (colonIdx === -1) {
41
+ // No glob: "git(commit)" or "bash(*)"
42
+ return { tool, qualifier: inner || '*', glob: null };
43
+ }
44
+ const qualifier = inner.slice(0, colonIdx) || '*';
45
+ const glob = inner.slice(colonIdx + 1) || '*';
46
+ return { tool, qualifier, glob };
47
+ }
48
+ /** Extract qualifier + value from a tool call for pattern matching */
49
+ export function extractMatchKey(toolName, args, permissionKey) {
50
+ const name = toolName.toLowerCase();
51
+ // Built-in tools have hardcoded extraction
52
+ switch (name) {
53
+ case 'bash': {
54
+ const cmd = String(args.command || '');
55
+ const firstWord = cmd.trimStart().split(/\s+/)[0] || '';
56
+ return { qualifier: firstWord, value: cmd };
57
+ }
58
+ case 'file':
59
+ return { qualifier: String(args.action || '*'), value: String(args.path || '') };
60
+ case 'git':
61
+ return { qualifier: String(args.action || '*'), value: '' };
62
+ case 'web': {
63
+ const action = String(args.action || '*');
64
+ if (action === 'fetch' && args.url) {
65
+ try {
66
+ const hostname = new URL(String(args.url)).hostname;
67
+ return { qualifier: action, value: hostname };
68
+ }
69
+ catch { /* fall through */ }
70
+ }
71
+ return { qualifier: action, value: String(args.url || args.query || '') };
72
+ }
73
+ }
74
+ // Custom tools: use permissionKey if provided
75
+ if (permissionKey?.qualifier) {
76
+ const q = String(args[permissionKey.qualifier] ?? '*');
77
+ const v = permissionKey.value ? String(args[permissionKey.value] ?? '') : '';
78
+ return { qualifier: q, value: v };
79
+ }
80
+ // No permissionKey: opaque tool
81
+ return { qualifier: '*', value: '' };
82
+ }
83
+ // ── Pattern Inference ───────────────────────────────────────────────────────
84
+ /** Generate a reasonable permission pattern from a concrete tool call */
85
+ export function inferPattern(toolName, args, permissionKey) {
86
+ const name = toolName.toLowerCase();
87
+ switch (name) {
88
+ case 'bash': {
89
+ const cmd = String(args.command || '');
90
+ const firstWord = cmd.trimStart().split(/\s+/)[0] || '*';
91
+ return `bash(${firstWord}:*)`;
92
+ }
93
+ case 'file': {
94
+ const action = String(args.action || '*');
95
+ const path = String(args.path || '');
96
+ const firstDir = path.split('/')[0];
97
+ const glob = firstDir && firstDir !== path ? `${firstDir}/**` : '*';
98
+ return `file(${action}:${glob})`;
99
+ }
100
+ case 'git':
101
+ return `git(${String(args.action || '*')})`;
102
+ case 'web': {
103
+ const action = String(args.action || '*');
104
+ if (action === 'fetch' && args.url) {
105
+ try {
106
+ const hostname = new URL(String(args.url)).hostname;
107
+ return `web(fetch:*${hostname}*)`;
108
+ }
109
+ catch { /* fall through */ }
110
+ }
111
+ return `web(${action}:*)`;
112
+ }
113
+ }
114
+ // Custom tools
115
+ if (permissionKey?.qualifier) {
116
+ const q = String(args[permissionKey.qualifier] ?? '*');
117
+ if (permissionKey.value) {
118
+ return `${name}(${q}:*)`;
119
+ }
120
+ return `${name}(${q})`;
121
+ }
122
+ return `${name}(*)`;
123
+ }
124
+ // ── Pattern Matching ────────────────────────────────────────────────────────
125
+ function matchesRule(pattern, toolName, key) {
126
+ // Tool name check
127
+ if (pattern.tool !== '*' && pattern.tool !== toolName.toLowerCase())
128
+ return false;
129
+ // Qualifier check
130
+ if (pattern.qualifier !== '*' && !globMatch(pattern.qualifier, key.qualifier))
131
+ return false;
132
+ // Glob check (if pattern has one)
133
+ if (pattern.glob !== null && key.value && !globMatch(pattern.glob, key.value))
134
+ return false;
135
+ return true;
136
+ }
137
+ // ── PermissionManager ───────────────────────────────────────────────────────
138
+ export class PermissionManager {
139
+ cwd;
140
+ projectPath;
141
+ globalPath;
142
+ sessionRules = [];
143
+ projectRules = [];
144
+ globalRules = [];
145
+ constructor(cwd) {
146
+ this.cwd = cwd;
147
+ this.projectPath = join(cwd, '.ag', 'permissions.json');
148
+ this.globalPath = join(AG_DIR, 'permissions.json');
149
+ this.reload();
150
+ }
151
+ /** Reload project + global rules from disk */
152
+ reload() {
153
+ this.projectRules = this.loadFile(this.projectPath);
154
+ this.globalRules = this.loadFile(this.globalPath);
155
+ }
156
+ /** Check whether a tool call is allowed, denied, or needs prompting */
157
+ check(toolName, args, permissionKey) {
158
+ const key = extractMatchKey(toolName, args, permissionKey);
159
+ const allRules = [...this.sessionRules, ...this.projectRules, ...this.globalRules];
160
+ // Deny wins: check deny rules first across all scopes
161
+ for (const rule of allRules) {
162
+ if (rule.effect === 'deny' && matchesRule(parsePattern(rule.pattern), toolName, key)) {
163
+ return 'deny';
164
+ }
165
+ }
166
+ // Then check allow rules
167
+ for (const rule of allRules) {
168
+ if (rule.effect === 'allow' && matchesRule(parsePattern(rule.pattern), toolName, key)) {
169
+ return 'allow';
170
+ }
171
+ }
172
+ return 'ask';
173
+ }
174
+ /** Add a permission rule to a scope */
175
+ addRule(rule, scope) {
176
+ const list = this.rulesForScope(scope);
177
+ // Avoid duplicates
178
+ if (!list.some(r => r.pattern === rule.pattern && r.effect === rule.effect)) {
179
+ list.push(rule);
180
+ }
181
+ }
182
+ /** Remove a rule by pattern from a scope */
183
+ removeRule(pattern, scope) {
184
+ const list = this.rulesForScope(scope);
185
+ const idx = list.findIndex(r => r.pattern === pattern);
186
+ if (idx !== -1) {
187
+ list.splice(idx, 1);
188
+ return true;
189
+ }
190
+ return false;
191
+ }
192
+ /** Get rules, optionally filtered by scope */
193
+ getRules(scope) {
194
+ const result = [];
195
+ const add = (rules, s) => {
196
+ for (const r of rules)
197
+ result.push({ ...r, scope: s });
198
+ };
199
+ if (!scope || scope === 'session')
200
+ add(this.sessionRules, 'session');
201
+ if (!scope || scope === 'project')
202
+ add(this.projectRules, 'project');
203
+ if (!scope || scope === 'global')
204
+ add(this.globalRules, 'global');
205
+ return result;
206
+ }
207
+ /** Persist rules to disk */
208
+ save(scope) {
209
+ const path = scope === 'project' ? this.projectPath : this.globalPath;
210
+ const rules = scope === 'project' ? this.projectRules : this.globalRules;
211
+ const dir = join(path, '..');
212
+ if (!existsSync(dir))
213
+ mkdirSync(dir, { recursive: true });
214
+ const data = {
215
+ allow: rules.filter(r => r.effect === 'allow').map(r => r.pattern),
216
+ deny: rules.filter(r => r.effect === 'deny').map(r => r.pattern),
217
+ };
218
+ writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
219
+ }
220
+ /** Clear rules for a scope */
221
+ clear(scope) {
222
+ if (scope === 'session')
223
+ this.sessionRules = [];
224
+ else if (scope === 'project')
225
+ this.projectRules = [];
226
+ else if (scope === 'global')
227
+ this.globalRules = [];
228
+ }
229
+ // ── Private helpers ─────────────────────────────────────────────────────
230
+ rulesForScope(scope) {
231
+ switch (scope) {
232
+ case 'session': return this.sessionRules;
233
+ case 'project': return this.projectRules;
234
+ case 'global': return this.globalRules;
235
+ }
236
+ }
237
+ loadFile(path) {
238
+ try {
239
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
240
+ const rules = [];
241
+ for (const p of raw.allow ?? [])
242
+ rules.push({ pattern: p, effect: 'allow' });
243
+ for (const p of raw.deny ?? [])
244
+ rules.push({ pattern: p, effect: 'deny' });
245
+ return rules;
246
+ }
247
+ catch {
248
+ return [];
249
+ }
250
+ }
251
+ }
252
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAK5E;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ClE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAK5E;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2DlE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD"}
@@ -1,6 +1,7 @@
1
1
  import { writeFileSync, mkdirSync, existsSync, rmSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
+ import { scanContent } from './guardrails.js';
4
5
  const SKILLS_DIR = join(homedir(), '.ag', 'skills');
5
6
  const SEARCH_API = 'https://skills.sh/api/search';
6
7
  function githubHeaders() {
@@ -84,6 +85,21 @@ export async function installSkill(source) {
84
85
  continue;
85
86
  }
86
87
  const buffer = Buffer.from(await res.arrayBuffer());
88
+ if (relativePath.endsWith('.md') || relativePath.endsWith('.mjs')) {
89
+ const text = buffer.toString('utf-8');
90
+ const scan = scanContent(text, `installed file "${relativePath}" from ${repo}`);
91
+ if (!scan.ok) {
92
+ const reasons = scan.findings.filter(f => f.severity === 'block').map(f => f.message).join('; ');
93
+ errors.push(`Blocked ${relativePath}: ${reasons}`);
94
+ if (relativePath === 'SKILL.md') {
95
+ // Core skill file is compromised — abort entire installation
96
+ if (existsSync(skillDir))
97
+ rmSync(skillDir, { recursive: true });
98
+ throw new Error(`Skill "${skillName}" blocked by guardrails: ${reasons}`);
99
+ }
100
+ continue;
101
+ }
102
+ }
87
103
  writeFileSync(destPath, buffer);
88
104
  }
89
105
  catch (err) {