param( [string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il", [string]$OutputDir = "runs/inprocess-adapter-research", [string]$ReportPath = "docs/source-discovery/2026-07-12-inprocess-adapter-static-map.md", [int]$WindowRadius = 4, [int]$MaxEvidencePerAnchor = 16 ) $ErrorActionPreference = "Stop" $repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path $ilDirFull = Join-Path $repo $IlDir $outDir = Join-Path $repo $OutputDir $reportFullPath = Join-Path $repo $ReportPath New-Item -ItemType Directory -Force -Path $outDir | Out-Null New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null $sources = @( @{ path = "IMPlatformClient.exe.il"; role = "main managed client; UI, AppContextManager, MessageScheduling, frmMain, login runtime" }, @{ path = "smack.dll.il"; role = "XMPP/smack managed library; Chat and XMPPConnection send primitives" }, @{ path = "IMPP.Service.dll.il"; role = "file transfer service implementation" }, @{ path = "IMPP.ServiceBase.dll.il"; role = "file transfer DTOs and FileUploadPara/FileUploadResult contracts" }, @{ path = "IMPP.Interface.dll.il"; role = "service and network interface contracts" }, @{ path = "IMPP.Common.dll.il"; role = "common helper and runtime DTO evidence" } ) $anchors = @( @{ key = "text_high_level_entry" display = "AppContextManager.SendTxtMessageByJid" command = "send_text" adapter_role = "preferred high-level managed entry if an in-process adapter can reach AppContextManager" confidence = "high_static" usable_offline = $false required_runtime = @("logged-in IMPPManager/UserInfo", "target JID", "initialized AppContextManager", "live XMPP connection") patterns = @("AppContextManager::SendTxtMessageByJid", "\bSendTxtMessageByJid\b") }, @{ key = "text_scheduling_entry" display = "MessageScheduling.SendChatMessage" command = "send_text" adapter_role = "fallback managed entry when scheduling layer has chat and contact args" confidence = "high_static" usable_offline = $false required_runtime = @("MessageScheduling instance or static caller", "RosterContactsArgs/contact context", "Chat object", "live XMPP connection") patterns = @("MessageScheduling::SendChatMessage", "SendChatMessage\(", "SendChatMessage\(class IMPP\.Client") }, @{ key = "text_low_level_chat" display = "com.vision.smack.Chat.SendMessage / XMPPConnection.send" command = "send_text" adapter_role = "lowest-level send target; requires already resolved Chat/XMPPConnection" confidence = "high_static" usable_offline = $false required_runtime = @("Chat instance", "XMPPConnection authenticated session", "message payload format") patterns = @("com\.vision\.smack\.Chat::SendMessage", "com\.vision\.smack\.Chat::sendMessage", "XMPPConnection::send\b") }, @{ key = "file_upload_orchestrator" display = "IMPP.Client.OffLineFileSend.sendFile" command = "upload_file_then_send_file" adapter_role = "UI-side upload orchestration and transition into chat file message" confidence = "high_static" usable_offline = $false required_runtime = @("local file path", "HTTP file server/auth state", "target roster/chat args", "upload completion callback") patterns = @("OffLineFileSend::sendFile", "sendFile\(\) cil managed", "OffLineFileSend::trans_UploadCompleted") }, @{ key = "file_upload_service" display = "IFileTransfer/FileTransfer.FileUpload + FileUploadPara" command = "upload_file" adapter_role = "service upload entry that must return FileUploadResult before chat file finalization" confidence = "high_static" usable_offline = $false required_runtime = @("FileUploadPara host/port/domain", "localFilePath", "token/auth", "FileUploadResult.FileID") patterns = @("FileTransfer::FileUpload", "FileUploadPara", "FileUploadResult::get_FileID", "FileUploadResult") }, @{ key = "file_message_finalizer" display = "MessageScheduling.SendFileMessage / Chat.sendFileMessage" command = "send_file_message" adapter_role = "chat file-message finalization after FileID/remote folder exists" confidence = "high_static" usable_offline = $false required_runtime = @("uploaded file id", "remote folder metadata", "file name/size", "target Chat/JID") patterns = @("MessageScheduling::SendFileMessage", "Chat::sendFileMessage", "sendFileMessage\(") }, @{ key = "logged_in_identity_auth" display = "IMPPManager.UserInfo / UnifiedAuthentication / Auth token" command = "runtime_context" adapter_role = "runtime state required before any production send/upload can be meaningful" confidence = "medium_static" usable_offline = $false required_runtime = @("UserInfo", "UnifiedAuthentication", "Auth token", "HTTPFileServer", "server resource/domain") patterns = @("IMPPManager::get_UserInfo", "IMPPManager.UserInfo", "UnifiedAuthentication", "HTTPFileServer", "Auth::get_token", "get_token") }, @{ key = "main_window_runtime_owner" display = "frmMain / MessageCenter / chat runtime ownership" command = "runtime_context" adapter_role = "likely object owner for initialized chat, reconnect, message center, and plugin runtime state" confidence = "medium_static" usable_offline = $false required_runtime = @("running frmMain", "initialized message center", "contact/session state") patterns = @("class IMPP\.Client\.frmMain", "frmMain::", "GetMessageCenter\(\)", "MessageCenter", "OnReConnectionSucceed", "GetChat\(") }, @{ key = "existing_http_not_bridge" display = "frmLogin.StartWinServer / HttpServerLib" command = "not_selected_bridge" adapter_role = "known local HTTP framework, but not a confirmed send bridge" confidence = "high_static_negative" usable_offline = $false required_runtime = @("logged-in state", "route module registration not found", "send route not found") patterns = @("StartWinServer", "HttpServerLib", "HttpService::\.ctor", "RegisterModule", "RouteAttribute") } ) function Get-RelativePath([string]$Path) { if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) { return $Path.Substring($repo.Length).TrimStart([char[]]@('\', '/')) } return $Path } function Write-Utf8NoBomLf([string]$Path, [string]$Text) { $utf8NoBom = [System.Text.UTF8Encoding]::new($false) $normalized = $Text -replace "`r`n", "`n" $normalized = $normalized -replace "`r", "`n" $normalized = $normalized.TrimEnd() + "`n" [System.IO.File]::WriteAllText($Path, $normalized, $utf8NoBom) } function Get-EvidenceKind([string]$Line) { if ($Line -match "^\s*\.class\b") { return "type_definition" } if ($Line -match "^\s*\.method\b") { return "method_header" } if ($Line -match "\bend of method\b") { return "method_end" } if ($Line -match "\bnewobj\b") { return "constructor_call" } if ($Line -match "\bcall(?:virt)?\b") { return "callsite" } if ($Line -match "\bldstr\b") { return "string_literal" } if ($Line -match "^\s*(instance|static)\b.*\(") { return "signature_continuation" } if ($Line -match "^\s*\.field\b") { return "field" } return "reference" } function Get-MethodContext($Lines, [int]$Index) { $start = [Math]::Max(0, $Index - 100) for ($i = $Index; $i -ge $start; $i--) { if ($Lines[$i] -match "^\s*\.method\b") { $header = $Lines[$i].Trim() if (($i + 1) -lt $Lines.Count -and $Lines[$i + 1] -match "^\s+(instance|static|default)\b") { $header = ($header + " " + $Lines[$i + 1].Trim()) } return "L$($i + 1): $header" } } return "" } function Get-WindowSnippet($Lines, [int]$Index, [int]$Radius) { $start = [Math]::Max(0, $Index - $Radius) $end = [Math]::Min($Lines.Count - 1, $Index + $Radius) $parts = New-Object System.Collections.Generic.List[string] for ($i = $start; $i -le $end; $i++) { $parts.Add(("L{0}: {1}" -f ($i + 1), $Lines[$i].Trim())) } return ($parts -join "`n") } function Find-AnchorEvidence($Anchor) { $items = New-Object System.Collections.Generic.List[object] foreach ($source in $sources) { $sourcePath = Join-Path $ilDirFull $source.path if (-not (Test-Path -LiteralPath $sourcePath)) { continue } $lines = [System.IO.File]::ReadAllLines($sourcePath) for ($i = 0; $i -lt $lines.Count; $i++) { $line = $lines[$i] foreach ($pattern in $Anchor.patterns) { if ($line -match $pattern) { $items.Add([pscustomobject]@{ source = Get-RelativePath $sourcePath source_role = $source.role line = $i + 1 kind = Get-EvidenceKind $line pattern = $pattern snippet = $line.Trim() context = Get-MethodContext $lines $i window = Get-WindowSnippet $lines $i $WindowRadius }) break } } if ($items.Count -ge $MaxEvidencePerAnchor) { break } } if ($items.Count -ge $MaxEvidencePerAnchor) { break } } return $items.ToArray() } function Classify-Anchor($Anchor, $Evidence) { if ($Evidence.Count -eq 0) { return "missing_static_evidence" } if ($Anchor.command -eq "not_selected_bridge") { return "not_a_send_bridge" } if ($Anchor.command -eq "runtime_context") { return "runtime_dependency" } return "in_process_candidate_static_only" } $sourceSummary = foreach ($source in $sources) { $path = Join-Path $ilDirFull $source.path $exists = Test-Path -LiteralPath $path $lineCount = 0 $size = 0 if ($exists) { $item = Get-Item -LiteralPath $path $size = $item.Length $lineCount = ([System.IO.File]::ReadLines($path) | Measure-Object).Count } [pscustomobject]@{ path = Get-RelativePath $path role = $source.role exists = $exists lines = $lineCount size_bytes = $size } } $anchorResults = foreach ($anchor in $anchors) { $evidence = Find-AnchorEvidence $anchor [pscustomobject]@{ key = $anchor.key display = $anchor.display command = $anchor.command adapter_role = $anchor.adapter_role classification = Classify-Anchor $anchor $evidence confidence = $anchor.confidence usable_offline = $anchor.usable_offline required_runtime = $anchor.required_runtime evidence_count = $evidence.Count evidence = $evidence } } $commands = @( [pscustomobject]@{ op = "probe_runtime" status = "contract_only" purpose = "report whether adapter is inside logged-in IMPP runtime and can see required types/singletons" required_evidence = @("process identity", "loaded assemblies", "IMPPManager.UserInfo presence", "XMPP connection state") }, [pscustomobject]@{ op = "send_text" status = "blocked_until_logged_in_adapter" purpose = "invoke high-level text send path after target JID and content hash are validated" required_evidence = @("AppContextManager or MessageScheduling reachability", "target JID", "sent record or ack containing content hash/correlation") }, [pscustomobject]@{ op = "upload_file" status = "blocked_until_logged_in_adapter" purpose = "call file upload service and return server FileID/remote folder metadata" required_evidence = @("FileUploadPara values", "HTTP file server/auth", "FileUploadResult.FileID") }, [pscustomobject]@{ op = "send_file_message" status = "blocked_until_upload_evidence" purpose = "finalize file message after upload returns server id" required_evidence = @("uploaded FileID", "file name/size", "target JID/Chat", "sent record/ack") } ) $requiredRuntimeState = @( "logged-in IMPlatformClient.exe process", "loaded IMPP.Client/AppContextManager/MessageScheduling types", "initialized IMPPManager.UserInfo and UnifiedAuthentication/Auth token", "live XMPPConnection/Chat session", "target JID or group JID resolved by existing search tools", "file upload service parameters and FileUploadResult.FileID for file sends" ) $stopConditions = @( "No logged-in process is available for runtime reachability validation.", "Adapter cannot see AppContextManager, MessageScheduling, Chat, or IFileTransfer in process.", "Every viable path requires unsupported binary patching instead of a replaceable managed adapter/harness.", "Online evidence cannot produce sent-record/content-hash/ack correlation for text send.", "File upload cannot return FileUploadResult.FileID or equivalent remote folder metadata." ) $result = [pscustomobject]@{ ok = $true generated_at = (Get-Date).ToUniversalTime().ToString("o") decision = "managed_in_process_adapter_contract_first" sources = $sourceSummary anchors = $anchorResults commands = $commands required_runtime_state = $requiredRuntimeState stop_conditions = $stopConditions originals_copied = $false originals_modified = $false } $jsonPath = Join-Path $outDir "inprocess-adapter-map.json" $jsonText = $result | ConvertTo-Json -Depth 8 Write-Utf8NoBomLf $jsonPath $jsonText $md = New-Object System.Collections.Generic.List[string] $md.Add("# In-Process Adapter Static Map") $md.Add("") $md.Add("Date: 2026-07-12") $md.Add("") $md.Add("## Scope") $md.Add("") $md.Add("This report uses offline IL/static evidence only. It does not log in, attach hooks, inject into a process, modify client binaries, upload files, send messages, or open network connections.") $md.Add("") $md.Add("Machine-readable evidence: ``$(Get-RelativePath $jsonPath)``") $md.Add("") $md.Add("## Decision") $md.Add("") $md.Add("| Question | Answer |") $md.Add("| --- | --- |") $md.Add("| Selected next B-route branch | `managed_in_process_adapter_contract_first` |") $md.Add("| Can offline branch enable production send? | **No** |") $md.Add("| Go connector mode to expose now | `in_process_contract` |") $md.Add("| Required online proof later | logged-in adapter reachability plus sent-record/content-hash/ack evidence |") $md.Add("") $md.Add("## Sources") $md.Add("") $md.Add("| Source | Role | Exists | Lines | Size bytes |") $md.Add("| --- | --- | --- | ---: | ---: |") foreach ($source in $sourceSummary) { $md.Add("| ``$($source.path)`` | $($source.role) | $($source.exists) | $($source.lines) | $($source.size_bytes) |") } $md.Add("") $md.Add("## Command contract for future in-process adapter") $md.Add("") $md.Add("| Op | Current status | Purpose | Required evidence before production |") $md.Add("| --- | --- | --- | --- |") foreach ($cmd in $commands) { $required = ($cmd.required_evidence -join "; ") $md.Add("| ``$($cmd.op)`` | ``$($cmd.status)`` | $($cmd.purpose) | $required |") } $md.Add("") $md.Add("## Anchor summary") $md.Add("") $md.Add("| Anchor | Command | Classification | Confidence | Evidence | Required runtime |") $md.Add("| --- | --- | --- | --- | ---: | --- |") foreach ($anchor in $anchorResults) { $required = ($anchor.required_runtime -join "; ") $md.Add("| ``$($anchor.display)`` | ``$($anchor.command)`` | ``$($anchor.classification)`` | ``$($anchor.confidence)`` | $($anchor.evidence_count) | $required |") } $md.Add("") $md.Add("## Verified facts") $md.Add("") $md.Add("- Text send has static in-process candidates, but all require logged-in runtime objects and a live XMPP/Chat path.") $md.Add("- File send is split into upload and message-finalization; `FileUploadResult.FileID` is a required boundary value before `Chat.sendFileMessage` is meaningful.") $md.Add("- The earlier IPC/plugin/local HTTP scan did not find a confirmed external send bridge, so this branch must stay at `in_process_contract` until online reachability exists.") $md.Add("- `in_process_contract` is a Go-side contract and audit mode only; it must return blocked/no-side-effect metadata in this offline branch.") $md.Add("") $md.Add("## Required runtime state") $md.Add("") foreach ($state in $requiredRuntimeState) { $md.Add("- $state") } $md.Add("") $md.Add("## Anchor evidence") foreach ($anchor in $anchorResults) { $md.Add("") $md.Add("### $($anchor.display)") $md.Add("") $md.Add("Classification: ``$($anchor.classification)``; command: ``$($anchor.command)``; evidence count: $($anchor.evidence_count).") $md.Add("") $md.Add("Adapter role: $($anchor.adapter_role)") $md.Add("") if ($anchor.evidence_count -eq 0) { $md.Add("No static evidence found for this anchor in the configured IL set.") continue } $md.Add("| Source | Kind | Snippet | Context |") $md.Add("| --- | --- | --- | --- |") foreach ($e in $anchor.evidence) { $snippet = ($e.snippet -replace "\|", "\\|" ) if ($snippet.Length -gt 220) { $snippet = $snippet.Substring(0, 217) + "..." } $context = ($e.context -replace "\|", "\\|" ) if ($context.Length -gt 180) { $context = $context.Substring(0, 177) + "..." } $md.Add("| ``$($e.source):$($e.line)`` | ``$($e.kind)`` | ``$snippet`` | ``$context`` |") } } $md.Add("") $md.Add("## Stop conditions") $md.Add("") foreach ($condition in $stopConditions) { $md.Add("- $condition") } $md.Add("") $md.Add("## Next work") $md.Add("") $md.Add("1. Add Go connector mode `in_process_contract` that validates request shape and returns blocked/no-side-effect metadata.") $md.Add("2. In a logged-in environment, build a recorder/probe that only checks reachability of the listed runtime objects before attempting any send.") $md.Add("3. Promote text send only after sent-record/content-hash/ack evidence exists.") $md.Add("4. Promote file send only after upload returns `FileUploadResult.FileID` and file-message finalization is proven online.") Write-Utf8NoBomLf $reportFullPath ($md -join "`n") [pscustomobject]@{ ok = $true json_path = $jsonPath report_path = $reportFullPath anchors = $anchorResults.Count decision = $result.decision originals_copied = $false originals_modified = $false } | ConvertTo-Json -Depth 5