micro-models-agent 0.56.5 → 0.57.1

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 (233) hide show
  1. package/CHANGELOG.md +481 -463
  2. package/README.md +358 -358
  3. package/dist/certification/certifications.json +493 -493
  4. package/dist/cli/commands.js +447 -0
  5. package/dist/cli/completer.js +167 -0
  6. package/dist/cli/index.js +2 -0
  7. package/dist/cli/main.js +153 -0
  8. package/dist/cli/plugin-commands.js +36 -0
  9. package/dist/cli/repl-commands.js +761 -0
  10. package/dist/cli/repl.js +702 -0
  11. package/dist/cli/run-result.js +33 -0
  12. package/dist/cli/security-commands.js +164 -0
  13. package/dist/cli/setup.js +237 -0
  14. package/dist/config/config.js +276 -0
  15. package/dist/config/defaults.js +141 -0
  16. package/dist/config/domains.js +179 -0
  17. package/dist/config/experts.js +15 -0
  18. package/dist/config/index.js +4 -0
  19. package/dist/config/security.js +213 -0
  20. package/dist/config/types.js +1 -0
  21. package/dist/core/agent-moe.js +102 -0
  22. package/dist/core/agent.js +1018 -0
  23. package/dist/core/bootstrap.js +481 -0
  24. package/dist/core/crash-handler.js +51 -0
  25. package/dist/core/environment.js +199 -0
  26. package/dist/core/index.js +2 -0
  27. package/dist/core/prompt-builder.js +76 -0
  28. package/dist/core/session-logger.js +251 -0
  29. package/dist/core/types.js +1 -0
  30. package/dist/core/version.js +26 -0
  31. package/dist/core/workspace.js +76 -0
  32. package/dist/i18n/en.json +679 -0
  33. package/dist/i18n/index.js +46 -0
  34. package/dist/i18n/ru.json +679 -0
  35. package/dist/index.js +22 -0
  36. package/dist/llm/image-utils.js +143 -0
  37. package/dist/llm/index.js +4 -0
  38. package/dist/llm/model-loader.js +78 -0
  39. package/dist/llm/openai-compat.js +497 -0
  40. package/dist/llm/orchestrator.js +200 -0
  41. package/dist/llm/provider.js +10 -0
  42. package/dist/llm/response.js +39 -0
  43. package/dist/llm/token-counter.js +39 -0
  44. package/dist/llm/types.js +1 -0
  45. package/dist/logger/app-logger.js +189 -0
  46. package/dist/logger/file-log.js +151 -0
  47. package/dist/logger/index.js +1 -0
  48. package/dist/main.js +457 -324
  49. package/dist/migration/backup.js +45 -0
  50. package/dist/migration/detect.js +50 -0
  51. package/dist/migration/index.js +2 -0
  52. package/dist/modules/artifacts/store.js +61 -0
  53. package/dist/modules/browser/actions.js +76 -0
  54. package/dist/modules/browser/bridge-client.js +199 -0
  55. package/dist/modules/browser/bridge-path.js +10 -0
  56. package/dist/modules/browser/bridge-server.mjs +219 -219
  57. package/dist/modules/browser/cookie-store.js +24 -0
  58. package/dist/modules/browser/driver.js +136 -0
  59. package/dist/modules/browser/index.js +7 -0
  60. package/dist/modules/browser/module.js +29 -0
  61. package/dist/modules/browser/session.js +342 -0
  62. package/dist/modules/browser/snapshot.js +148 -0
  63. package/dist/modules/browser/types.js +12 -0
  64. package/dist/modules/certification/cli.js +213 -0
  65. package/dist/modules/certification/fact-checker.js +82 -0
  66. package/dist/modules/certification/loader.js +106 -0
  67. package/dist/modules/certification/manifest.js +58 -0
  68. package/dist/modules/certification/runner.js +245 -0
  69. package/dist/modules/certification/scenarios.js +407 -0
  70. package/dist/modules/certification/types.js +1 -0
  71. package/dist/modules/context/chunk-query.js +100 -0
  72. package/dist/modules/context/fact-extractor.js +168 -0
  73. package/dist/modules/context/history.js +15 -0
  74. package/dist/modules/context/index.js +1 -0
  75. package/dist/modules/context/manager.js +440 -0
  76. package/dist/modules/execution/audit-runners.js +206 -0
  77. package/dist/modules/execution/auditor.js +218 -0
  78. package/dist/modules/execution/execution-plugin.js +431 -0
  79. package/dist/modules/execution/index.js +8 -0
  80. package/dist/modules/execution/module.js +625 -0
  81. package/dist/modules/execution/moe-executor.js +304 -0
  82. package/dist/modules/execution/plan-coverage.js +68 -0
  83. package/dist/modules/execution/plan-persister.js +46 -0
  84. package/dist/modules/execution/plan-store.js +196 -0
  85. package/dist/modules/execution/plan-tool.js +677 -0
  86. package/dist/modules/execution/plan-validator.js +153 -0
  87. package/dist/modules/execution/planner.js +94 -0
  88. package/dist/modules/execution/stuck-detector.js +746 -0
  89. package/dist/modules/execution/tracker.js +69 -0
  90. package/dist/modules/execution/types.js +1 -0
  91. package/dist/modules/execution/verifier.js +235 -0
  92. package/dist/modules/execution/windows-commands.js +41 -0
  93. package/dist/modules/hallucination/confidence.js +66 -0
  94. package/dist/modules/hallucination/consistency.js +26 -0
  95. package/dist/modules/hallucination/detector.js +47 -0
  96. package/dist/modules/hallucination/factual.js +169 -0
  97. package/dist/modules/hallucination/index.js +5 -0
  98. package/dist/modules/hallucination/js-identifiers.js +262 -0
  99. package/dist/modules/hallucination/llm-judge.js +101 -0
  100. package/dist/modules/index.js +5 -0
  101. package/dist/modules/indexer/cache.js +40 -0
  102. package/dist/modules/indexer/index.js +3 -0
  103. package/dist/modules/indexer/module.js +246 -0
  104. package/dist/modules/indexer/project-profile.js +183 -0
  105. package/dist/modules/indexer/walker.js +101 -0
  106. package/dist/modules/lsp/check-tool.js +58 -0
  107. package/dist/modules/lsp/client.js +389 -0
  108. package/dist/modules/lsp/command.js +60 -0
  109. package/dist/modules/lsp/config.js +135 -0
  110. package/dist/modules/lsp/index.js +3 -0
  111. package/dist/modules/lsp/module.js +260 -0
  112. package/dist/modules/lsp/probe.js +86 -0
  113. package/dist/modules/lsp/project-root.js +32 -0
  114. package/dist/modules/lsp/startup-check.js +144 -0
  115. package/dist/modules/lsp/types.js +1 -0
  116. package/dist/modules/mcp/client.js +399 -0
  117. package/dist/modules/mcp/index.js +3 -0
  118. package/dist/modules/mcp/module.js +142 -0
  119. package/dist/modules/mcp/registry.js +15 -0
  120. package/dist/modules/memory/index.js +1 -0
  121. package/dist/modules/memory/module.js +96 -0
  122. package/dist/modules/memory/search.js +42 -0
  123. package/dist/modules/memory/store.js +69 -0
  124. package/dist/modules/pipelines/engine.js +60 -0
  125. package/dist/modules/pipelines/index.js +3 -0
  126. package/dist/modules/pipelines/parser.js +56 -0
  127. package/dist/modules/pipelines/template.js +14 -0
  128. package/dist/modules/plugins/builtin/lint-on-write.js +334 -0
  129. package/dist/modules/plugins/builtin/notify.js +9 -0
  130. package/dist/modules/plugins/index.js +1 -0
  131. package/dist/modules/plugins/loader.js +70 -0
  132. package/dist/modules/plugins/manager.js +261 -0
  133. package/dist/modules/plugins/types.js +1 -0
  134. package/dist/modules/pricing/index.js +61 -0
  135. package/dist/modules/pricing/prices.js +129 -0
  136. package/dist/modules/processes/detect.js +34 -0
  137. package/dist/modules/processes/index.js +2 -0
  138. package/dist/modules/processes/registry.js +327 -0
  139. package/dist/modules/processes/runner.js +23 -0
  140. package/dist/modules/providers/create.js +22 -0
  141. package/dist/modules/providers/fallback.js +79 -0
  142. package/dist/modules/providers/health.js +46 -0
  143. package/dist/modules/providers/index.js +5 -0
  144. package/dist/modules/providers/manager.js +161 -0
  145. package/dist/modules/providers/presets.js +128 -0
  146. package/dist/modules/providers/registry.js +22 -0
  147. package/dist/modules/providers/types.js +1 -0
  148. package/dist/modules/registry.js +48 -0
  149. package/dist/modules/security/audit-log.js +136 -0
  150. package/dist/modules/security/audit-notifier.js +292 -0
  151. package/dist/modules/security/command-validator.js +219 -0
  152. package/dist/modules/security/content-scanner.js +53 -0
  153. package/dist/modules/security/data-sanitizer.js +89 -0
  154. package/dist/modules/security/encryption.js +242 -0
  155. package/dist/modules/security/index.js +14 -0
  156. package/dist/modules/security/network-validator.js +88 -0
  157. package/dist/modules/security/path-validator.js +203 -0
  158. package/dist/modules/security/rate-limiter.js +119 -0
  159. package/dist/modules/security/security-policies.js +531 -0
  160. package/dist/modules/security/session-encryption.js +210 -0
  161. package/dist/modules/security/session-isolation.js +95 -0
  162. package/dist/modules/session/index.js +3 -0
  163. package/dist/modules/session/manager.js +172 -0
  164. package/dist/modules/session/module.js +24 -0
  165. package/dist/modules/session/store.js +222 -0
  166. package/dist/modules/session/types.js +1 -0
  167. package/dist/modules/skills/index.js +2 -0
  168. package/dist/modules/skills/loader.js +72 -0
  169. package/dist/modules/skills/matcher.js +27 -0
  170. package/dist/modules/skills/module.js +129 -0
  171. package/dist/modules/types.js +1 -0
  172. package/dist/modules/updater/checker.js +96 -0
  173. package/dist/modules/updater/index.js +2 -0
  174. package/dist/modules/updater/module.js +116 -0
  175. package/dist/modules/user-profile/compressor.js +16 -0
  176. package/dist/modules/user-profile/index.js +1 -0
  177. package/dist/modules/user-profile/profile.js +68 -0
  178. package/dist/skills/builtin/git.md +36 -36
  179. package/dist/skills/builtin/typescript.md +35 -35
  180. package/dist/tools/approve.js +33 -0
  181. package/dist/tools/attach-image.js +101 -0
  182. package/dist/tools/bash.js +519 -0
  183. package/dist/tools/browser.js +115 -0
  184. package/dist/tools/chunk-query.js +100 -0
  185. package/dist/tools/create-dir.js +56 -0
  186. package/dist/tools/delete-file.js +63 -0
  187. package/dist/tools/download-file.js +117 -0
  188. package/dist/tools/edit-file.js +80 -0
  189. package/dist/tools/enable-tools.js +59 -0
  190. package/dist/tools/executor.js +154 -0
  191. package/dist/tools/file-info.js +47 -0
  192. package/dist/tools/filter-tools.js +17 -0
  193. package/dist/tools/glob-tool.js +27 -0
  194. package/dist/tools/grep-tool.js +125 -0
  195. package/dist/tools/hidden-tools-block.js +37 -0
  196. package/dist/tools/index.js +78 -0
  197. package/dist/tools/list-dir.js +49 -0
  198. package/dist/tools/load-skill.js +43 -0
  199. package/dist/tools/mcp-call.js +69 -0
  200. package/dist/tools/move-file.js +86 -0
  201. package/dist/tools/path-utils.js +101 -0
  202. package/dist/tools/pipeline-run.js +145 -0
  203. package/dist/tools/preview.js +2 -0
  204. package/dist/tools/process-kill.js +40 -0
  205. package/dist/tools/process-list.js +37 -0
  206. package/dist/tools/process-log.js +54 -0
  207. package/dist/tools/question.js +141 -0
  208. package/dist/tools/read-file.js +179 -0
  209. package/dist/tools/recall.js +118 -0
  210. package/dist/tools/registry.js +47 -0
  211. package/dist/tools/remember.js +68 -0
  212. package/dist/tools/scope-check.js +32 -0
  213. package/dist/tools/search-history.js +85 -0
  214. package/dist/tools/subagent.js +196 -0
  215. package/dist/tools/types.js +1 -0
  216. package/dist/tools/user-input.js +123 -0
  217. package/dist/tools/web-browse.js +87 -0
  218. package/dist/tools/web-fetch.js +119 -0
  219. package/dist/tools/web-search.js +105 -0
  220. package/dist/tools/write-file.js +82 -0
  221. package/dist/ui/box.js +77 -0
  222. package/dist/ui/colors.js +4 -0
  223. package/dist/ui/diff.js +178 -0
  224. package/dist/ui/index.js +6 -0
  225. package/dist/ui/line-editor.js +822 -0
  226. package/dist/ui/line-math.js +73 -0
  227. package/dist/ui/md-formatter.js +212 -0
  228. package/dist/ui/output.js +13 -0
  229. package/dist/ui/plan-view.js +103 -0
  230. package/dist/ui/renderer.js +259 -0
  231. package/dist/ui/spinner.js +70 -0
  232. package/dist/ui/table.js +144 -0
  233. package/package.json +51 -51
