refactor(artifacts): consolidate inventory into inspection
This commit is contained in:
parent
a66e43a99d
commit
0fe96d92df
24
README.md
24
README.md
|
|
@ -356,18 +356,18 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
|
|||
|
||||
## Tool catalog for investigation
|
||||
|
||||
| Tool family | Count | Examples |
|
||||
| ------------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native inspection | 36 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations, bounded byte reads, file-offset translation |
|
||||
| Investigation workflows | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `trace_feature`, exact string-to-code lookup, bounded call paths, call graphs, Swift and Objective-C discovery |
|
||||
| Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
|
||||
| Artifact graph | 3 | bounded provider-neutral inspection, deterministic directory/ZIP/APK/IPA/MSIX/AppX/ASAR inventory, and explicitly selected extraction into an absent owned tree |
|
||||
| Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
|
||||
| Browser observation | 9 | exact-origin passive CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, visual evidence, and bounded Playwright scenarios |
|
||||
| Electron analysis | 5 | passive root-confined observation, static application mapping, evidence-backed static/runtime reconciliation, and provider-owned click/wait scenarios |
|
||||
| JavaScript runtime | 2 | approved attach-only Node/Electron Inspector target discovery plus bounded script and execution-context observation without evaluation or instrumentation |
|
||||
| Application workflows | 11 | bounded cross-layer traces, unique-only version matching, historical-source to bundle mapping, static export return-shape comparison, approved Linux-isolated extracted-module replay, managed-runtime characterization, reconstruction coverage closure, deterministic obligation ledgers, and end-to-end readiness conformance |
|
||||
| Workspace and observation | 22 | target lifecycle, inline Evidence bundle retrieval, aggregate navigation/address context, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
|
||||
| Tool family | Count | Examples |
|
||||
| ------------------------- | ----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native inspection | 36 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations, bounded byte reads, file-offset translation |
|
||||
| Investigation workflows | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `trace_feature`, exact string-to-code lookup, bounded call paths, call graphs, Swift and Objective-C discovery |
|
||||
| Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
|
||||
| Artifact graph | 2 | complete inline inspection of directories and supported packages, plus explicitly selected extraction into an absent owned tree |
|
||||
| Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
|
||||
| Browser observation | 9 | exact-origin passive CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, visual evidence, and bounded Playwright scenarios |
|
||||
| Electron analysis | 5 | passive root-confined observation, static application mapping, evidence-backed static/runtime reconciliation, and provider-owned click/wait scenarios |
|
||||
| JavaScript runtime | 2 | approved attach-only Node/Electron Inspector target discovery plus bounded script and execution-context observation without evaluation or instrumentation |
|
||||
| Application workflows | 10 | bounded cross-layer traces, unique-only version matching, historical-source to bundle mapping, static export return-shape comparison, approved Linux-isolated extracted-module replay, managed-runtime characterization, reconstruction coverage closure, and deterministic obligation ledgers |
|
||||
| Workspace and observation | 22 | target lifecycle, inline Evidence bundle retrieval, aggregate navigation/address context, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
|
||||
|
||||
The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
|
||||
|
||||
|
|
|
|||
24
README_ar.md
24
README_ar.md
|
|
@ -159,18 +159,18 @@ rea uninstall --purge-data
|
|||
|
||||
## منصة أدوات التحقيق
|
||||
|
||||
| عائلة الأدوات | العدد | أمثلة |
|
||||
| --------------------- | ----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| فحص الملفات التنفيذية | 36 | الدوال، والشيفرة شبه المصدرية، والتعليمات، والسلاسل، والأسماء، والمراجع، والتعليقات |
|
||||
| التحليل المركب | 13 | النظرة العامة، و`inspect_native_api`، وفك الترجمة الدفعي، ومخططات الاستدعاء، والمراجع، واكتشاف الأنواع |
|
||||
| أدوات macOS الأصلية | 5 | بيانات Mach-O والتوقيعات وملفات plist والمعماريات وفك رموز Swift دون تشغيل Hopper |
|
||||
| رسم القطع الأثرية | 3 | جرد حتمي للدلائل وZIP/APK/IPA وASAR واستخراج معاملاتي محدد صراحة |
|
||||
| Managed PE/CLI | 8 | هوية PE/CLI، أعضاء البيانات الوصفية، بصمات CIL، تصريحات P/Invoke/الحدود الأصلية والتحقق منها، إسقاط رسم التطبيق، استيراد إعادة البناء من فك الترجمة، إعادة ربط الرموز، خطط ترابط وقت التشغيل ومقارنة الإصدارات |
|
||||
| مراقبة المتصفح | 9 | التقاط CDP محدود الأصل وتحليل الحزم وخرائط المصدر واكتشاف WebMCP والفروق والأدلة المرئية |
|
||||
| تحليل Electron | 5 | مراقبة سلبية ضمن جذور canonical، ورسم ثابت ومحدود للتطبيق، ومطابقة ثابتة/وقت تشغيل قائمة على Evidence |
|
||||
| وقت تشغيل JavaScript | 2 | اكتشاف أهداف Node/Electron Inspector المعتمدة للاتصال فقط، ومراقبة محدودة للبرامج النصية وسياقات التنفيذ دون تقييم أو تجهيز |
|
||||
| سير عمل التطبيقات | 11 | تتبع محدود ومقارنة إصدارات فريدة ومقارنة ثابتة لأشكال قيم إرجاع الصادرات وإعادة تشغيل وحدات معزولة ومعتمدة على Linux وتوصيف وقت التشغيل المُدار وإغلاق تغطية إعادة البناء ودفاتر التزامات حتمية والتحقق الشامل من جاهزية إعادة البناء |
|
||||
| جلسة الملف التنفيذي | 22 | فتح الأهداف وحزم الأدلة وتشغيل آلة إعادة محدودة ومقارنة العمليات والقطع والدوال وسجل المجهولات المتبقية |
|
||||
| عائلة الأدوات | العدد | أمثلة |
|
||||
| --------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| فحص الملفات التنفيذية | 36 | الدوال، والشيفرة شبه المصدرية، والتعليمات، والسلاسل، والأسماء، والمراجع، والتعليقات |
|
||||
| التحليل المركب | 13 | النظرة العامة، و`inspect_native_api`، وفك الترجمة الدفعي، ومخططات الاستدعاء، والمراجع، واكتشاف الأنواع |
|
||||
| أدوات macOS الأصلية | 5 | بيانات Mach-O والتوقيعات وملفات plist والمعماريات وفك رموز Swift دون تشغيل Hopper |
|
||||
| رسم القطع الأثرية | 2 | فحص كامل مباشر للدلائل والحزم المدعومة، واستخراج معاملاتي محدد صراحة |
|
||||
| Managed PE/CLI | 8 | هوية PE/CLI، أعضاء البيانات الوصفية، بصمات CIL، تصريحات P/Invoke/الحدود الأصلية والتحقق منها، إسقاط رسم التطبيق، استيراد إعادة البناء من فك الترجمة، إعادة ربط الرموز، خطط ترابط وقت التشغيل ومقارنة الإصدارات |
|
||||
| مراقبة المتصفح | 9 | التقاط CDP محدود الأصل وتحليل الحزم وخرائط المصدر واكتشاف WebMCP والفروق والأدلة المرئية |
|
||||
| تحليل Electron | 5 | مراقبة سلبية ضمن جذور canonical، ورسم ثابت ومحدود للتطبيق، ومطابقة ثابتة/وقت تشغيل قائمة على Evidence |
|
||||
| وقت تشغيل JavaScript | 2 | اكتشاف أهداف Node/Electron Inspector المعتمدة للاتصال فقط، ومراقبة محدودة للبرامج النصية وسياقات التنفيذ دون تقييم أو تجهيز |
|
||||
| سير عمل التطبيقات | 10 | تتبع محدود ومقارنة إصدارات فريدة ومقارنة ثابتة لأشكال قيم إرجاع الصادرات وإعادة تشغيل وحدات معزولة ومعتمدة على Linux وتوصيف وقت التشغيل المُدار وإغلاق تغطية إعادة البناء ودفاتر التزامات حتمية |
|
||||
| جلسة الملف التنفيذي | 22 | فتح الأهداف وحزم الأدلة وتشغيل آلة إعادة محدودة ومقارنة العمليات والقطع والدوال وسجل المجهولات المتبقية |
|
||||
|
||||
## استخدام REA مع وكلاء برمجة آخرين
|
||||
|
||||
|
|
|
|||
24
README_ja.md
24
README_ja.md
|
|
@ -149,18 +149,18 @@ REA は手順 1〜5 のバイナリ解析を処理し、手順 6 はエージェ
|
|||
|
||||
## 調査ツールカタログ
|
||||
|
||||
| ツール群 | 数 | 例 |
|
||||
| ---------------------- | --: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| バイナリ調査 | 36 | プロシージャ、疑似コード、アセンブリ、文字列、名前、セグメント、callers、callees、xrefs、注釈 |
|
||||
| 合成解析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift / ObjC 検出 |
|
||||
| macOS ネイティブ | 5 | Mach-O メタデータ、署名、plist、アーキテクチャ、Swift デマングル。Hopper 起動不要 |
|
||||
| アーティファクトグラフ | 3 | ディレクトリ、ZIP/APK/IPA、ASAR の決定的一覧と明示選択されたトランザクション抽出 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 識別、メタデータメンバー、CIL ハッシュ、P/Invoke/ネイティブ境界宣言と検証、アプリケーショングラフ投影、逆コンパイル再構成インポート、構造的 token 再マッピング、ランタイム相関計画、バージョン比較 |
|
||||
| ブラウザ観察 | 9 | origin 限定 CDP 取得、bundle/source map 解析、WebMCP 検出、セッション、capture diff、視覚証拠 |
|
||||
| Electron 解析 | 5 | canonical ファイルルート内の受動観察、有界な静的アプリマッピング、Evidence に基づく静的/実行時の照合 |
|
||||
| JavaScript ランタイム | 2 | 承認済みのアタッチ専用 Node/Electron Inspector ターゲット検出と、評価や計装を行わない有界なスクリプト/実行コンテキスト観察 |
|
||||
| アプリケーション | 11 | 有界なクロスレイヤー追跡、一意限定のバージョン比較、静的なエクスポート戻り値形状比較、承認済み Linux 隔離 extracted-module replay、マネージドランタイム特性評価、再構築カバレッジ閉包、決定論的な義務台帳、エンドツーエンドの準備適合性検証 |
|
||||
| バイナリセッション | 22 | `open_binary`、`binary_session`、証拠バンドル、有限リプレイ状態機械、プロセス/成果物/関数比較、残余未知レジストリ |
|
||||
| ツール群 | 数 | 例 |
|
||||
| ---------------------- | --: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| バイナリ調査 | 36 | プロシージャ、疑似コード、アセンブリ、文字列、名前、セグメント、callers、callees、xrefs、注釈 |
|
||||
| 合成解析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift / ObjC 検出 |
|
||||
| macOS ネイティブ | 5 | Mach-O メタデータ、署名、plist、アーキテクチャ、Swift デマングル。Hopper 起動不要 |
|
||||
| アーティファクトグラフ | 2 | ディレクトリと対応パッケージの完全なインライン調査、および明示選択されたトランザクション抽出 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 識別、メタデータメンバー、CIL ハッシュ、P/Invoke/ネイティブ境界宣言と検証、アプリケーショングラフ投影、逆コンパイル再構成インポート、構造的 token 再マッピング、ランタイム相関計画、バージョン比較 |
|
||||
| ブラウザ観察 | 9 | origin 限定 CDP 取得、bundle/source map 解析、WebMCP 検出、セッション、capture diff、視覚証拠 |
|
||||
| Electron 解析 | 5 | canonical ファイルルート内の受動観察、有界な静的アプリマッピング、Evidence に基づく静的/実行時の照合 |
|
||||
| JavaScript ランタイム | 2 | 承認済みのアタッチ専用 Node/Electron Inspector ターゲット検出と、評価や計装を行わない有界なスクリプト/実行コンテキスト観察 |
|
||||
| アプリケーション | 10 | 有界なクロスレイヤー追跡、一意限定のバージョン比較、静的なエクスポート戻り値形状比較、承認済み Linux 隔離 extracted-module replay、マネージドランタイム特性評価、再構築カバレッジ閉包、決定論的な義務台帳 |
|
||||
| バイナリセッション | 22 | `open_binary`、`binary_session`、証拠バンドル、有限リプレイ状態機械、プロセス/成果物/関数比較、残余未知レジストリ |
|
||||
|
||||
## 他のコーディングエージェントで使う
|
||||
|
||||
|
|
|
|||
24
README_ko.md
24
README_ko.md
|
|
@ -149,18 +149,18 @@ REA는 1–5단계의 바이너리 분석을 처리합니다. 6단계는 에이
|
|||
|
||||
## 조사 도구 카탈로그
|
||||
|
||||
| 도구 그룹 | 수 | 예시 |
|
||||
| ------------------- | --: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 바이너리 검사 | 36 | 프로시저, 의사 코드, 어셈블리, 문자열, 이름, 세그먼트, callers, callees, xrefs, 주석 |
|
||||
| 합성 분석 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift 및 ObjC 탐색 |
|
||||
| macOS 네이티브 도구 | 5 | Mach-O 메타데이터, 코드 서명, plist, 아키텍처, Swift 디맹글링. Hopper 실행 불필요 |
|
||||
| 아티팩트 그래프 | 3 | 디렉터리, ZIP/APK/IPA, ASAR 결정적 인벤토리와 명시적으로 선택한 트랜잭션 추출 |
|
||||
| Managed PE/CLI | 8 | PE/CLI ID, 메타데이터 멤버, CIL 해시, P/Invoke/네이티브 경계 선언 및 검증, 애플리케이션 그래프 투영, 디컴파일 재구성 가져오기, 구조적 token 재매핑, 런타임 상관 계획, 버전 비교 |
|
||||
| 브라우저 관찰 | 9 | origin 제한 CDP 캡처, bundle/source map 분석, WebMCP 검색, 세션 타임라인, capture diff 및 시각 증거 |
|
||||
| Electron 분석 | 5 | canonical 파일 루트 내 수동 관찰, 제한된 정적 앱 매핑, Evidence 기반 정적/런타임 조정 |
|
||||
| JavaScript 런타임 | 2 | 승인된 연결 전용 Node/Electron Inspector 대상 탐색과 평가·계측 없는 제한된 스크립트 및 실행 컨텍스트 관찰 |
|
||||
| 애플리케이션 | 11 | 제한된 교차 계층 추적, 고유 일치 버전 비교, 정적 export 반환 형태 비교, 승인된 Linux 격리 extracted-module replay, 관리형 런타임 특성화, 재구성 커버리지 폐쇄, 결정론적 의무 원장, 엔드투엔드 준비 적합성 검증 |
|
||||
| 바이너리 세션 | 22 | `open_binary`, `binary_session`, 증거 번들, 유한 replay 상태 머신, 프로세스·아티팩트·함수 비교, 잔여 미지수 레지스트리 |
|
||||
| 도구 그룹 | 수 | 예시 |
|
||||
| ------------------- | --: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 바이너리 검사 | 36 | 프로시저, 의사 코드, 어셈블리, 문자열, 이름, 세그먼트, callers, callees, xrefs, 주석 |
|
||||
| 합성 분석 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift 및 ObjC 탐색 |
|
||||
| macOS 네이티브 도구 | 5 | Mach-O 메타데이터, 코드 서명, plist, 아키텍처, Swift 디맹글링. Hopper 실행 불필요 |
|
||||
| 아티팩트 그래프 | 2 | 디렉터리와 지원 패키지의 전체 인라인 검사 및 명시적으로 선택한 트랜잭션 추출 |
|
||||
| Managed PE/CLI | 8 | PE/CLI ID, 메타데이터 멤버, CIL 해시, P/Invoke/네이티브 경계 선언 및 검증, 애플리케이션 그래프 투영, 디컴파일 재구성 가져오기, 구조적 token 재매핑, 런타임 상관 계획, 버전 비교 |
|
||||
| 브라우저 관찰 | 9 | origin 제한 CDP 캡처, bundle/source map 분석, WebMCP 검색, 세션 타임라인, capture diff 및 시각 증거 |
|
||||
| Electron 분석 | 5 | canonical 파일 루트 내 수동 관찰, 제한된 정적 앱 매핑, Evidence 기반 정적/런타임 조정 |
|
||||
| JavaScript 런타임 | 2 | 승인된 연결 전용 Node/Electron Inspector 대상 탐색과 평가·계측 없는 제한된 스크립트 및 실행 컨텍스트 관찰 |
|
||||
| 애플리케이션 | 10 | 제한된 교차 계층 추적, 고유 일치 버전 비교, 정적 export 반환 형태 비교, 승인된 Linux 격리 extracted-module replay, 관리형 런타임 특성화, 재구성 커버리지 폐쇄, 결정론적 의무 원장 |
|
||||
| 바이너리 세션 | 22 | `open_binary`, `binary_session`, 증거 번들, 유한 replay 상태 머신, 프로세스·아티팩트·함수 비교, 잔여 미지수 레지스트리 |
|
||||
|
||||
## 다른 코딩 에이전트에서 사용하기
|
||||
|
||||
|
|
|
|||
24
README_zh.md
24
README_zh.md
|
|
@ -149,18 +149,18 @@ REA 负责第 1–5 步中的二进制分析。第 6 步由智能体使用其常
|
|||
|
||||
## 调查工具目录
|
||||
|
||||
| 工具类别 | 数量 | 示例 |
|
||||
| ----------------- | ---: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 二进制检查 | 36 | 过程、伪代码、汇编、字符串、名称、段、调用者、被调用者、交叉引用、注释 |
|
||||
| 组合分析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`、Swift 与 ObjC 发现 |
|
||||
| macOS 原生工具 | 5 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原,无需启动 Hopper |
|
||||
| 制品图谱 | 3 | 确定性目录、ZIP/APK/IPA 与 ASAR 清单,以及显式选择的事务式提取 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 身份、元数据成员、CIL 哈希、P/Invoke/原生边界声明与验证、应用图谱投影、反编译重建导入、结构化 token 重映射、运行时关联计划与跨版本比较 |
|
||||
| 浏览器观察 | 9 | 精确来源 CDP 抓取、bundle/source map 分析、WebMCP 发现、会话时间线、capture diff 与视觉证据 |
|
||||
| Electron 分析 | 5 | 限定 canonical 文件根的被动观察、有界静态应用映射,以及基于 Evidence 的静态/运行时对账 |
|
||||
| JavaScript 运行时 | 2 | 经批准的只附加 Node/Electron Inspector 目标发现,以及不执行求值或插桩的有界脚本和执行上下文观察 |
|
||||
| 应用工作流 | 11 | 有界跨层追踪、唯一匹配跨版本图比较、静态导出返回形状比较、经批准的 Linux 隔离 extracted-module replay、托管运行时表征、重建覆盖闭合、确定性义务账本和端到端就绪性一致性验证 |
|
||||
| 二进制会话 | 22 | `open_binary`、`binary_session`、证据包、有限重放状态机、进程、制品与函数比较、残余未知项注册表 |
|
||||
| 工具类别 | 数量 | 示例 |
|
||||
| ----------------- | ---: | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 二进制检查 | 36 | 过程、伪代码、汇编、字符串、名称、段、调用者、被调用者、交叉引用、注释 |
|
||||
| 组合分析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`、Swift 与 ObjC 发现 |
|
||||
| macOS 原生工具 | 5 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原,无需启动 Hopper |
|
||||
| 制品图谱 | 2 | 目录及支持的软件包的完整内联检查,以及显式选择的事务式提取 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 身份、元数据成员、CIL 哈希、P/Invoke/原生边界声明与验证、应用图谱投影、反编译重建导入、结构化 token 重映射、运行时关联计划与跨版本比较 |
|
||||
| 浏览器观察 | 9 | 精确来源 CDP 抓取、bundle/source map 分析、WebMCP 发现、会话时间线、capture diff 与视觉证据 |
|
||||
| Electron 分析 | 5 | 限定 canonical 文件根的被动观察、有界静态应用映射,以及基于 Evidence 的静态/运行时对账 |
|
||||
| JavaScript 运行时 | 2 | 经批准的只附加 Node/Electron Inspector 目标发现,以及不执行求值或插桩的有界脚本和执行上下文观察 |
|
||||
| 应用工作流 | 10 | 有界跨层追踪、唯一匹配跨版本图比较、静态导出返回形状比较、经批准的 Linux 隔离 extracted-module replay、托管运行时表征、重建覆盖闭合和确定性义务账本 |
|
||||
| 二进制会话 | 22 | `open_binary`、`binary_session`、证据包、有限重放状态机、进程、制品与函数比较、残余未知项注册表 |
|
||||
|
||||
## 与其他编程智能体一起使用
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ distinct. Requested prompt arguments and completion choices are rendered as
|
|||
untrusted selection data, not instructions or authorization.
|
||||
|
||||
For target paths in `compare_application_versions`, the rendered workflow
|
||||
opens each target before calling `inventory_artifact`; artifact inventory and
|
||||
inspection operate on the active target and do not accept a path themselves.
|
||||
opens each target before calling `inspect_artifact`; inspection operates on the
|
||||
active target and returns the graph and findings together inline.
|
||||
|
||||
Use standard MCP discovery and retrieval:
|
||||
|
||||
|
|
@ -49,11 +49,9 @@ cached prompt catalog.
|
|||
|
||||
## Session-aware completion
|
||||
|
||||
MCP `completion/complete` currently completes prompt arguments and resource
|
||||
template variables. It does not define completion for arbitrary tool-call
|
||||
arguments. REA therefore attaches completion to optional guided-prompt
|
||||
arguments that feed later tool selection; it does not claim protocol-level
|
||||
completion for every REA tool.
|
||||
MCP `completion/complete` completes guided-prompt arguments; it does not define
|
||||
completion for arbitrary tool-call arguments. REA attaches completion to
|
||||
optional prompt arguments that feed later tool selection.
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"skill_version": "23"
|
||||
},
|
||||
"tools": {
|
||||
"total": 114,
|
||||
"total": 112,
|
||||
"families": [
|
||||
{
|
||||
"id": "direct",
|
||||
|
|
@ -90,8 +90,8 @@
|
|||
{
|
||||
"id": "artifact",
|
||||
"surface": "artifact-provider",
|
||||
"count": 3,
|
||||
"tools": ["extract_artifact", "inspect_artifact", "inventory_artifact"]
|
||||
"count": 2,
|
||||
"tools": ["extract_artifact", "inspect_artifact"]
|
||||
},
|
||||
{
|
||||
"id": "managed",
|
||||
|
|
@ -148,14 +148,13 @@
|
|||
{
|
||||
"id": "application",
|
||||
"surface": "application-workflow",
|
||||
"count": 11,
|
||||
"count": 10,
|
||||
"tools": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"evaluate_reconstruction_readiness",
|
||||
"execute_node_characterization",
|
||||
"prepare_node_characterization",
|
||||
"run_controlled_replay",
|
||||
|
|
@ -281,11 +280,7 @@
|
|||
"id": "rea-artifact-graph",
|
||||
"name": "REA safe artifact graph provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inventory_artifact"
|
||||
]
|
||||
"capabilities": ["extract_artifact", "inspect_artifact"]
|
||||
},
|
||||
{
|
||||
"id": "rea-cdp-browser",
|
||||
|
|
@ -346,7 +341,6 @@
|
|||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"evaluate_reconstruction_readiness",
|
||||
"execute_node_characterization",
|
||||
"prepare_node_characterization",
|
||||
"run_controlled_replay",
|
||||
|
|
@ -427,7 +421,7 @@
|
|||
}
|
||||
],
|
||||
"cli": {
|
||||
"primary_count": 66,
|
||||
"primary_count": 64,
|
||||
"commands": [
|
||||
"analyze",
|
||||
"analyze-javascript-application",
|
||||
|
|
@ -451,7 +445,6 @@
|
|||
"discover-webmcp-tools",
|
||||
"doctor",
|
||||
"evaluate-reconstruction-coverage",
|
||||
"evaluate-reconstruction-readiness",
|
||||
"evidence-export",
|
||||
"evidence-import",
|
||||
"execute-node-characterization",
|
||||
|
|
@ -471,7 +464,6 @@
|
|||
"inspect-signature",
|
||||
"inspect-web-page",
|
||||
"instructions",
|
||||
"inventory-artifact",
|
||||
"list-architectures",
|
||||
"list-browser-targets",
|
||||
"list-electron-targets",
|
||||
|
|
@ -505,15 +497,15 @@
|
|||
},
|
||||
"runtime_catalog": {
|
||||
"counts": {
|
||||
"cli_commands": 66,
|
||||
"mcp_tools": 114,
|
||||
"cli_commands": 64,
|
||||
"mcp_tools": 112,
|
||||
"mcp_prompts": 6
|
||||
},
|
||||
"digests": {
|
||||
"tools_sha256": "6b56b65d4d0b16365617dd2fcd08d8254ddd309b000fa0d173512f544441d17f",
|
||||
"prompts_sha256": "77c76df62f92484a43cebf5ca110bdf562c38fbee2531d293eeb51d6e712d459",
|
||||
"combined_sha256": "e9e72f81202fc2c0e19fef2b66ec015db90c208005cac9afff90410ce7efd531",
|
||||
"providers_sha256": "919047a899143c606e521b3b0437c3cd494368504672e4f7423521ab3651f843"
|
||||
"tools_sha256": "82f634088db343feeabf4ea210c08ed83f29f237c4c21026c5a8fce1c59d7250",
|
||||
"prompts_sha256": "00b328caadcfac801f42c29caf174fb04b899937390f7ba8c7d40851b63f712b",
|
||||
"combined_sha256": "6a20b6b76be7cf9ced6e6b71ca435e3ec6eb6f910694f695f2a2627def628036",
|
||||
"providers_sha256": "7247eece8fad09c8023af8d5c682d223d7ed7ebd68eac79e9154962a7e03b66b"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,32 +11,49 @@ export async function verifyPackageArtifactAndElectron({
|
|||
workspace,
|
||||
environment,
|
||||
}) {
|
||||
const artifactArchive = await verifyPackagedArtifact({
|
||||
cli,
|
||||
workspace,
|
||||
environment,
|
||||
});
|
||||
await verifyPackagedElectronApplication({
|
||||
cli,
|
||||
workspace,
|
||||
environment,
|
||||
});
|
||||
return { artifactArchive };
|
||||
}
|
||||
|
||||
const verifyPackagedArtifact = async ({ cli, workspace, environment }) => {
|
||||
const artifactArchive = join(workspace, "artifact.zip");
|
||||
const artifactWriter = new ZipWriter(new Uint8ArrayWriter());
|
||||
await artifactWriter.add("app/main.js", new TextReader("main();"));
|
||||
await writeFile(artifactArchive, await artifactWriter.close());
|
||||
const artifactInventory = json(
|
||||
const artifactInspection = json(
|
||||
await run(
|
||||
cli,
|
||||
["inventory-artifact", artifactArchive, "--limit", "500", "--json"],
|
||||
["inspect-artifact", artifactArchive, "--json"],
|
||||
environment,
|
||||
),
|
||||
);
|
||||
const repeatedArtifactInventory = json(
|
||||
const repeatedArtifactInspection = json(
|
||||
await run(
|
||||
cli,
|
||||
["inventory-artifact", artifactArchive, "--limit", "500", "--json"],
|
||||
["inspect-artifact", artifactArchive, "--json"],
|
||||
environment,
|
||||
),
|
||||
);
|
||||
const artifactInventory =
|
||||
artifactInspection.normalized_result?.substeps?.[0]?.evidence
|
||||
?.normalized_result;
|
||||
const repeatedArtifactInventory =
|
||||
repeatedArtifactInspection.normalized_result?.substeps?.[0]?.evidence
|
||||
?.normalized_result;
|
||||
if (
|
||||
artifactInventory.operation !== "inventory_artifact" ||
|
||||
artifactInventory.provider?.id !== "rea-artifact-graph" ||
|
||||
artifactInventory.normalized_result?.manifest?.root_format !== "zip" ||
|
||||
!sameArtifactIdentity(
|
||||
artifactInventory.normalized_result,
|
||||
repeatedArtifactInventory.normalized_result,
|
||||
)
|
||||
artifactInspection.operation !== "inspect_artifact" ||
|
||||
artifactInspection.provider?.id !== "rea-artifact-graph" ||
|
||||
artifactInventory?.manifest?.root_format !== "zip" ||
|
||||
!sameArtifactIdentity(artifactInventory, repeatedArtifactInventory)
|
||||
)
|
||||
throw new Error("packaged artifact inventory CLI failed");
|
||||
await verifyPackagedArtifactExtraction({
|
||||
|
|
@ -46,6 +63,14 @@ export async function verifyPackageArtifactAndElectron({
|
|||
environment,
|
||||
workspace,
|
||||
});
|
||||
return artifactArchive;
|
||||
};
|
||||
|
||||
const verifyPackagedElectronApplication = async ({
|
||||
cli,
|
||||
workspace,
|
||||
environment,
|
||||
}) => {
|
||||
const applicationRoot = join(workspace, "electron-app");
|
||||
await mkdir(applicationRoot);
|
||||
await writeFile(
|
||||
|
|
@ -88,9 +113,7 @@ export async function verifyPackageArtifactAndElectron({
|
|||
),
|
||||
);
|
||||
assertRoutedApplicationAnalysis(routedApplicationAnalysis);
|
||||
|
||||
return { artifactArchive };
|
||||
}
|
||||
};
|
||||
|
||||
const sameArtifactIdentity = (left, right) =>
|
||||
isDeepStrictEqual(left?.manifest, right?.manifest) &&
|
||||
|
|
@ -105,10 +128,9 @@ const verifyPackagedArtifactExtraction = async ({
|
|||
environment,
|
||||
workspace,
|
||||
}) => {
|
||||
const occurrence =
|
||||
artifactInventory.normalized_result?.occurrences?.items?.find(
|
||||
({ logical_path: path }) => path === "app/main.js",
|
||||
);
|
||||
const occurrence = artifactInventory?.occurrences?.items?.find(
|
||||
({ logical_path: path }) => path === "app/main.js",
|
||||
);
|
||||
if (occurrence?.occurrence_id === undefined)
|
||||
throw new Error("packaged artifact inventory omitted the selected member");
|
||||
const outputRoot = join(workspace, "artifact-extraction");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { json, run, runWithStatus } from "./lib/verify-package-core.mjs";
|
|||
const REQUIRED_HELP_COMMANDS = [
|
||||
"setup",
|
||||
"upgrade",
|
||||
"inventory-artifact",
|
||||
"inspect-artifact",
|
||||
"extract-artifact",
|
||||
"import-reference-source",
|
||||
"list-browser-targets",
|
||||
|
|
@ -21,7 +21,7 @@ const REQUIRED_LLM_TOPICS = [
|
|||
"trace",
|
||||
"capabilities",
|
||||
"providers",
|
||||
"inventory-artifact",
|
||||
"inspect-artifact",
|
||||
"list-browser-targets",
|
||||
"inspect-web-page",
|
||||
"analyze-javascript-application",
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ name: reverse-engineer-anything
|
|||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, and browser applications with REA. Use shipped-artifact or approved runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
|
||||
metadata:
|
||||
version: "23"
|
||||
tool_count: 114
|
||||
catalog_digest: "e9e72f81202fc2c0e19fef2b66ec015db90c208005cac9afff90410ce7efd531"
|
||||
tool_count: 112
|
||||
catalog_digest: "6a20b6b76be7cf9ced6e6b71ca435e3ec6eb6f910694f695f2a2627def628036"
|
||||
---
|
||||
|
||||
# REA
|
||||
|
|
@ -23,8 +23,7 @@ Choose the first tool from the target the user supplied. Do not call
|
|||
- ASAR or extracted JavaScript/Electron tree:
|
||||
`analyze_javascript_application`.
|
||||
- Archive, application package, ZIP/APK/IPA/MSIX/AppX, or DMG:
|
||||
`open_binary` with the supplied local path, then `inspect_artifact` or
|
||||
`inventory_artifact` (both operate on the active target and accept no path).
|
||||
`open_binary` with the supplied local path, then `inspect_artifact`.
|
||||
- Managed PE/CLI assembly: `inspect_managed_artifact`.
|
||||
- User-owned browser page already open: `list_browser_targets`.
|
||||
- User-owned Electron runtime already open: `list_electron_targets`.
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ parser and must not become an implicit setup dependency.
|
|||
|
||||
## Packages and extraction
|
||||
|
||||
Call `inventory_artifact` before extraction for application bundles, archives,
|
||||
ZIP/APK/IPA/MSIX/AppX, ASAR, or DMG inputs. Continue deterministic occurrence
|
||||
pages from the returned offset only when needed. Cite graph manifest IDs.
|
||||
Call `inspect_artifact` before extraction for application bundles, archives,
|
||||
ZIP/APK/IPA/MSIX/AppX, ASAR, or DMG inputs. It returns the complete artifact
|
||||
graph inline. Cite graph manifest IDs.
|
||||
|
||||
`extract_artifact` requires explicit approval, an absent absolute output root,
|
||||
and selected occurrence IDs. Never extract every entry implicitly. Symlinks and
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { BinaryTarget } from "../domain/binaryTarget.js";
|
|||
import type { OfficialToolName } from "../contracts/toolContracts.js";
|
||||
import type { EnhancedToolName } from "../contracts/enhancedInputs.js";
|
||||
import type { NativeToolName } from "../contracts/nativeToolContracts.js";
|
||||
import type { ArtifactToolName } from "../contracts/artifactToolContracts.js";
|
||||
import type { ArtifactAnalysisOperation } from "../contracts/artifactToolContracts.js";
|
||||
import type { ManagedToolName } from "../contracts/managedToolContracts.js";
|
||||
import type { AnalysisError } from "../domain/errors.js";
|
||||
import type { AnalysisProfileCommitment } from "../domain/analysisProfile.js";
|
||||
|
|
@ -25,7 +25,7 @@ export type AnalysisOperation =
|
|||
| OfficialToolName
|
||||
| EnhancedToolName
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| "health";
|
||||
|
||||
|
|
|
|||
|
|
@ -126,19 +126,23 @@ export const resolveCompareApplicationVersionsRequestValidated = (
|
|||
input: CompareApplicationVersionsRequest,
|
||||
lookup?: EvidenceLookup,
|
||||
): Result<ComparisonInput, AnalysisError> => {
|
||||
const left = graphEvidence(
|
||||
"left" in input
|
||||
? { application: input.left }
|
||||
: { application_evidence_id: input.left_evidence_id },
|
||||
lookup,
|
||||
);
|
||||
const leftReference = input.left ?? input.left_evidence_id;
|
||||
if (leftReference === undefined)
|
||||
return err(
|
||||
new AnalysisInputError("compare_application_versions", undefined, [
|
||||
{ path: ["left"], reason: "missing_argument" },
|
||||
]),
|
||||
);
|
||||
const left = resolveApplicationGraphReference(leftReference, lookup);
|
||||
if (!left.ok) return left;
|
||||
const right = graphEvidence(
|
||||
"right" in input
|
||||
? { application: input.right }
|
||||
: { application_evidence_id: input.right_evidence_id },
|
||||
lookup,
|
||||
);
|
||||
const rightReference = input.right ?? input.right_evidence_id;
|
||||
if (rightReference === undefined)
|
||||
return err(
|
||||
new AnalysisInputError("compare_application_versions", undefined, [
|
||||
{ path: ["right"], reason: "missing_argument" },
|
||||
]),
|
||||
);
|
||||
const right = resolveApplicationGraphReference(rightReference, lookup);
|
||||
if (!right.ok) return right;
|
||||
const leftNative = resolveEvidenceReferences(
|
||||
lookup,
|
||||
|
|
@ -265,6 +269,14 @@ const graphEvidence = (
|
|||
return resolved.ok ? ok(resolved.value[0]) : resolved;
|
||||
};
|
||||
|
||||
const resolveApplicationGraphReference = (
|
||||
reference: Evidence | string,
|
||||
lookup: EvidenceLookup | undefined,
|
||||
) =>
|
||||
typeof reference === "string"
|
||||
? graphEvidence({ application_evidence_id: reference }, lookup)
|
||||
: graphEvidence({ application: reference }, lookup);
|
||||
|
||||
const invalid = <Value>(
|
||||
operation: string,
|
||||
cause: z.ZodError,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export const binarySessionStatus = ({
|
|||
const provider = providerSummary(configuredProvider);
|
||||
const providers = router.providerIdentities(route).map(providerSummary);
|
||||
const capabilities = [...(route.capabilities?.values() ?? [])]
|
||||
.filter(({ operation }) => operation !== "inventory_artifact")
|
||||
.sort((left, right) => left.operation.localeCompare(right.operation))
|
||||
.map((descriptor) => {
|
||||
const unavailable = runtimeUnavailability.get(descriptor.operation);
|
||||
|
|
@ -66,6 +67,7 @@ export const binarySessionStatus = ({
|
|||
selected: candidate.selected,
|
||||
capabilities: candidate.capabilities
|
||||
.slice()
|
||||
.filter(({ operation }) => operation !== "inventory_artifact")
|
||||
.sort((left, right) => left.operation.localeCompare(right.operation))
|
||||
.map(capabilityStatus),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { silentLogger, type Logger } from "../logger.js";
|
|||
import { createEvidence } from "../domain/evidence.js";
|
||||
import type { Evidence } from "../domain/evidence.js";
|
||||
import type { NativeToolName } from "../contracts/nativeToolContracts.js";
|
||||
import type { ArtifactToolName } from "../contracts/artifactToolContracts.js";
|
||||
import type { ArtifactAnalysisOperation } from "../contracts/artifactToolContracts.js";
|
||||
import type { ManagedToolName } from "../contracts/managedToolContracts.js";
|
||||
import {
|
||||
EvidenceIntegrityError,
|
||||
|
|
@ -92,7 +92,7 @@ export const runDirectAnalysis = async (
|
|||
export const runProviderAnalysis = async (
|
||||
...[path, tool, arguments_, logger = silentLogger, signal]: readonly [
|
||||
path: string,
|
||||
tool: NativeToolName | ArtifactToolName | ManagedToolName,
|
||||
tool: NativeToolName | ArtifactAnalysisOperation | ManagedToolName,
|
||||
arguments_: Readonly<Record<string, JsonValue>>,
|
||||
logger?: Logger,
|
||||
signal?: AbortSignal,
|
||||
|
|
@ -110,7 +110,7 @@ const authorizeAnalysis = async (
|
|||
authority: PermissionAuthority,
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
arguments_: Readonly<Record<string, JsonValue>>,
|
||||
|
|
@ -170,7 +170,7 @@ const authorizeSnapshotAccess = async (
|
|||
authority: PermissionAuthority,
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
snapshotPath: string | undefined,
|
||||
|
|
@ -196,7 +196,7 @@ const authorizeAnalysisRun = async (input: {
|
|||
readonly suppliedAuthority: PermissionAuthority | undefined;
|
||||
readonly tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool;
|
||||
readonly arguments: Readonly<Record<string, JsonValue>>;
|
||||
|
|
@ -226,7 +226,7 @@ const runAnalysis = async (
|
|||
path: string,
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
arguments_: Readonly<Record<string, JsonValue>>,
|
||||
|
|
@ -316,7 +316,7 @@ const executeAnalysisTool = async (input: {
|
|||
readonly openedTarget: BinaryTarget;
|
||||
readonly tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool;
|
||||
readonly arguments: Readonly<Record<string, JsonValue>>;
|
||||
|
|
@ -421,7 +421,7 @@ const replayProviderFor = (
|
|||
session: ReturnType<typeof createBinarySession>,
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
) =>
|
||||
|
|
@ -433,7 +433,7 @@ const analysisProfileForEvidence = (
|
|||
session: ReturnType<typeof createBinarySession>,
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
): AnalysisProfileCommitment | undefined => {
|
||||
|
|
@ -445,7 +445,7 @@ const analysisProfileForEvidence = (
|
|||
const isWorkflowEvidenceTool = (
|
||||
tool:
|
||||
| NativeToolName
|
||||
| ArtifactToolName
|
||||
| ArtifactAnalysisOperation
|
||||
| ManagedToolName
|
||||
| DirectAnalysisTool,
|
||||
): boolean =>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ import {
|
|||
import { inventoryArtifactFully } from "../application/ArtifactInventory.js";
|
||||
import { extractArtifact } from "../application/ArtifactExtraction.js";
|
||||
import {
|
||||
ARTIFACT_TOOL_CONTRACTS,
|
||||
artifactInspectionInputSchema,
|
||||
ARTIFACT_ANALYSIS_OPERATIONS,
|
||||
artifactInventoryInputSchema,
|
||||
artifactExtractionExecutionSchema,
|
||||
type ArtifactToolName,
|
||||
type ArtifactAnalysisOperation,
|
||||
} from "../contracts/artifactToolContracts.js";
|
||||
import type { BinaryTarget } from "../domain/binaryTarget.js";
|
||||
import {
|
||||
|
|
@ -46,10 +45,10 @@ export class ArtifactProvider implements AnalysisProvider {
|
|||
) {}
|
||||
|
||||
readonly #capabilities: readonly CapabilityDescriptor[] = Object.freeze(
|
||||
ARTIFACT_TOOL_CONTRACTS.map((contract) =>
|
||||
ARTIFACT_ANALYSIS_OPERATIONS.map((operation) =>
|
||||
Object.freeze({
|
||||
provider: IDENTITY,
|
||||
operation: contract.name,
|
||||
operation,
|
||||
available: true as const,
|
||||
reason: null,
|
||||
effects: Object.freeze({
|
||||
|
|
@ -57,7 +56,7 @@ export class ArtifactProvider implements AnalysisProvider {
|
|||
launchesProcess: true,
|
||||
mayShowUi: false,
|
||||
mayAccessNetwork: false,
|
||||
mayWriteFilesystem: contract.name === "extract_artifact",
|
||||
mayWriteFilesystem: operation === "extract_artifact",
|
||||
changesPermissions: false,
|
||||
requiresRoot: false,
|
||||
}),
|
||||
|
|
@ -169,7 +168,7 @@ class ArtifactClient implements AnalysisClient {
|
|||
parameters: Readonly<Record<string, JsonValue>>,
|
||||
options?: ExecutionOptions,
|
||||
) {
|
||||
const parsed = artifactInspectionInputSchema.parse(parameters);
|
||||
const parsed = artifactInventoryInputSchema.parse(parameters);
|
||||
const inventoryParameters = artifactInventoryInputSchema.parse({
|
||||
native_mount_approved: parsed.native_mount_approved,
|
||||
integrity_policy: parsed.integrity_policy,
|
||||
|
|
@ -256,11 +255,13 @@ const DEFAULT_ARTIFACT_LIMITS: ArtifactLimits = {
|
|||
|
||||
const isArtifactOperation = (
|
||||
operation: AnalysisOperation,
|
||||
): operation is ArtifactToolName =>
|
||||
ARTIFACT_TOOL_CONTRACTS.some(({ name }) => name === operation);
|
||||
): operation is ArtifactAnalysisOperation =>
|
||||
ARTIFACT_ANALYSIS_OPERATIONS.includes(
|
||||
operation as (typeof ARTIFACT_ANALYSIS_OPERATIONS)[number],
|
||||
);
|
||||
|
||||
const translateFailure = (
|
||||
operation: ArtifactToolName,
|
||||
operation: ArtifactAnalysisOperation,
|
||||
cause: unknown,
|
||||
): AnalysisError => {
|
||||
if (cause instanceof ArtifactReaderFailure)
|
||||
|
|
|
|||
|
|
@ -11,52 +11,10 @@ export const registerArtifactCommands = (
|
|||
cli: CliInstance,
|
||||
logger: Logger,
|
||||
): void => {
|
||||
registerInventoryCommand(cli, logger);
|
||||
registerInspectionCommand(cli, logger);
|
||||
registerExtractionCommand(cli, logger);
|
||||
};
|
||||
|
||||
const registerInventoryCommand = (cli: CliInstance, logger: Logger): void => {
|
||||
cli.command(CLI_COMMANDS.inventoryArtifact, {
|
||||
description: "Build a complete deterministic artifact graph",
|
||||
args: z.object({
|
||||
path: z.string().describe("Application or package path"),
|
||||
}),
|
||||
options: z.object({
|
||||
integrityPolicy: z
|
||||
.enum(["fail", "record-and-continue"])
|
||||
.default("fail")
|
||||
.describe("Behavior when declared artifact integrity does not match"),
|
||||
integrityContinueApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve continuing after recorded integrity mismatches"),
|
||||
nativeMountApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve read-only native mounting when required"),
|
||||
}),
|
||||
alias: {
|
||||
integrityPolicy: "integrity-policy",
|
||||
integrityContinueApproved: "integrity-continue-approved",
|
||||
nativeMountApproved: "native-mount-approved",
|
||||
},
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inventory-artifact", () =>
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inventory_artifact",
|
||||
{
|
||||
integrity_policy: options.integrityPolicy,
|
||||
integrity_continue_approved: options.integrityContinueApproved,
|
||||
native_mount_approved: options.nativeMountApproved,
|
||||
},
|
||||
logger,
|
||||
),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const registerExtractionCommand = (cli: CliInstance, logger: Logger): void => {
|
||||
cli.command(CLI_COMMANDS.extractArtifact, {
|
||||
description: "Extract explicitly selected artifact occurrences safely",
|
||||
|
|
@ -67,7 +25,6 @@ const registerExtractionCommand = (cli: CliInstance, logger: Logger): void => {
|
|||
occurrenceIds: z
|
||||
.array(z.string().regex(/^occ_[a-f0-9]{64}$/u))
|
||||
.min(1)
|
||||
.max(500)
|
||||
.describe("Exact artifact occurrence IDs selected for extraction"),
|
||||
}),
|
||||
alias: { occurrenceIds: "occurrence-ids" },
|
||||
|
|
@ -93,10 +50,37 @@ const registerInspectionCommand = (cli: CliInstance, logger: Logger): void => {
|
|||
args: z.object({
|
||||
path: z.string().describe("Application or package path"),
|
||||
}),
|
||||
options: z.object({}),
|
||||
run: ({ args }) =>
|
||||
options: z.object({
|
||||
integrityPolicy: z
|
||||
.enum(["fail", "record-and-continue"])
|
||||
.default("fail")
|
||||
.describe("Behavior when declared artifact integrity does not match"),
|
||||
integrityContinueApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve continuing after recorded integrity mismatches"),
|
||||
nativeMountApproved: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.describe("Approve read-only native mounting when required"),
|
||||
}),
|
||||
alias: {
|
||||
integrityPolicy: "integrity-policy",
|
||||
integrityContinueApproved: "integrity-continue-approved",
|
||||
nativeMountApproved: "native-mount-approved",
|
||||
},
|
||||
run: ({ args, options }) =>
|
||||
logCliCommand(logger, "inspect-artifact", () =>
|
||||
runProviderAnalysis(args.path, "inspect_artifact", {}, logger),
|
||||
runProviderAnalysis(
|
||||
args.path,
|
||||
"inspect_artifact",
|
||||
{
|
||||
integrity_policy: options.integrityPolicy,
|
||||
integrity_continue_approved: options.integrityContinueApproved,
|
||||
native_mount_approved: options.nativeMountApproved,
|
||||
},
|
||||
logger,
|
||||
),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,18 +35,8 @@ export const artifactExtractionInputSchema = z.object({
|
|||
export const artifactExtractionExecutionSchema =
|
||||
artifactExtractionInputSchema.extend({ output_root: z.string().min(1) });
|
||||
|
||||
/** Bounded provider-neutral inspection using one atomic inventory substep. */
|
||||
const artifactInspectionFacts = {
|
||||
native_mount_approved: z.boolean().default(false),
|
||||
} as const;
|
||||
export const artifactInspectionInputSchema = z.union([
|
||||
z.object({ ...artifactInspectionFacts, ...integrityInput.fail }),
|
||||
z.object({ ...artifactInspectionFacts, ...integrityInput.continue }),
|
||||
]);
|
||||
|
||||
const exampleInputSchema = z.record(z.string(), jsonValueSchema);
|
||||
const examples: Readonly<Record<string, Readonly<Record<string, unknown>>>> = {
|
||||
inventory_artifact: {},
|
||||
inspect_artifact: {},
|
||||
extract_artifact: {
|
||||
occurrence_ids: [`occ_${"0".repeat(64)}`],
|
||||
|
|
@ -80,15 +70,10 @@ const artifact = <
|
|||
|
||||
/** Artifact-graph inventory and safe extraction contracts. */
|
||||
export const ARTIFACT_TOOL_CONTRACTS = [
|
||||
artifact(
|
||||
"inventory_artifact",
|
||||
"After open_binary binds a local archive, application package, or other artifact—or when one is already active—inventory the complete content-addressed artifact graph in one call. This tool accepts no path; in a target-free session open the target first. It does not extract or mount by default.",
|
||||
artifactInventoryInputSchema,
|
||||
),
|
||||
artifact(
|
||||
"inspect_artifact",
|
||||
"After open_binary binds a local archive, application package, or other artifact—or when one is already active—inspect the complete artifact inventory in one cancellable substep. This tool accepts no path; in a target-free session open the target first. Returns the full substep Evidence, observations, derived relationships, hypotheses, contradictions, unexplored branches, limitations, and format-specific next probes. Any substep failure fails the whole call.",
|
||||
artifactInspectionInputSchema,
|
||||
"Inspect an active archive or application package in one call. Returns the complete content-addressed artifact graph, source Evidence, observations, relationships, integrity contradictions, hypotheses, unexplored branches, limitations, and next probes inline. It does not extract files; use extract_artifact for selected occurrences.",
|
||||
artifactInventoryInputSchema,
|
||||
),
|
||||
artifact(
|
||||
"extract_artifact",
|
||||
|
|
@ -97,5 +82,12 @@ export const ARTIFACT_TOOL_CONTRACTS = [
|
|||
),
|
||||
] as const satisfies readonly ToolContract[];
|
||||
|
||||
/** Names of provider-neutral artifact graph operations. */
|
||||
export type ArtifactToolName = (typeof ARTIFACT_TOOL_CONTRACTS)[number]["name"];
|
||||
/** Provider operations; inventory remains an internal primitive of inspection. */
|
||||
export const ARTIFACT_ANALYSIS_OPERATIONS = [
|
||||
"inventory_artifact",
|
||||
...ARTIFACT_TOOL_CONTRACTS.map(({ name }) => name),
|
||||
] as const;
|
||||
|
||||
/** Provider-level operations including the inventory primitive used internally. */
|
||||
export type ArtifactAnalysisOperation =
|
||||
(typeof ARTIFACT_ANALYSIS_OPERATIONS)[number];
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
type IntegrityContradiction,
|
||||
} from "./artifactGraph.js";
|
||||
import { parseEvidence, type Evidence } from "./evidence.js";
|
||||
import { artifactInspectionResultSchema } from "./artifactInspection.js";
|
||||
|
||||
/** Complete or partially assembled inventory used by comparison workflows. */
|
||||
export interface InventorySet {
|
||||
|
|
@ -34,17 +35,25 @@ export const parseArtifactInventoryEvidence = (
|
|||
assembleInventorySet(
|
||||
(Array.isArray(input) ? input : [input]).map((value) => {
|
||||
const evidence = parseEvidence(value);
|
||||
if (evidence.operation !== "inventory_artifact")
|
||||
const inventoryEvidence =
|
||||
evidence.operation === "inventory_artifact"
|
||||
? evidence
|
||||
: evidence.operation === "inspect_artifact"
|
||||
? artifactInspectionResultSchema.parse(evidence.normalized_result)
|
||||
.substeps[0]?.evidence
|
||||
: undefined;
|
||||
if (inventoryEvidence === undefined)
|
||||
throw new TypeError(
|
||||
"Artifact comparison requires inventory_artifact Evidence",
|
||||
"Artifact comparison requires inspect_artifact Evidence",
|
||||
);
|
||||
const inventory = artifactInventoryResultSchema.parse(
|
||||
evidence.normalized_result,
|
||||
inventoryEvidence.normalized_result,
|
||||
);
|
||||
if (
|
||||
evidence.subject === null ||
|
||||
evidence.subject.digest.sha256 !== inventory.manifest.root_sha256 ||
|
||||
evidence.subject.format !== inventory.manifest.root_format
|
||||
inventoryEvidence.subject === null ||
|
||||
inventoryEvidence.subject.digest.sha256 !==
|
||||
inventory.manifest.root_sha256 ||
|
||||
inventoryEvidence.subject.format !== inventory.manifest.root_format
|
||||
)
|
||||
throw new TypeError(
|
||||
"Artifact inventory Evidence subject does not match its root digest",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -4,6 +4,7 @@ import type { AnalysisOperationPort } from "../application/AnalysisProvider.js";
|
|||
import type { BinarySessionPort } from "../application/BinarySession.js";
|
||||
import type { PromptCompletionKind } from "../contracts/promptContracts.js";
|
||||
import { artifactInventoryResultSchema } from "../domain/artifactGraph.js";
|
||||
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
|
||||
import { processCaptureSchema } from "../domain/processCapture.js";
|
||||
|
||||
const documentListSchema = z.array(z.string().min(1));
|
||||
|
|
@ -145,10 +146,21 @@ const evidenceValues = (
|
|||
if (kind === "evidence") return [evidence.evidence_id];
|
||||
if (kind === "capture")
|
||||
return isProcessCaptureEvidence(evidence) ? [evidence.evidence_id] : [];
|
||||
if (evidence.operation !== "inventory_artifact") return [];
|
||||
const inventory = artifactInventoryResultSchema.safeParse(
|
||||
evidence.normalized_result,
|
||||
);
|
||||
const inventoryResult =
|
||||
evidence.operation === "inventory_artifact"
|
||||
? evidence.normalized_result
|
||||
: evidence.operation === "inspect_artifact"
|
||||
? (() => {
|
||||
const inspection = artifactInspectionResultSchema.safeParse(
|
||||
evidence.normalized_result,
|
||||
);
|
||||
return inspection.success
|
||||
? inspection.data.substeps[0]?.evidence.normalized_result
|
||||
: undefined;
|
||||
})()
|
||||
: undefined;
|
||||
if (inventoryResult === undefined) return [];
|
||||
const inventory = artifactInventoryResultSchema.safeParse(inventoryResult);
|
||||
if (!inventory.success) return [];
|
||||
if (kind === "manifest") return [inventory.data.manifest.manifest_id];
|
||||
return inventory.data.occurrences.map(({ occurrence_id: id }) => id);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const registerArtifactComparisonTool = (
|
|||
toolRegistrationOptions(contract),
|
||||
async (input, context) => {
|
||||
const expected = {
|
||||
operation: "inventory_artifact",
|
||||
operation: ["inspect_artifact", "inventory_artifact"],
|
||||
predicate: "rea.analysis/v2",
|
||||
};
|
||||
const left = resolveSessionEvidenceIds(
|
||||
|
|
|
|||
|
|
@ -13,9 +13,16 @@ type EvidenceAuthorityResult = Result<Evidence[], EvidenceIntegrityError>;
|
|||
export const resolveSessionEvidenceIds = (
|
||||
session: BinarySessionPort,
|
||||
evidenceIds: readonly string[],
|
||||
expected: { readonly operation: string; readonly predicate: string },
|
||||
expected: {
|
||||
readonly operation: string | readonly string[];
|
||||
readonly predicate: string;
|
||||
},
|
||||
): EvidenceAuthorityResult => {
|
||||
const records: Evidence[] = [];
|
||||
const expectedOperations = Array.isArray(expected.operation)
|
||||
? expected.operation
|
||||
: [expected.operation];
|
||||
const expectedOperationLabel = expectedOperations.join(" or ");
|
||||
for (const evidenceId of evidenceIds) {
|
||||
const record = session.evidenceById(evidenceId);
|
||||
if (record === undefined)
|
||||
|
|
@ -23,16 +30,16 @@ export const resolveSessionEvidenceIds = (
|
|||
new EvidenceReferenceError(
|
||||
evidenceId,
|
||||
"missing",
|
||||
expected.operation,
|
||||
expectedOperationLabel,
|
||||
null,
|
||||
),
|
||||
);
|
||||
if (record.operation !== expected.operation)
|
||||
if (!expectedOperations.includes(record.operation))
|
||||
return err(
|
||||
new EvidenceReferenceError(
|
||||
evidenceId,
|
||||
"wrong_operation",
|
||||
expected.operation,
|
||||
expectedOperationLabel,
|
||||
record.operation,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
|||
import type { BinarySession } from "../../../src/application/BinarySession.js";
|
||||
import { createServer } from "../../../src/server/createServer.js";
|
||||
import {
|
||||
JAVASCRIPT_APPLICATION_VERSION_COMPARISON_EXAMPLE,
|
||||
JAVASCRIPT_FEATURE_TRACE_FULL_EVIDENCE_EXAMPLE,
|
||||
JAVASCRIPT_FEATURE_TRACE_EXAMPLE,
|
||||
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE,
|
||||
|
|
@ -17,7 +16,6 @@ import { observed } from "../../fixtures/analysisExecution.js";
|
|||
interface TestHarness {
|
||||
readonly client: Client;
|
||||
readonly session: BinarySession;
|
||||
readonly resourceListChanges: () => number;
|
||||
readonly close: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
|
@ -33,17 +31,12 @@ async function createTestHarness(): Promise<TestHarness> {
|
|||
});
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
let resourceListChanges = 0;
|
||||
client.setNotificationHandler("notifications/resources/list_changed", () => {
|
||||
resourceListChanges += 1;
|
||||
});
|
||||
await server.connect(serverTransport);
|
||||
await client.connect(clientTransport);
|
||||
|
||||
return {
|
||||
client,
|
||||
session,
|
||||
resourceListChanges: () => resourceListChanges,
|
||||
close: async () => {
|
||||
await client.close();
|
||||
await server.close();
|
||||
|
|
@ -103,8 +96,6 @@ async function runInlineEvidenceScenarios(
|
|||
}
|
||||
|
||||
async function runEvidenceIdScenarios(harness: TestHarness): Promise<void> {
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
const notificationsBeforeIdReuse = harness.resourceListChanges();
|
||||
const tracedById = await harness.client.callTool({
|
||||
name: "trace_application_feature",
|
||||
arguments: {
|
||||
|
|
@ -114,13 +105,10 @@ async function runEvidenceIdScenarios(harness: TestHarness): Promise<void> {
|
|||
},
|
||||
});
|
||||
expect(tracedById.isError).not.toBe(true);
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
expect(harness.resourceListChanges()).toBe(notificationsBeforeIdReuse);
|
||||
|
||||
const comparedById = await harness.client.callTool({
|
||||
name: "compare_application_versions",
|
||||
arguments: {
|
||||
...JAVASCRIPT_APPLICATION_VERSION_COMPARISON_EXAMPLE,
|
||||
left_evidence_id:
|
||||
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left.evidence_id,
|
||||
right_evidence_id:
|
||||
|
|
@ -138,6 +126,36 @@ async function runEvidenceIdScenarios(harness: TestHarness): Promise<void> {
|
|||
},
|
||||
});
|
||||
|
||||
const mixedLeftInline = await harness.client.callTool({
|
||||
name: "compare_application_versions",
|
||||
arguments: {
|
||||
left: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left,
|
||||
right:
|
||||
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.right.evidence_id,
|
||||
},
|
||||
});
|
||||
expect(mixedLeftInline.isError).not.toBe(true);
|
||||
|
||||
const mixedRightInline = await harness.client.callTool({
|
||||
name: "compare_application_versions",
|
||||
arguments: {
|
||||
left: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left
|
||||
.evidence_id,
|
||||
right: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.right,
|
||||
},
|
||||
});
|
||||
expect(mixedRightInline.isError).not.toBe(true);
|
||||
|
||||
const mixedWithLegacyAlias = await harness.client.callTool({
|
||||
name: "compare_application_versions",
|
||||
arguments: {
|
||||
left: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left,
|
||||
right_evidence_id:
|
||||
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.right.evidence_id,
|
||||
},
|
||||
});
|
||||
expect(mixedWithLegacyAlias.isError).not.toBe(true);
|
||||
|
||||
const sourceComparedById = await harness.client.callTool({
|
||||
name: "compare_source_to_bundle",
|
||||
arguments: {
|
||||
|
|
@ -225,6 +243,17 @@ async function assertRejectedInlineEvidence(client: Client): Promise<void> {
|
|||
},
|
||||
});
|
||||
expect(spoofed.isError).toBe(true);
|
||||
|
||||
const duplicateComparisonReference = await client.callTool({
|
||||
name: "compare_application_versions",
|
||||
arguments: {
|
||||
left: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left,
|
||||
left_evidence_id:
|
||||
JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.left.evidence_id,
|
||||
right: JAVASCRIPT_VERSION_COMPARISON_FULL_EVIDENCE_EXAMPLE.right,
|
||||
},
|
||||
});
|
||||
expect(duplicateComparisonReference.isError).toBe(true);
|
||||
}
|
||||
|
||||
describe("application workflow MCP parity", () => {
|
||||
|
|
|
|||
|
|
@ -37,23 +37,58 @@ it("returns every artifact occurrence in one MCP call", async () => {
|
|||
arguments: { path: archive },
|
||||
});
|
||||
expect(opened.isError).not.toBe(true);
|
||||
const names = (await client.listTools()).tools.map(({ name }) => name);
|
||||
expect(names).toContain("inspect_artifact");
|
||||
expect(names).not.toContain("inventory_artifact");
|
||||
const status = await client.callTool({
|
||||
name: "binary_session",
|
||||
arguments: {},
|
||||
});
|
||||
expect(
|
||||
z
|
||||
.object({
|
||||
result: z.object({
|
||||
capabilities: z.array(z.object({ operation: z.string() })),
|
||||
}),
|
||||
})
|
||||
.parse(status.structuredContent)
|
||||
.result.capabilities.map(({ operation }) => operation),
|
||||
).not.toContain("inventory_artifact");
|
||||
const result = await client.callTool({
|
||||
name: "inventory_artifact",
|
||||
name: "inspect_artifact",
|
||||
arguments: {},
|
||||
});
|
||||
expect(result.isError, JSON.stringify(result.structuredContent)).not.toBe(
|
||||
true,
|
||||
);
|
||||
const inventory = z
|
||||
const inspection = z
|
||||
.object({
|
||||
occurrences: z.array(z.object({ logical_path: z.string() })),
|
||||
substeps: z.array(
|
||||
z.object({
|
||||
evidence: z.object({
|
||||
normalized_result: z.object({
|
||||
occurrences: z.array(z.object({ logical_path: z.string() })),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
observations: z.array(z.unknown()),
|
||||
derived_relationships: z.array(z.unknown()),
|
||||
hypotheses: z.array(z.unknown()),
|
||||
unexplored_branches: z.array(z.unknown()),
|
||||
next_probes: z.array(z.unknown()),
|
||||
})
|
||||
.parse(
|
||||
z.object({ result: z.unknown() }).parse(result.structuredContent)
|
||||
.result,
|
||||
);
|
||||
expect(inventory.occurrences).toHaveLength(fileCount + 1);
|
||||
expect(inventory.occurrences.length).toBe(fileCount + 1);
|
||||
const occurrences =
|
||||
inspection.substeps[0]?.evidence.normalized_result.occurrences;
|
||||
if (occurrences === undefined)
|
||||
throw new Error("inspection omitted the artifact inventory");
|
||||
expect(occurrences).toHaveLength(fileCount + 1);
|
||||
expect(inspection.observations.length).toBeGreaterThan(0);
|
||||
expect(inspection.derived_relationships.length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await Promise.allSettled([client.close(), server.close(), session.close()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ it.each([
|
|||
expect(opened.isError).not.toBe(true);
|
||||
|
||||
const result = await client.callTool({
|
||||
name: "inventory_artifact",
|
||||
name: "inspect_artifact",
|
||||
arguments: {},
|
||||
});
|
||||
expect(result.isError).toBe(true);
|
||||
|
|
@ -142,7 +142,7 @@ it("records an approved mismatch, preserves verified siblings, and never reports
|
|||
arguments: { path: archive },
|
||||
});
|
||||
const result = await client.callTool({
|
||||
name: "inventory_artifact",
|
||||
name: "inspect_artifact",
|
||||
arguments: {
|
||||
integrity_policy: "record-and-continue",
|
||||
integrity_continue_approved: true,
|
||||
|
|
@ -154,7 +154,20 @@ it("records an approved mismatch, preserves verified siblings, and never reports
|
|||
const compact = compactResult(result.structuredContent);
|
||||
const evidence = session.evidenceById(compact.evidence_id);
|
||||
if (evidence === undefined) throw new Error("missing inventory Evidence");
|
||||
const inventory = artifactInventoryResultSchema.parse(compact.result);
|
||||
const inspection = z
|
||||
.object({
|
||||
contradictions: z.array(z.unknown()),
|
||||
substeps: z.array(
|
||||
z.object({
|
||||
evidence: z.object({ normalized_result: z.unknown() }),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.parse(compact.result);
|
||||
const inventory = artifactInventoryResultSchema.parse(
|
||||
inspection.substeps[0]?.evidence.normalized_result,
|
||||
);
|
||||
expect(inspection.contradictions).toHaveLength(2);
|
||||
expect(inventory.integrity_contradictions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
|
@ -183,26 +196,6 @@ it("records an approved mismatch, preserves verified siblings, and never reports
|
|||
}),
|
||||
]),
|
||||
);
|
||||
const inspected = await client.callTool({
|
||||
name: "inspect_artifact",
|
||||
arguments: {
|
||||
integrity_policy: "record-and-continue",
|
||||
integrity_continue_approved: true,
|
||||
},
|
||||
});
|
||||
expect(inspected.isError).not.toBe(true);
|
||||
expect(compactResult(inspected.structuredContent).result).toMatchObject({
|
||||
contradictions: [
|
||||
expect.objectContaining({ occurrence_id: expect.any(String) }),
|
||||
expect.objectContaining({ occurrence_id: expect.any(String) }),
|
||||
],
|
||||
substeps: [
|
||||
expect.objectContaining({
|
||||
operation: "inventory_artifact",
|
||||
status: "completed",
|
||||
}),
|
||||
],
|
||||
});
|
||||
const compared = await client.callTool({
|
||||
name: "compare_artifacts",
|
||||
arguments: {
|
||||
|
|
@ -362,7 +355,7 @@ it("returns full artifact graphs inline and compares changed inventories", async
|
|||
inspection.substeps[0]?.evidence_id,
|
||||
]);
|
||||
const inventory = await client.callTool({
|
||||
name: "inventory_artifact",
|
||||
name: "inspect_artifact",
|
||||
arguments: {},
|
||||
});
|
||||
expect(inventory.isError).not.toBe(true);
|
||||
|
|
@ -373,13 +366,22 @@ it("returns full artifact graphs inline and compares changed inventories", async
|
|||
provider: { id: "rea-artifact-graph" },
|
||||
subject: { format: "ipa" },
|
||||
});
|
||||
const inventoryInspection = z
|
||||
.object({
|
||||
substeps: z.array(
|
||||
z.object({
|
||||
evidence: z.object({
|
||||
normalized_result: z.object({
|
||||
manifest: z.object({ root_format: z.literal("ipa") }),
|
||||
occurrences: z.array(z.object({ logical_path: z.string() })),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.parse(inventoryResult.result);
|
||||
expect(
|
||||
z
|
||||
.object({
|
||||
manifest: z.object({ root_format: z.literal("ipa") }),
|
||||
occurrences: z.array(z.object({ logical_path: z.string() })),
|
||||
})
|
||||
.parse(inventoryResult.result).occurrences,
|
||||
inventoryInspection.substeps[0]?.evidence.normalized_result.occurrences,
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
|
@ -393,7 +395,7 @@ it("returns full artifact graphs inline and compares changed inventories", async
|
|||
});
|
||||
expect(openedChanged.isError).not.toBe(true);
|
||||
const changedInventory = await client.callTool({
|
||||
name: "inventory_artifact",
|
||||
name: "inspect_artifact",
|
||||
arguments: {},
|
||||
});
|
||||
const changedResult = compactResult(changedInventory.structuredContent);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js
|
|||
|
||||
import { runProviderAnalysis } from "../../../../src/application/DirectAnalysis.js";
|
||||
import { ArtifactProvider } from "../../../../src/artifacts/ArtifactProvider.js";
|
||||
import { artifactInspectionInputSchema } from "../../../../src/contracts/artifactToolContracts.js";
|
||||
import { artifactInventoryResultSchema } from "../../../../src/domain/artifactGraph.js";
|
||||
import { artifactInspectionResultSchema } from "../../../../src/domain/artifactInspection.js";
|
||||
import type { BinaryTarget } from "../../../../src/domain/binaryTarget.js";
|
||||
|
|
@ -123,7 +122,7 @@ describe("artifact archive inventory", () => {
|
|||
});
|
||||
const failedInspection = await new ArtifactProvider()
|
||||
.createClient(target(unpackedPath, "asar"))
|
||||
.execute("inspect_artifact", artifactInspectionInputSchema.parse({}));
|
||||
.execute("inspect_artifact", {});
|
||||
expect(failedInspection).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "ArtifactOperationError", reason: "integrity" },
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ describe("artifact comparison", () => {
|
|||
result: evidence.normalized_result,
|
||||
});
|
||||
expect(() => compareArtifacts(wrongOperation, evidence)).toThrow(
|
||||
/inventory_artifact/u,
|
||||
/inspect_artifact/u,
|
||||
);
|
||||
const mismatchedSubject = createEvidence(
|
||||
{ path: root, sha256: "f".repeat(64), format: "directory" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue