micro-models-agent 0.39.0 → 0.40.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.
Files changed (96) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +116 -3
  3. package/dist/cli/main.js +35 -8
  4. package/dist/cli/repl-commands.js +633 -0
  5. package/dist/cli/repl.js +110 -611
  6. package/dist/cli/setup.js +32 -12
  7. package/dist/config/config.js +46 -30
  8. package/dist/config/defaults.js +10 -1
  9. package/dist/config/security.js +15 -8
  10. package/dist/core/agent-moe.js +24 -12
  11. package/dist/core/agent.js +281 -47
  12. package/dist/core/bootstrap.js +52 -36
  13. package/dist/core/session-logger.js +35 -2
  14. package/dist/core/workspace.js +76 -0
  15. package/dist/i18n/en.json +79 -15
  16. package/dist/i18n/index.js +12 -9
  17. package/dist/i18n/ru.json +79 -15
  18. package/dist/index.js +13 -13
  19. package/dist/llm/openai-compat.js +39 -10
  20. package/dist/logger/app-logger.js +83 -16
  21. package/dist/logger/file-log.js +151 -0
  22. package/dist/main.js +537 -284
  23. package/dist/modules/browser/bridge-server.mjs +113 -105
  24. package/dist/modules/browser/session.js +108 -60
  25. package/dist/modules/certification/cli.js +176 -0
  26. package/dist/modules/certification/fact-checker.js +84 -0
  27. package/dist/modules/certification/loader.js +111 -0
  28. package/dist/modules/certification/manifest.js +50 -0
  29. package/dist/modules/certification/runner.js +162 -0
  30. package/dist/modules/certification/scenarios.js +124 -0
  31. package/dist/modules/certification/types.js +1 -0
  32. package/dist/modules/context/manager.js +119 -10
  33. package/dist/modules/execution/auditor.js +33 -39
  34. package/dist/modules/execution/index.js +8 -6
  35. package/dist/modules/execution/module.js +474 -32
  36. package/dist/modules/execution/moe-executor.js +97 -40
  37. package/dist/modules/execution/plan-coverage.js +68 -0
  38. package/dist/modules/execution/plan-persister.js +46 -0
  39. package/dist/modules/execution/plan-store.js +159 -0
  40. package/dist/modules/execution/planner.js +63 -13
  41. package/dist/modules/execution/stuck-detector.js +252 -39
  42. package/dist/modules/execution/tracker.js +21 -7
  43. package/dist/modules/execution/verifier.js +46 -17
  44. package/dist/modules/hallucination/confidence.js +7 -2
  45. package/dist/modules/hallucination/consistency.js +8 -42
  46. package/dist/modules/hallucination/detector.js +26 -21
  47. package/dist/modules/hallucination/factual.js +170 -150
  48. package/dist/modules/hallucination/index.js +5 -4
  49. package/dist/modules/hallucination/js-identifiers.js +72 -0
  50. package/dist/modules/hallucination/llm-judge.js +103 -0
  51. package/dist/modules/index.js +5 -5
  52. package/dist/modules/lsp/client.js +235 -0
  53. package/dist/modules/lsp/config.js +81 -0
  54. package/dist/modules/lsp/index.js +3 -0
  55. package/dist/modules/lsp/module.js +68 -0
  56. package/dist/modules/lsp/types.js +1 -0
  57. package/dist/modules/mcp/client.js +8 -2
  58. package/dist/modules/memory/store.js +4 -0
  59. package/dist/modules/plugins/builtin/lint-on-write.js +143 -38
  60. package/dist/modules/processes/index.js +1 -2
  61. package/dist/modules/processes/registry.js +125 -35
  62. package/dist/modules/processes/runner.js +9 -110
  63. package/dist/modules/security/audit-log.js +30 -10
  64. package/dist/modules/security/command-validator.js +42 -16
  65. package/dist/modules/security/content-scanner.js +9 -8
  66. package/dist/modules/security/network-validator.js +2 -2
  67. package/dist/modules/security/path-validator.js +64 -10
  68. package/dist/modules/security/security-policies.js +221 -67
  69. package/dist/modules/security/session-encryption.js +42 -25
  70. package/dist/modules/session/manager.js +15 -10
  71. package/dist/modules/session/store.js +62 -8
  72. package/dist/modules/skills/index.js +2 -3
  73. package/dist/modules/skills/module.js +10 -23
  74. package/dist/tools/bash.js +287 -90
  75. package/dist/tools/create-dir.js +0 -1
  76. package/dist/tools/delete-file.js +0 -1
  77. package/dist/tools/edit-file.js +10 -8
  78. package/dist/tools/executor.js +57 -7
  79. package/dist/tools/grep-tool.js +51 -29
  80. package/dist/tools/index.js +55 -40
  81. package/dist/tools/load-skill.js +14 -18
  82. package/dist/tools/move-file.js +3 -2
  83. package/dist/tools/pipeline-run.js +1 -1
  84. package/dist/tools/read-file.js +15 -5
  85. package/dist/tools/search-history.js +42 -22
  86. package/dist/tools/subagent.js +21 -12
  87. package/dist/tools/web-browse.js +54 -25
  88. package/dist/tools/web-fetch.js +60 -34
  89. package/dist/tools/web-search.js +39 -20
  90. package/dist/tools/write-file.js +13 -10
  91. package/dist/ui/diff.js +9 -16
  92. package/dist/ui/renderer.js +69 -6
  93. package/package.json +48 -45
  94. package/dist/modules/context/history.js +0 -15
  95. package/dist/modules/processes/detect.js +0 -34
  96. package/dist/modules/skills/matcher.js +0 -27