@@ -0,0 +1,68 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir, hostname, platform, type } from "os";
4
+ import { env } from "process";
5
+ import { ProfileCompressor } from "./compressor";
6
+ export class UserProfile {
7
+ profileDir;
8
+ info = null;
9
+ preferences = {};
10
+ constructor(profileDir) {
11
+ this.profileDir = profileDir;
12
+ }
13
+ collect() {
14
+ this.info = {
15
+ platform: platform(),
16
+ os: `${type()} ${hostname()}`,
17
+ hostname: hostname(),
18
+ shell: env.SHELL || env.ComSpec || "unknown",
19
+ home: homedir(),
20
+ nodeVersion: process.version,
21
+ preferences: { ...this.preferences },
22
+ };
23
+ return this.info;
24
+ }
25
+ save() {
26
+ if (!existsSync(this.profileDir)) {
27
+ mkdirSync(this.profileDir, { recursive: true });
28
+ }
29
+ writeFileSync(join(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
30
+ }
31
+ load() {
32
+ const path = join(this.profileDir, "profile.json");
33
+ if (!existsSync(path))
34
+ return null;
35
+ try {
36
+ const data = JSON.parse(readFileSync(path, "utf-8"));
37
+ this.info = {
38
+ platform: data.platform,
39
+ os: data.os,
40
+ hostname: data.hostname,
41
+ shell: data.shell,
42
+ home: data.home,
43
+ nodeVersion: data.nodeVersion,
44
+ preferences: data.preferences || {},
45
+ };
46
+ this.preferences = data.preferences || {};
47
+ return this.info;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ getInfo() {
54
+ return this.info;
55
+ }
56
+ setPreference(key, value) {
57
+ this.preferences[key] = value;
58
+ }
59
+ getPreference(key) {
60
+ return this.preferences[key];
61
+ }
62
+ compress() {
63
+ if (!this.info)
64
+ this.collect();
65
+ const compressor = new ProfileCompressor();
66
+ return compressor.compress(this.info);
67
+ }
68
+ }
@@ -1,36 +1,36 @@
1
- ---
2
- name: git-expert
3
- description: Git branching, merge conflicts, rebasing, workflow strategies
4
- keywords: [git, branch, merge, rebase, commit, conflict, workflow]
5
- mma_version: 2
6
- ---
7
-
8
- # Git Expert
9
-
10
- ## Branching Strategies
11
-
12
- - **Feature branches**: `feature/xxx` from `main`, merge via PR
13
- - **Hotfix**: `hotfix/xxx` from `main`, merge to both `main` and `develop`
14
- - **Release**: `release/xxx` from `develop`, merge to `main` + `develop`
15
-
16
- ## Conflict Resolution
17
-
18
- 1. `git fetch origin` — get latest
19
- 2. `git checkout your-branch`
20
- 3. `git rebase origin/main` — apply your commits on top
21
- 4. Resolve conflicts file by file
22
- 5. `git add <files>` then `git rebase --continue`
23
-
24
- ## Useful Commands
25
-
26
- - `git log --oneline --graph --all` — visualize history
27
- - `git diff --stat` — summary of changes
28
- - `git stash pop` — restore stashed changes
29
- - `git cherry-pick <commit>` — apply specific commit
30
-
31
- ## Best Practices
32
-
33
- - Commit often with clear messages
34
- - Keep commits atomic (one logical change)
35
- - Never force push to shared branches
36
- - Use `git rebase -i` to clean up before PR
1
+ ---
2
+ name: git-expert
3
+ description: Git branching, merge conflicts, rebasing, workflow strategies
4
+ keywords: [git, branch, merge, rebase, commit, conflict, workflow]
5
+ mma_version: 2
6
+ ---
7
+
8
+ # Git Expert
9
+
10
+ ## Branching Strategies
11
+
12
+ - **Feature branches**: `feature/xxx` from `main`, merge via PR
13
+ - **Hotfix**: `hotfix/xxx` from `main`, merge to both `main` and `develop`
14
+ - **Release**: `release/xxx` from `develop`, merge to `main` + `develop`
15
+
16
+ ## Conflict Resolution
17
+
18
+ 1. `git fetch origin` — get latest
19
+ 2. `git checkout your-branch`
20
+ 3. `git rebase origin/main` — apply your commits on top
21
+ 4. Resolve conflicts file by file
22
+ 5. `git add <files>` then `git rebase --continue`
23
+
24
+ ## Useful Commands
25
+
26
+ - `git log --oneline --graph --all` — visualize history
27
+ - `git diff --stat` — summary of changes
28
+ - `git stash pop` — restore stashed changes
29
+ - `git cherry-pick <commit>` — apply specific commit
30
+
31
+ ## Best Practices
32
+
33
+ - Commit often with clear messages
34
+ - Keep commits atomic (one logical change)
35
+ - Never force push to shared branches
36
+ - Use `git rebase -i` to clean up before PR
@@ -1,35 +1,35 @@
1
- ---
2
- name: typescript-expert
3
- description: TypeScript type system, generics, advanced patterns, compiler API
4
- keywords: [typescript, types, generics, tsconfig, compiler, interface, type]
5
- mma_version: 2
6
- ---
7
-
8
- # TypeScript Expert
9
-
10
- ## Key Patterns
11
-
12
- - Use `readonly` for immutable properties
13
- - Prefer `interface` over `type` for object shapes
14
- - Use `as const` for literal types
15
- - Prefer `unknown` over `any` for type safety
16
-
17
- ## Common Pitfalls
18
-
19
- - Avoid `any` — use `unknown` + type guards
20
- - Don't use `enum` — use `const` objects or union types
21
- - Avoid `namespace` — use ES modules
22
-
23
- ## Type Guards
24
-
25
- ```ts
26
- function isString(val: unknown): val is string {
27
- return typeof val === 'string'
28
- }
29
- ```
30
-
31
- ## Generics
32
-
33
- - Use `extends` for constraints
34
- - Use `infer` for conditional type extraction
35
- - Prefer explicit over implicit for complex cases
1
+ ---
2
+ name: typescript-expert
3
+ description: TypeScript type system, generics, advanced patterns, compiler API
4
+ keywords: [typescript, types, generics, tsconfig, compiler, interface, type]
5
+ mma_version: 2
6
+ ---
7
+
8
+ # TypeScript Expert
9
+
10
+ ## Key Patterns
11
+
12
+ - Use `readonly` for immutable properties
13
+ - Prefer `interface` over `type` for object shapes
14
+ - Use `as const` for literal types
15
+ - Prefer `unknown` over `any` for type safety
16
+
17
+ ## Common Pitfalls
18
+
19
+ - Avoid `any` — use `unknown` + type guards
20
+ - Don't use `enum` — use `const` objects or union types
21
+ - Avoid `namespace` — use ES modules
22
+
23
+ ## Type Guards
24
+
25
+ ```ts
26
+ function isString(val: unknown): val is string {
27
+ return typeof val === 'string'
28
+ }
29
+ ```
30
+
31
+ ## Generics
32
+
33
+ - Use `extends` for constraints
34
+ - Use `infer` for conditional type extraction
35
+ - Prefer explicit over implicit for complex cases
@@ -0,0 +1,33 @@
1
+ import { t } from "../i18n/index";
2
+ import { askChoice } from "./user-input";
3
+ export const approveTool = {
4
+ name: "approve",
5
+ icon: "🙋",
6
+ description: "Request user approval for an action. The user picks Yes or No from a menu.",
7
+ tags: ["core"],
8
+ interactive: true,
9
+ parameters: {
10
+ type: "object",
11
+ properties: {
12
+ action: {
13
+ type: "string",
14
+ description: "Description of the action requiring approval",
15
+ },
16
+ },
17
+ required: ["action"],
18
+ },
19
+ handler: async (ctx, args) => {
20
+ if (ctx.exitOnComplete) {
21
+ return { success: false, output: t("tool.interactive_disabled") };
22
+ }
23
+ const action = String(args.action || "");
24
+ const indexes = await askChoice(t("tool.approve_prompt", { action }), [
25
+ { label: t("tool.approve_yes"), description: t("tool.approve_yes_desc") },
26
+ { label: t("tool.approve_no"), description: t("tool.approve_no_desc") },
27
+ ]);
28
+ if (indexes[0] === 0) {
29
+ return { success: true, output: t("tool.approved") };
30
+ }
31
+ return { success: false, output: t("tool.rejected") };
32
+ },
33
+ };
@@ -0,0 +1,101 @@
1
+ import { existsSync } from "fs";
2
+ import { resolve } from "path";
3
+ import { t } from "../i18n/index";
4
+ import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl, } from "../llm/image-utils";
5
+ import { logSecurityBlock } from "../modules/security/audit-log";
6
+ export const attachImageTool = {
7
+ name: "attach_image",
8
+ icon: "🖼️",
9
+ description: "Attach an image to the conversation from a file path, URL, or clipboard. The image will be included in the next message sent to the model. Supports PNG, JPEG, GIF, WebP.",
10
+ tags: ["vision", "image"],
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ source: {
15
+ type: "string",
16
+ description: 'Image source: file path (e.g. "./screenshot.png"), URL (e.g. "https://example.com/img.png"), or "clipboard" to read from system clipboard.',
17
+ },
18
+ },
19
+ required: ["source"],
20
+ },
21
+ handler: async (ctx, args) => {
22
+ const source = String(args.source ?? "").trim();
23
+ if (!source) {
24
+ return { success: false, output: t("image.source_required") };
25
+ }
26
+ try {
27
+ let dataUrl;
28
+ if (source.toLowerCase() === "clipboard") {
29
+ const clipBuf = await readClipboardImage();
30
+ if (!clipBuf) {
31
+ return {
32
+ success: false,
33
+ output: t("image.clipboard_empty"),
34
+ };
35
+ }
36
+ const result = await bufferToDataUrl(clipBuf);
37
+ dataUrl = result.dataUrl;
38
+ }
39
+ else if (source.startsWith("http://") || source.startsWith("https://")) {
40
+ // Security: validate URL against the network policy (SSRF).
41
+ const { isUrlAllowed } = await import("../modules/security/network-validator");
42
+ const { getSessionSecurityConfig } = await import("../modules/security/session-isolation");
43
+ const networkConfig = ctx.sessionContext
44
+ ? getSessionSecurityConfig(ctx.config, ctx.sessionContext).network
45
+ : ctx.config?.security?.network;
46
+ const urlCheck = isUrlAllowed(source, networkConfig);
47
+ if (!urlCheck.allowed) {
48
+ logSecurityBlock(ctx.sessionId, "network_request", urlCheck.reason ?? "attach_image URL blocked", source);
49
+ return { success: false, output: `[SECURITY BLOCKED] ${urlCheck.reason}` };
50
+ }
51
+ const result = await loadUrlAsDataUrl(source);
52
+ dataUrl = result.dataUrl;
53
+ }
54
+ else {
55
+ const absPath = resolve(ctx.baseDir, source);
56
+ if (!existsSync(absPath)) {
57
+ return {
58
+ success: false,
59
+ output: t("image.not_found", { path: source }),
60
+ };
61
+ }
62
+ // Security: check path scope
63
+ if (ctx.scope) {
64
+ const { isPathInScope } = await import("../modules/security/path-validator");
65
+ const validation = isPathInScope(ctx.baseDir, absPath, ctx.scope);
66
+ if (!validation.allowed) {
67
+ logSecurityBlock(ctx.sessionId, "file_read", validation.reason ?? "attach_image out of scope", absPath);
68
+ return {
69
+ success: false,
70
+ output: t("file.path_not_allowed", { path: source }),
71
+ };
72
+ }
73
+ }
74
+ const result = await loadFileAsDataUrl(absPath);
75
+ dataUrl = result.dataUrl;
76
+ }
77
+ // Add the image as a pending content part on the context manager
78
+ if (!ctx.contextManager) {
79
+ return {
80
+ success: false,
81
+ output: t("image.no_context"),
82
+ };
83
+ }
84
+ ctx.contextManager.addPendingImage({
85
+ type: "image_url",
86
+ image_url: { url: dataUrl },
87
+ });
88
+ const sizeKb = Math.round((dataUrl.length * 3) / 4 / 1024);
89
+ return {
90
+ success: true,
91
+ output: t("image.attached", { source, size: `${sizeKb} KB` }),
92
+ };
93
+ }
94
+ catch (err) {
95
+ return {
96
+ success: false,
97
+ output: t("image.error", { message: err.message }),
98
+ };
99
+ }
100
+ },
101
+ };