aiden-runtime 3.16.2 → 3.18.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.
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Permission System v1
4
+ // Reads workspace/permissions.yaml and exposes checkShell(),
5
+ // checkFileWrite(), checkFileRead(), checkBrowserDomain().
6
+ //
7
+ // Design:
8
+ // verdict 'allow' — explicitly permitted, skip inner gates
9
+ // verdict 'deny' — blocked, return error immediately
10
+ // verdict 'ask' — needs user approval before running
11
+ // verdict 'defer' — no opinion, let existing hardcoded gate decide
12
+ //
13
+ // Changes to permissions.yaml take effect immediately (file-watcher).
14
+ // ============================================================
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ var __importDefault = (this && this.__importDefault) || function (mod) {
49
+ return (mod && mod.__esModule) ? mod : { "default": mod };
50
+ };
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.permissionSystem = void 0;
53
+ const fs = __importStar(require("fs"));
54
+ const path = __importStar(require("path"));
55
+ const js_yaml_1 = __importDefault(require("js-yaml"));
56
+ const minimatch_1 = __importDefault(require("minimatch"));
57
+ // ── PermissionSystem ──────────────────────────────────────────
58
+ class PermissionSystem {
59
+ constructor() {
60
+ this.configPath = path.join(process.cwd(), 'workspace', 'permissions.yaml');
61
+ this.config = this.defaults();
62
+ this.load();
63
+ this.startWatcher();
64
+ }
65
+ // ── Defaults ────────────────────────────────────────────────
66
+ defaults() {
67
+ return {
68
+ version: 1,
69
+ mode: 'ask',
70
+ shell: {
71
+ deny: [],
72
+ allow: [],
73
+ },
74
+ filesystem: {
75
+ deny_read: [],
76
+ deny_write: [],
77
+ allow_write: [],
78
+ },
79
+ browser: {
80
+ deny_domains: [],
81
+ require_approval: false,
82
+ },
83
+ audit: {
84
+ enabled: true,
85
+ log_file: 'workspace/audit.log',
86
+ log_level: 'deny',
87
+ },
88
+ };
89
+ }
90
+ // ── Loader ──────────────────────────────────────────────────
91
+ load() {
92
+ try {
93
+ if (!fs.existsSync(this.configPath))
94
+ return;
95
+ const raw = fs.readFileSync(this.configPath, 'utf-8');
96
+ const parsed = js_yaml_1.default.load(raw);
97
+ const d = this.defaults();
98
+ this.config = {
99
+ ...d,
100
+ ...parsed,
101
+ shell: { ...d.shell, ...(parsed.shell ?? {}) },
102
+ filesystem: { ...d.filesystem, ...(parsed.filesystem ?? {}) },
103
+ browser: { ...d.browser, ...(parsed.browser ?? {}) },
104
+ audit: { ...d.audit, ...(parsed.audit ?? {}) },
105
+ };
106
+ }
107
+ catch (e) {
108
+ console.warn('[Permissions] Failed to load permissions.yaml:', e.message);
109
+ }
110
+ }
111
+ reload() {
112
+ this.load();
113
+ console.log('[Permissions] Reloaded permissions.yaml');
114
+ }
115
+ // ── File watcher ────────────────────────────────────────────
116
+ startWatcher() {
117
+ try {
118
+ if (!fs.existsSync(path.dirname(this.configPath)))
119
+ return;
120
+ // Watch the workspace dir so we catch the file being created too
121
+ const watchDir = path.dirname(this.configPath);
122
+ this.watcher = fs.watch(watchDir, { persistent: false }, (event, filename) => {
123
+ if (filename && filename.includes('permissions')) {
124
+ this.load();
125
+ console.log('[Permissions] Auto-reloaded permissions.yaml');
126
+ }
127
+ });
128
+ }
129
+ catch { /* workspace may not exist yet */ }
130
+ }
131
+ // ── Glob helper ─────────────────────────────────────────────
132
+ matches(value, patterns) {
133
+ const normalized = value.replace(/\\/g, '/');
134
+ return patterns.some(p => {
135
+ // Simple startsWith patterns (no glob chars) — substring match for shell cmds
136
+ if (!p.includes('*') && !p.includes('?') && !p.includes('[')) {
137
+ return normalized.toLowerCase().startsWith(p.toLowerCase().replace(/\s*$/, ''));
138
+ }
139
+ return (0, minimatch_1.default)(normalized, p, { dot: true, matchBase: true, nocase: true });
140
+ });
141
+ }
142
+ // ── Audit ────────────────────────────────────────────────────
143
+ audit(action, verdict, reason) {
144
+ if (!this.config.audit.enabled)
145
+ return;
146
+ const level = this.config.audit.log_level;
147
+ if (level === 'deny' && verdict !== 'deny')
148
+ return;
149
+ if (level === 'ask' && verdict === 'allow')
150
+ return;
151
+ try {
152
+ const line = `${new Date().toISOString()} [${verdict.toUpperCase()}] ${action} — ${reason}\n`;
153
+ const logPath = path.isAbsolute(this.config.audit.log_file)
154
+ ? this.config.audit.log_file
155
+ : path.join(process.cwd(), this.config.audit.log_file);
156
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
157
+ fs.appendFileSync(logPath, line, 'utf-8');
158
+ }
159
+ catch { /* audit failures are silent */ }
160
+ }
161
+ // ── checkShell ───────────────────────────────────────────────
162
+ checkShell(cmd) {
163
+ const trimmed = cmd.trim();
164
+ // 1. Hard deny from config (highest priority)
165
+ if (this.matches(trimmed, this.config.shell.deny)) {
166
+ this.audit(`shell: ${trimmed.slice(0, 120)}`, 'deny', 'matches shell.deny list');
167
+ return { verdict: 'deny', reason: 'Blocked by permissions.yaml shell deny list.' };
168
+ }
169
+ // 2. Explicit allow from config
170
+ if (this.matches(trimmed, this.config.shell.allow)) {
171
+ this.audit(`shell: ${trimmed.slice(0, 120)}`, 'allow', 'matches shell.allow list');
172
+ return { verdict: 'allow' };
173
+ }
174
+ // 3. Mode-based fallthrough
175
+ switch (this.config.mode) {
176
+ case 'allow':
177
+ return { verdict: 'allow' };
178
+ case 'strict':
179
+ this.audit(`shell: ${trimmed.slice(0, 120)}`, 'deny', 'strict mode, not in allow list');
180
+ return { verdict: 'deny', reason: 'Strict mode: command not in shell.allow list.' };
181
+ case 'ask':
182
+ default:
183
+ // Defer to the existing hardcoded SHELL_ALLOWLIST/DENIED_COMMANDS gate
184
+ return { verdict: 'defer' };
185
+ }
186
+ }
187
+ // ── checkFileWrite ───────────────────────────────────────────
188
+ checkFileWrite(filePath) {
189
+ const norm = filePath.replace(/\\/g, '/');
190
+ if (this.matches(norm, this.config.filesystem.deny_write)) {
191
+ this.audit(`file_write: ${norm}`, 'deny', 'matches filesystem.deny_write');
192
+ return { verdict: 'deny', reason: 'Blocked by permissions.yaml filesystem deny_write.' };
193
+ }
194
+ // allow_write is an explicit permit (relevant in strict mode)
195
+ if (this.matches(norm, this.config.filesystem.allow_write)) {
196
+ return { verdict: 'allow' };
197
+ }
198
+ switch (this.config.mode) {
199
+ case 'allow':
200
+ return { verdict: 'allow' };
201
+ case 'strict':
202
+ this.audit(`file_write: ${norm}`, 'deny', 'strict mode, not in allow_write');
203
+ return { verdict: 'deny', reason: 'Strict mode: path not in filesystem.allow_write.' };
204
+ default:
205
+ // ask/unknown — write is allowed by default; existing isPathDenied() covers secrets
206
+ return { verdict: 'defer' };
207
+ }
208
+ }
209
+ // ── checkFileRead ────────────────────────────────────────────
210
+ checkFileRead(filePath) {
211
+ const norm = filePath.replace(/\\/g, '/');
212
+ if (this.matches(norm, this.config.filesystem.deny_read)) {
213
+ this.audit(`file_read: ${norm}`, 'deny', 'matches filesystem.deny_read');
214
+ return { verdict: 'deny', reason: 'Blocked by permissions.yaml filesystem deny_read.' };
215
+ }
216
+ return { verdict: 'defer' };
217
+ }
218
+ // ── checkBrowserDomain ───────────────────────────────────────
219
+ checkBrowserDomain(url) {
220
+ try {
221
+ const hostname = new URL(url).hostname;
222
+ if (this.matches(hostname, this.config.browser.deny_domains)) {
223
+ this.audit(`browser: ${hostname}`, 'deny', 'matches browser.deny_domains');
224
+ return { verdict: 'deny', reason: `Domain blocked by permissions.yaml: ${hostname}` };
225
+ }
226
+ if (this.config.browser.require_approval) {
227
+ return { verdict: 'ask', reason: 'browser.require_approval=true in permissions.yaml' };
228
+ }
229
+ }
230
+ catch { /* invalid URL — pass through */ }
231
+ return { verdict: 'defer' };
232
+ }
233
+ // ── Accessors ────────────────────────────────────────────────
234
+ getConfig() { return structuredClone(this.config); }
235
+ getMode() { return this.config.mode; }
236
+ getConfigPath() { return this.configPath; }
237
+ }
238
+ // ── Singleton ─────────────────────────────────────────────────
239
+ exports.permissionSystem = new PermissionSystem();
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Plugin Loader (unified flat .js format)
4
+ // Loads workspace/plugins/*.js (files NOT starting with _)
5
+ //
6
+ // This is the single plugin system for Aiden v3.17+.
7
+ // core/pluginSystem.ts is deprecated — all new plugins use
8
+ // the flat format with init(ctx).
9
+ // ============================================================
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.loadedFlatPlugins = exports.pluginHooks = void 0;
45
+ exports.loadPlugins = loadPlugins;
46
+ exports.reloadPlugins = reloadPlugins;
47
+ exports.listFlatPlugins = listFlatPlugins;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const toolRegistry_1 = require("./toolRegistry");
51
+ const hooks_1 = require("./hooks");
52
+ exports.pluginHooks = {
53
+ preTool: [],
54
+ postTool: [],
55
+ onSessionStart: [],
56
+ onSessionEnd: [],
57
+ };
58
+ exports.loadedFlatPlugins = [];
59
+ // ── Plugin context (passed to init / onLoad) ──────────────────
60
+ function makeContext(pluginName) {
61
+ return {
62
+ registerTool(def) {
63
+ (0, toolRegistry_1.registerExternalTool)(def.name, def.execute, pluginName);
64
+ },
65
+ // Lifecycle hooks that fire around every tool call and session boundary
66
+ hooks: {
67
+ preTool(fn) { exports.pluginHooks.preTool.push(fn); },
68
+ postTool(fn) { exports.pluginHooks.postTool.push(fn); },
69
+ onSessionStart(fn) { exports.pluginHooks.onSessionStart.push(fn); },
70
+ onSessionEnd(fn) { exports.pluginHooks.onSessionEnd.push(fn); },
71
+ },
72
+ // Core lifecycle events: 'pre_compact' | 'session_stop' | 'after_tool_call'
73
+ registerHook(event, handler) {
74
+ (0, hooks_1.registerExternalHook)(event, handler, pluginName);
75
+ },
76
+ log(...args) {
77
+ console.log(`[Plugin:${pluginName}]`, ...args);
78
+ },
79
+ };
80
+ }
81
+ // ── Loader ────────────────────────────────────────────────────
82
+ async function loadPlugins(pluginDir) {
83
+ if (!fs.existsSync(pluginDir)) {
84
+ fs.mkdirSync(pluginDir, { recursive: true });
85
+ return;
86
+ }
87
+ const entries = fs.readdirSync(pluginDir).filter(f => {
88
+ if (!f.endsWith('.js'))
89
+ return false; // .js only
90
+ if (f.startsWith('_'))
91
+ return false; // skip _example.js etc.
92
+ return true;
93
+ });
94
+ for (const file of entries) {
95
+ const fullPath = path.resolve(pluginDir, file);
96
+ try {
97
+ // Clear require cache so reload works
98
+ delete require.cache[require.resolve(fullPath)];
99
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
100
+ const def = require(fullPath);
101
+ const name = def.name || path.basename(file, '.js');
102
+ const version = def.version || '0.0.0';
103
+ // Support both flat format (init) and legacy subdirectory format (onLoad)
104
+ const initFn = def.init ?? def.onLoad ?? (def.default?.init) ?? (def.default?.onLoad);
105
+ if (typeof initFn !== 'function') {
106
+ console.warn(`[PluginLoader] ${file}: no init() — skipping`);
107
+ continue;
108
+ }
109
+ const ctx = makeContext(name);
110
+ const dispose = await initFn.call(def.default ?? def, ctx);
111
+ exports.loadedFlatPlugins.push({
112
+ name,
113
+ version,
114
+ description: def.description || def.default?.description || '',
115
+ author: def.author || def.default?.author || '',
116
+ file: fullPath,
117
+ loadedAt: Date.now(),
118
+ active: true,
119
+ dispose: typeof dispose === 'function' ? dispose
120
+ : (def.dispose ?? def.default?.onUnload ?? def.default?.dispose),
121
+ });
122
+ console.log(`[PluginLoader] Loaded: ${name} v${version} (${file})`);
123
+ }
124
+ catch (err) {
125
+ console.error(`[PluginLoader] Failed to load ${file}:`, err.message);
126
+ }
127
+ }
128
+ }
129
+ // ── Reload — dispose old plugins then reload all flat plugins ─
130
+ async function reloadPlugins(pluginDir) {
131
+ // Dispose in reverse order
132
+ for (let i = exports.loadedFlatPlugins.length - 1; i >= 0; i--) {
133
+ const p = exports.loadedFlatPlugins[i];
134
+ if (p.dispose) {
135
+ try {
136
+ await p.dispose();
137
+ }
138
+ catch { /* ignore dispose errors */ }
139
+ }
140
+ p.active = false;
141
+ }
142
+ exports.loadedFlatPlugins.length = 0;
143
+ // Clear all hooks contributed by plugins
144
+ exports.pluginHooks.preTool.length = 0;
145
+ exports.pluginHooks.postTool.length = 0;
146
+ exports.pluginHooks.onSessionStart.length = 0;
147
+ exports.pluginHooks.onSessionEnd.length = 0;
148
+ await loadPlugins(pluginDir);
149
+ }
150
+ // ── Status ────────────────────────────────────────────────────
151
+ function listFlatPlugins() {
152
+ return exports.loadedFlatPlugins.map(p => ({
153
+ name: p.name,
154
+ version: p.version,
155
+ description: p.description,
156
+ author: p.author,
157
+ file: path.basename(p.file),
158
+ loadedAt: p.loadedAt,
159
+ active: p.active,
160
+ }));
161
+ }
@@ -44,45 +44,27 @@ function getSkillCacheStats() {
44
44
  }