@@ -1,46 +1,134 @@
1
- import { mergeSecurityConfig } from '../../config/security';
1
+ import { mergeSecurityConfig, } from "../../config/security";
2
2
  /**
3
3
  * Strict security policy - maximum security, minimum flexibility
4
4
  * Recommended for: production environments, sensitive data, multi-user systems
5
5
  */
6
6
  export const STRICT_POLICY = {
7
- name: 'Strict',
8
- description: 'Maximum security with minimal flexibility. Blocks most dangerous operations.',
9
- preset: 'strict',
10
- recommendedFor: ['production', 'sensitive-data', 'multi-user', 'enterprise'],
7
+ name: "Strict",
8
+ description: "Maximum security with minimal flexibility. Blocks most dangerous operations.",
9
+ preset: "strict",
10
+ recommendedFor: ["production", "sensitive-data", "multi-user", "enterprise"],
11
11
  config: {
12
+ enabled: true,
12
13
  bash: {
14
+ enabled: true,
13
15
  blacklist: [
14
- 'rm', 'dd', 'chmod', 'wget', 'curl', 'scp', 'ssh', 'nc', 'netcat',
15
- 'mknod', 'mkfifo', 'ln', 'chown', 'chgrp', 'useradd', 'usermod',
16
- 'passwd', 'sudo', 'su', 'exec', 'eval', 'source', 'kill', 'pkill',
17
- 'killall', 'shutdown', 'reboot', 'halt', 'poweroff', 'format',
18
- 'fdisk', 'parted', 'mkfs', 'mount', 'umount', 'mv', 'cp',
16
+ "rm",
17
+ "dd",
18
+ "chmod",
19
+ "wget",
20
+ "curl",
21
+ "scp",
22
+ "ssh",
23
+ "nc",
24
+ "netcat",
25
+ "mknod",
26
+ "mkfifo",
27
+ "ln",
28
+ "chown",
29
+ "chgrp",
30
+ "useradd",
31
+ "usermod",
32
+ "passwd",
33
+ "sudo",
34
+ "su",
35
+ "exec",
36
+ "eval",
37
+ "source",
38
+ "kill",
39
+ "pkill",
40
+ "killall",
41
+ "shutdown",
42
+ "reboot",
43
+ "halt",
44
+ "poweroff",
45
+ "format",
46
+ "fdisk",
47
+ "parted",
48
+ "mkfs",
49
+ "mount",
50
+ "umount",
51
+ "mv",
52
+ "cp",
19
53
  ],
20
- whitelist: ['ls', 'cat', 'echo', 'grep', 'find', 'pwd', 'whoami', 'date'],
54
+ whitelist: ["ls", "cat", "echo", "grep", "find", "pwd", "whoami", "date"],
21
55
  blockDangerousFlags: true,
22
56
  dangerousFlags: [
23
- '--force', '-rf', '--no-preserve-root', '--recursive', '--all',
24
- '-x', '-e', '--delete', '--remove', '--dangerous', '--yes', '-y',
25
- '--no-confirm', '-R', '--no-clobber',
57
+ "--force",
58
+ "-rf",
59
+ "--no-preserve-root",
60
+ "--recursive",
61
+ "--all",
62
+ "-x",
63
+ "-e",
64
+ "--delete",
65
+ "--remove",
66
+ "--dangerous",
67
+ "--yes",
68
+ "-y",
69
+ "--no-confirm",
70
+ "-R",
71
+ "--no-clobber",
72
+ ],
73
+ dangerousOperators: [
74
+ ">",
75
+ ">>",
76
+ "2>",
77
+ "2>>",
78
+ "|",
79
+ "&&",
80
+ "||",
81
+ ";",
82
+ "&",
83
+ "`",
26
84
  ],
27
- dangerousOperators: ['>', '>>', '2>', '2>>', '|', '&&', '||', ';', '&', '`'],
28
85
  logCommands: true,
29
86
  },
30
87
  paths: {
88
+ enabled: true,
31
89
  denied: [
32
- '.git/', 'node_modules/', '.env', '*.key', '*.pem', '*.crt',
33
- '*.p12', '*.pfx', '*.secret', '*.token', 'package-lock.json',
34
- 'bun.lock', 'yarn.lock', 'pnpm-lock.yaml', '.ssh/', '.config/',
35
- '.bashrc', '.bash_profile', '.zshrc', '.profile', '/etc/',
36
- '/usr/', '/var/', '/bin/', '/sbin/', '/root/', '/home/',
90
+ ".git/",
91
+ "node_modules/",
92
+ ".env",
93
+ "*.key",
94
+ "*.pem",
95
+ "*.crt",
96
+ "*.p12",
97
+ "*.pfx",
98
+ "*.secret",
99
+ "*.token",
100
+ "package-lock.json",
101
+ "bun.lock",
102
+ "yarn.lock",
103
+ "pnpm-lock.yaml",
104
+ ".ssh/",
105
+ ".config/",
106
+ ".bashrc",
107
+ ".bash_profile",
108
+ ".zshrc",
109
+ ".profile",
110
+ "/etc/",
111
+ "/usr/",
112
+ "/var/",
113
+ "/bin/",
114
+ "/sbin/",
115
+ "/root/",
116
+ "/home/",
37
117
  ],
38
118
  allowed: [],
39
119
  },
40
120
  network: {
121
+ enabled: true,
41
122
  deniedDomains: [
42
- 'localhost', '127.0.0.1', '0.0.0.0', '::1',
43
- '*.internal', '*.local', '192.168.*', '10.*', '172.16.*',
123
+ "localhost",
124
+ "127.0.0.1",
125
+ "0.0.0.0",
126
+ "::1",
127
+ "*.internal",
128
+ "*.local",
129
+ "192.168.*",
130
+ "10.*",
131
+ "172.16.*",
44
132
  ],
45
133
  allowedDomains: [],
46
134
  requestTimeout: 10000,
@@ -81,11 +169,16 @@ export const STRICT_POLICY = {
81
169
  },
82
170
  auditNotifier: {
83
171
  enabled: true,
84
- minSeverity: 'low',
172
+ minSeverity: "low",
85
173
  eventTypes: [
86
- 'security_block', 'bash_command', 'file_operation',
87
- 'network_request', 'authentication', 'configuration_change',
88
- 'session_start', 'session_end',
174
+ "security_block",
175
+ "bash_command",
176
+ "file_operation",
177
+ "network_request",
178
+ "authentication",
179
+ "configuration_change",
180
+ "session_start",
181
+ "session_end",
89
182
  ],
90
183
  maxRetries: 3,
91
184
  },
@@ -96,37 +189,81 @@ export const STRICT_POLICY = {
96
189
  * Recommended for: development, single-user systems, general use
97
190
  */
98
191
  export const BALANCED_POLICY = {
99
- name: 'Balanced',
100
- description: 'Good security with reasonable flexibility. Default policy.',
101
- preset: 'balanced',
102
- recommendedFor: ['development', 'single-user', 'general-use', 'testing'],
192
+ name: "Balanced",
193
+ description: "Good security with reasonable flexibility. Default policy.",
194
+ preset: "balanced",
195
+ recommendedFor: ["development", "single-user", "general-use", "testing"],
103
196
  config: {
197
+ enabled: true,
104
198
  bash: {
199
+ enabled: true,
105
200
  blacklist: [
106
- 'rm', 'dd', 'chmod', 'wget', 'curl', 'scp', 'ssh', 'nc', 'netcat',
107
- 'mknod', 'mkfifo', 'useradd', 'usermod', 'passwd', 'sudo', 'su',
108
- 'shutdown', 'reboot', 'halt', 'poweroff', 'format',
109
- 'fdisk', 'parted', 'mkfs',
201
+ "rm",
202
+ "dd",
203
+ "chmod",
204
+ "wget",
205
+ "curl",
206
+ "scp",
207
+ "ssh",
208
+ "nc",
209
+ "netcat",
210
+ "mknod",
211
+ "mkfifo",
212
+ "useradd",
213
+ "usermod",
214
+ "passwd",
215
+ "sudo",
216
+ "su",
217
+ "shutdown",
218
+ "reboot",
219
+ "halt",
220
+ "poweroff",
221
+ "format",
222
+ "fdisk",
223
+ "parted",
224
+ "mkfs",
110
225
  ],
111
226
  whitelist: [],
112
227
  blockDangerousFlags: true,
113
228
  dangerousFlags: [
114
- '--force', '-rf', '--no-preserve-root', '--recursive', '--all',
115
- '--delete', '--remove', '--dangerous', '--yes', '-y',
116
- '--no-confirm',
229
+ "--force",
230
+ "-rf",
231
+ "--no-preserve-root",
232
+ "--recursive",
233
+ "--all",
234
+ "--delete",
235
+ "--remove",
236
+ "--dangerous",
237
+ "--yes",
238
+ "-y",
239
+ "--no-confirm",
117
240
  ],
118
- dangerousOperators: ['|', '&&', '||', ';', '&', '`'],
241
+ dangerousOperators: ["|", "&&", "||", ";", "&", "`"],
119
242
  logCommands: true,
120
243
  },
121
244
  paths: {
245
+ enabled: true,
122
246
  denied: [
123
- '.git/', 'node_modules/', '.env', '*.key', '*.pem', '*.crt',
124
- '*.p12', '*.pfx', '*.secret', '*.token', 'package-lock.json',
125
- 'bun.lock', 'yarn.lock', 'pnpm-lock.yaml', '.ssh/',
247
+ ".git/",
248
+ "node_modules/",
249
+ ".env",
250
+ "*.key",
251
+ "*.pem",
252
+ "*.crt",
253
+ "*.p12",
254
+ "*.pfx",
255
+ "*.secret",
256
+ "*.token",
257
+ "package-lock.json",
258
+ "bun.lock",
259
+ "yarn.lock",
260
+ "pnpm-lock.yaml",
261
+ ".ssh/",
126
262
  ],
127
263
  allowed: [],
128
264
  },
129
265
  network: {
266
+ enabled: true,
130
267
  deniedDomains: [],
131
268
  allowedDomains: [],
132
269
  requestTimeout: 15000,
@@ -161,8 +298,13 @@ export const BALANCED_POLICY = {
161
298
  },
162
299
  auditNotifier: {
163
300
  enabled: false,
164
- minSeverity: 'medium',
165
- eventTypes: ['security_block', 'bash_command', 'file_operation', 'network_request'],
301
+ minSeverity: "medium",
302
+ eventTypes: [
303
+ "security_block",
304
+ "bash_command",
305
+ "file_operation",
306
+ "network_request",
307
+ ],
166
308
  maxRetries: 3,
167
309
  },
168
310
  },
@@ -172,27 +314,39 @@ export const BALANCED_POLICY = {
172
314
  * Recommended for: trusted environments, local development, testing
173
315
  */
174
316
  export const PERMISSIVE_POLICY = {
175
- name: 'Permissive',
176
- description: 'Minimal security with maximum flexibility. Use with caution.',
177
- preset: 'permissive',
178
- recommendedFor: ['trusted-environment', 'local-dev', 'testing', 'sandbox'],
317
+ name: "Permissive",
318
+ description: "Minimal security with maximum flexibility. Use with caution.",
319
+ preset: "permissive",
320
+ recommendedFor: ["trusted-environment", "local-dev", "testing", "sandbox"],
179
321
  config: {
322
+ enabled: true,
180
323
  bash: {
324
+ enabled: true,
181
325
  blacklist: [
182
- 'rm', 'dd', 'shutdown', 'reboot', 'halt', 'poweroff',
183
- 'format', 'fdisk', 'parted', 'mkfs',
326
+ "rm",
327
+ "dd",
328
+ "shutdown",
329
+ "reboot",
330
+ "halt",
331
+ "poweroff",
332
+ "format",
333
+ "fdisk",
334
+ "parted",
335
+ "mkfs",
184
336
  ],
185
337
  whitelist: [],
186
338
  blockDangerousFlags: false,
187
- dangerousFlags: ['--force', '-rf', '--no-preserve-root'],
339
+ dangerousFlags: ["--force", "-rf", "--no-preserve-root"],
188
340
  dangerousOperators: [],
189
341
  logCommands: true,
190
342
  },
191
343
  paths: {
192
- denied: ['.env', '*.key', '*.pem', '*.crt', '*.p12', '*.pfx'],
344
+ enabled: true,
345
+ denied: [".env", "*.key", "*.pem", "*.crt", "*.p12", "*.pfx"],
193
346
  allowed: [],
194
347
  },
195
348
  network: {
349
+ enabled: true,
196
350
  deniedDomains: [],
197
351
  allowedDomains: [],
198
352
  requestTimeout: 30000,
@@ -222,8 +376,8 @@ export const PERMISSIVE_POLICY = {
222
376
  },
223
377
  auditNotifier: {
224
378
  enabled: false,
225
- minSeverity: 'high',
226
- eventTypes: ['security_block'],
379
+ minSeverity: "high",
380
+ eventTypes: ["security_block"],
227
381
  maxRetries: 2,
228
382
  },
229
383
  },
@@ -236,9 +390,9 @@ export const SECURITY_POLICIES = {
236
390
  balanced: BALANCED_POLICY,
237
391
  permissive: PERMISSIVE_POLICY,
238
392
  custom: {
239
- name: 'Custom',
240
- description: 'Custom security configuration',
241
- preset: 'custom',
393
+ name: "Custom",
394
+ description: "Custom security configuration",
395
+ preset: "custom",
242
396
  recommendedFor: [],
243
397
  config: {},
244
398
  },
@@ -247,7 +401,7 @@ export const SECURITY_POLICIES = {
247
401
  * Security policy manager
248
402
  */
249
403
  export class SecurityPolicyManager {
250
- currentPolicy = 'balanced';
404
+ currentPolicy = "balanced";
251
405
  customConfig = {};
252
406
  /**
253
407
  * Get all available policies
@@ -265,7 +419,7 @@ export class SecurityPolicyManager {
265
419
  * Get the current policy
266
420
  */
267
421
  getCurrentPolicy() {
268
- if (this.currentPolicy === 'custom') {
422
+ if (this.currentPolicy === "custom") {
269
423
  return {
270
424
  ...SECURITY_POLICIES.custom,
271
425
  config: this.customConfig,
@@ -284,7 +438,7 @@ export class SecurityPolicyManager {
284
438
  */
285
439
  setCustomConfig(config) {
286
440
  this.customConfig = config;
287
- this.currentPolicy = 'custom';
441
+ this.currentPolicy = "custom";
288
442
  }
289
443
  /**
290
444
  * Get the current security configuration
@@ -293,7 +447,7 @@ export class SecurityPolicyManager {
293
447
  const policy = this.getCurrentPolicy();
294
448
  return mergeSecurityConfig({
295
449
  ...policy.config,
296
- ...(this.currentPolicy === 'custom' ? this.customConfig : {}),
450
+ ...(this.currentPolicy === "custom" ? this.customConfig : {}),
297
451
  });
298
452
  }
299
453
  /**
@@ -303,7 +457,7 @@ export class SecurityPolicyManager {
303
457
  this.setPolicy(preset);
304
458
  if (customOverrides) {
305
459
  this.customConfig = customOverrides;
306
- this.currentPolicy = 'custom';
460
+ this.currentPolicy = "custom";
307
461
  }
308
462
  return this.getConfig();
309
463
  }
@@ -324,25 +478,25 @@ export class SecurityPolicyManager {
324
478
  allowsAction(action, context) {
325
479
  const config = this.getConfig();
326
480
  switch (action) {
327
- case 'bash':
481
+ case "bash":
328
482
  if (context?.command) {
329
- const cmd = context.command.split(' ')[0];
483
+ const cmd = context.command.split(" ")[0];
330
484
  return !config.bash.blacklist.includes(cmd);
331
485
  }
332
486
  return true;
333
- case 'file_access':
487
+ case "file_access":
334
488
  if (context?.path) {
335
489
  for (const denied of config.paths.denied) {
336
- if (context.path.includes(denied.replace('*', ''))) {
490
+ if (context.path.includes(denied.replace("*", ""))) {
337
491
  return false;
338
492
  }
339
493
  }
340
494
  }
341
495
  return true;
342
- case 'network':
496
+ case "network":
343
497
  if (context?.path) {
344
498
  for (const denied of config.network.deniedDomains) {
345
- if (context.path.includes(denied.replace('*', ''))) {
499
+ if (context.path.includes(denied.replace("*", ""))) {
346
500
  return false;
347
501
  }
348
502
  }
@@ -1,7 +1,7 @@
1
- import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync } from 'fs';
2
- import { join } from 'path';
3
- import { homedir } from 'os';
4
- import { encryptString, decryptString, isEncryptedString, ConfigEncryptor, } from './encryption';
1
+ import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync, } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ import { encryptString, decryptString, isEncryptedString, ConfigEncryptor, } from "./encryption";
5
5
  /**
6
6
  * Default session encryption configuration
7
7
  */
@@ -19,7 +19,7 @@ export class SessionFileEncryptor {
19
19
  constructor(config) {
20
20
  this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
21
21
  this.encryptor = new ConfigEncryptor({
22
- keyPath: config?.keyPath || join(homedir(), '.mma', '.session-encryption-key')
22
+ keyPath: config?.keyPath || join(homedir(), ".mma", ".session-encryption-key"),
23
23
  });
24
24
  }
25
25
  /**
@@ -58,10 +58,16 @@ export class SessionFileEncryptor {
58
58
  * Decrypt a JSON string
59
59
  */
60
60
  decryptJSON(content) {
61
- if (!this.config.enabled)
62
- return JSON.parse(content);
63
- const decrypted = this.decryptFileContent(content);
64
- return JSON.parse(decrypted);
61
+ let plain = content;
62
+ if (this.config.enabled) {
63
+ plain = this.decryptFileContent(content);
64
+ }
65
+ try {
66
+ return JSON.parse(plain);
67
+ }
68
+ catch {
69
+ throw new Error("Invalid JSON content in session file — file may be corrupted or encrypted with a different key");
70
+ }
65
71
  }
66
72
  /**
67
73
  * Encrypt a JSONL file (one JSON object per line)
@@ -69,7 +75,7 @@ export class SessionFileEncryptor {
69
75
  encryptJSONL(lines) {
70
76
  if (!this.config.enabled)
71
77
  return lines;
72
- return lines.map(line => this.encryptFileContent(line));
78
+ return lines.map((line) => this.encryptFileContent(line));
73
79
  }
74
80
  /**
75
81
  * Decrypt a JSONL file
@@ -77,13 +83,13 @@ export class SessionFileEncryptor {
77
83
  decryptJSONL(lines) {
78
84
  if (!this.config.enabled)
79
85
  return lines;
80
- return lines.map(line => this.decryptFileContent(line));
86
+ return lines.map((line) => this.decryptFileContent(line));
81
87
  }
82
88
  /**
83
89
  * Read and decrypt a session file
84
90
  */
85
91
  readSessionFile(filePath) {
86
- const content = readFileSync(filePath, 'utf8');
92
+ const content = readFileSync(filePath, "utf8");
87
93
  return this.decryptFileContent(content);
88
94
  }
89
95
  /**
@@ -91,13 +97,13 @@ export class SessionFileEncryptor {
91
97
  */
92
98
  writeSessionFile(filePath, content) {
93
99
  const encrypted = this.encryptFileContent(content);
94
- writeFileSync(filePath, encrypted, 'utf8');
100
+ writeFileSync(filePath, encrypted, "utf8");
95
101
  }
96
102
  /**
97
103
  * Read and decrypt a JSON session file
98
104
  */
99
105
  readSessionJSON(filePath) {
100
- const content = readFileSync(filePath, 'utf8');
106
+ const content = readFileSync(filePath, "utf8");
101
107
  return this.decryptJSON(content);
102
108
  }
103
109
  /**
@@ -105,23 +111,34 @@ export class SessionFileEncryptor {
105
111
  */
106
112
  writeSessionJSON(filePath, obj) {
107
113
  const content = this.encryptJSON(obj);
108
- writeFileSync(filePath, content, 'utf8');
114
+ writeFileSync(filePath, content, "utf8");
109
115
  }
110
116
  /**
111
117
  * Read and decrypt a JSONL session file
112
118
  */
113
119
  readSessionJSONL(filePath) {
114
- const content = readFileSync(filePath, 'utf8');
115
- const lines = content.split('\n').filter(line => line.trim());
120
+ const content = readFileSync(filePath, "utf8");
121
+ const lines = content.split("\n").filter((line) => line.trim());
116
122
  const decryptedLines = this.decryptJSONL(lines);
117
- return decryptedLines.map(line => JSON.parse(line));
123
+ // Skip malformed lines instead of crashing the whole session log
124
+ return decryptedLines.flatMap((line) => {
125
+ try {
126
+ return [JSON.parse(line)];
127
+ }
128
+ catch {
129
+ return [];
130
+ }
131
+ });
118
132
  }
119
133
  /**
120
134
  * Append an encrypted entry to a JSONL file
121
135
  */
122
136
  appendToSessionJSONL(filePath, obj) {
123
137
  const encryptedLine = this.encryptFileContent(JSON.stringify(obj));
124
- writeFileSync(filePath, encryptedLine + '\n', { flag: 'a', encoding: 'utf8' });
138
+ writeFileSync(filePath, encryptedLine + "\n", {
139
+ flag: "a",
140
+ encoding: "utf8",
141
+ });
125
142
  }
126
143
  /**
127
144
  * Encrypt all session files in a directory
@@ -132,11 +149,11 @@ export class SessionFileEncryptor {
132
149
  const files = readdirSync(sessionDir);
133
150
  for (const file of files) {
134
151
  const filePath = join(sessionDir, file);
135
- if (existsSync(filePath) && !file.endsWith('.enc')) {
152
+ if (existsSync(filePath) && !file.endsWith(".enc")) {
136
153
  try {
137
- const content = readFileSync(filePath, 'utf8');
154
+ const content = readFileSync(filePath, "utf8");
138
155
  const encrypted = this.encryptFileContent(content);
139
- writeFileSync(filePath + '.enc', encrypted, 'utf8');
156
+ writeFileSync(filePath + ".enc", encrypted, "utf8");
140
157
  unlinkSync(filePath);
141
158
  }
142
159
  catch {
@@ -153,13 +170,13 @@ export class SessionFileEncryptor {
153
170
  return;
154
171
  const files = readdirSync(sessionDir);
155
172
  for (const file of files) {
156
- if (file.endsWith('.enc')) {
173
+ if (file.endsWith(".enc")) {
157
174
  const encFilePath = join(sessionDir, file);
158
175
  const decFilePath = encFilePath.slice(0, -4); // Remove .enc
159
176
  try {
160
- const content = readFileSync(encFilePath, 'utf8');
177
+ const content = readFileSync(encFilePath, "utf8");
161
178
  const decrypted = this.decryptFileContent(content);
162
- writeFileSync(decFilePath, decrypted, 'utf8');
179
+ writeFileSync(decFilePath, decrypted, "utf8");
163
180
  unlinkSync(encFilePath);
164
181
  }
165
182
  catch {
@@ -1,5 +1,5 @@
1
- import { t } from '../../i18n/index';
2
- import { createSessionContext, DEFAULT_SESSION_ISOLATION } from '../../modules/security/session-isolation';
1
+ import { t } from "../../i18n/index";
2
+ import { createSessionContext, DEFAULT_SESSION_ISOLATION, } from "../../modules/security/session-isolation";
3
3
  export class SessionManager {
4
4
  store;
5
5
  activeId = null;
@@ -32,7 +32,7 @@ export class SessionManager {
32
32
  create(name, securityOverrides) {
33
33
  const now = new Date().toISOString();
34
34
  const id = `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
35
- const sessionName = name || t('session.default_name', { date: now.slice(0, 10) });
35
+ const sessionName = name || t("session.default_name", { date: now.slice(0, 10) });
36
36
  const meta = {
37
37
  id,
38
38
  name: sessionName,
@@ -73,7 +73,7 @@ export class SessionManager {
73
73
  rename(id, name) {
74
74
  const meta = this.store.loadMeta(id);
75
75
  if (!meta)
76
- throw new Error(t('session.not_found', { id }));
76
+ throw new Error(t("session.not_found", { id }));
77
77
  meta.name = name;
78
78
  this.store.saveMeta(id, meta);
79
79
  }
@@ -87,13 +87,13 @@ export class SessionManager {
87
87
  }
88
88
  setActive(id) {
89
89
  if (!this.store.sessionExists(id)) {
90
- throw new Error(t('session.not_found', { id }));
90
+ throw new Error(t("session.not_found", { id }));
91
91
  }
92
92
  this.activeId = id;
93
93
  }
94
94
  appendMessage(msg) {
95
95
  if (!this.activeId)
96
- throw new Error(t('session.no_active'));
96
+ throw new Error(t("session.no_active"));
97
97
  this.store.appendMessage(this.activeId, msg);
98
98
  }
99
99
  appendLog(entry) {
@@ -117,6 +117,9 @@ export class SessionManager {
117
117
  /**
118
118
  * Get the session context for a specific session
119
119
  */
120
+ getSessionDirectory(id) {
121
+ return this.store.getSessionDir(id);
122
+ }
120
123
  getSessionContextById(sessionId) {
121
124
  if (!this.sessionContexts.has(sessionId)) {
122
125
  const meta = this.store.loadMeta(sessionId);
@@ -148,10 +151,12 @@ export class SessionManager {
148
151
  return {
149
152
  maxFileOperations: overrides.maxFileOperations,
150
153
  maxRecursionDepth: overrides.maxRecursionDepth,
151
- rateLimits: overrides.rateLimits ? {
152
- maxRequestsPerMinute: overrides.rateLimits.maxRequestsPerMinute ?? 60,
153
- maxParallelTasks: overrides.rateLimits.maxParallelTasks ?? 5,
154
- } : undefined,
154
+ rateLimits: overrides.rateLimits
155
+ ? {
156
+ maxRequestsPerMinute: overrides.rateLimits.maxRequestsPerMinute ?? 60,
157
+ maxParallelTasks: overrides.rateLimits.maxParallelTasks ?? 5,
158
+ }
159
+ : undefined,
155
160
  };
156
161
  }
157
162
  enforceMaxSessions() {