45
45
  // ── Skill injection guard ─────────────────────────────────────
46
46
  const SKILL_INJECTION_PATTERNS = [
47
- // ── Original patterns ──────────────────────────────────────
47
+ // ── Prompt hijacking ───────────────────────────────────────
48
48
  /ignore\s+(all\s+)?(previous|above|prior)/i,
49
49
  /disregard\s+(all\s+)?(previous|above)/i,
50
50
  /you\s+are\s+now\s+/i,
51
51
  /new\s+instructions\s*:/i,
52
- /override\s+system/i,
53
- /curl\s+.*\|\s*bash/i,
52
+ /override\s+(your\s+)?(system|instructions|rules)/i,
53
+ /curl\s+[^\n]*\|\s*(bash|sh)\b/i,
54
54
  /ANTHROPIC_BASE_URL/i,
55
55
  /\]\s*\(\s*javascript:/i,
56
56
  // ── Role hijacking ─────────────────────────────────────────
57
- /act\s+as\s+(if\s+you\s+are|a|an)\s/i,
58
57
  /pretend\s+(to\s+be|you\s+are)/i,
59
58
  /roleplay\s+as/i,
60
59
  /you\s+must\s+obey/i,
61
60
  /your\s+new\s+(role|instruction|directive)/i,
62
- // ── Indirect injection (model-control tokens) ──────────────
63
- /\[SYSTEM\]/i,
61
+ // ── Model-control tokens (LLM prompt format injection) ─────
64
62
  /\[INST\]/i,
65
63
  /<\|im_start\|>/i,
66
64
  /<\|system\|>/i,
67
65
  /<<\s*SYS\s*>>/i,
68
- /###\s*instruction/i,
69
- // ── Encoded / obfuscated payloads ──────────────────────────
70
- /base64\s*decode/i,
71
- /eval\s*\(/i,
72
- /exec\s*\(/i,
73
- /import\s+os/i,
74
- /subprocess/i,
75
- /\\x[0-9a-f]{2}/i,
76
- // ── Data exfiltration ──────────────────────────────────────
77
- /send\s+(to|via)\s+(http|email|webhook|api)/i,
78
- /upload\s+(to|file|data)/i,
79
- /curl\s+.*-d\s/i,
80
- /fetch\s*\(\s*['"]http/i,
81
- // ── Privilege escalation ───────────────────────────────────
82
- /admin\s*(mode|access|privilege)/i,
83
- /sudo\s/i,
84
- /run\s+as\s+administrator/i,
85
- /elevation\s+prompt/i,
66
+ // ── Obfuscated payloads ────────────────────────────────────
67
+ /\\x[0-9a-f]{2}\\x[0-9a-f]{2}/i, // hex sequences (2+ consecutive)
86
68
  ];
87
69
  function sanitizeSkill(content, filename) {
88
70
  for (const pattern of SKILL_INJECTION_PATTERNS) {