Files
isphere-ai-bridge/docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md
2026-07-10 14:19:15 +08:00

361 lines
12 KiB
Markdown

# Send Connector B-First Plan
Date: 2026-07-10
Node: C29 / write-side connector route correction
Decision owner: user/business manager
## Decision
The selected send route is now:
1. **Primary route B: in-process / sidecar connector** that reuses the already logged-in `IMPlatformClient.exe` runtime and its existing message/file send classes.
2. **Fallback route A: constrained UI/RPA wrapper** only if B cannot attach, load, or invoke safely.
3. **Network replay/protocol reimplementation remains deferred** because it would require independently reproducing login/session/ack behavior.
This document supersedes the previous C15/R5 conclusion for future send work. The old conclusion was correct for the evidence available at that time: no write connector had been selected. This node records the newly approved route and the evidence that makes B worth trying first.
## Business goal
Expose controlled MCP actions for digital employees:
- `isphere_send_message`
- `isphere_send_file`
The implementation must preserve these business controls:
- target must be resolved from existing contact/group search results;
- text/file content must be previewed before execution;
- execution must use an `idempotency_key` and content hash;
- every outbound attempt must produce an audit record;
- early versions should support sandbox/test-target verification before normal production use.
## Static evidence summary
Offline package evidence under:
```text
runs/offline-evidence-intake/zyl-qqfile-20260709/
```
Relevant extracted binaries:
```text
zyl/Impp/IMPlatformClient.exe
zyl/Impp/smack.dll
zyl/Impp/IMPP.Interface.dll
zyl/Impp/IMPP.ServiceBase.dll
zyl/Impp/IMPP.Service.dll
zyl/Impp/TcpFileTransfer.dll
zyl/Impp/HYHC.IMPP.DAL.dll
```
Relevant IL snapshots:
```text
runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il/IMPlatformClient.exe.il
runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il/smack.dll.il
runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il/IMPP.Interface.dll.il
runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il/IMPP.Service.dll.il
runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il/IMPP.ServiceBase.dll.il
```
## Text-send call chain
### Preferred sidecar candidate
`IMPlatformClient.exe` contains a high-level helper:
```text
IMPP.Client.Core.Plugins.Manager.AppContextManager.SendTxtMessageByJid(jid, chatType, content)
```
Observed behavior from IL:
- for direct/P2P chat, it gets a `P2PChatManager` from `IMPPManager.Instance`;
- it resolves a `Chat` by JID;
- it finds or opens a chat form;
- it calls `frmP2PChat.SendTxtMessage(content)`;
- for temporary/reserved group chat, it calls the corresponding room form send method.
This is the best B-route first target because it reuses the logged-in client's own state and UI/business rules.
### Central message construction candidate
`IMPlatformClient.exe` also contains:
```text
IMPP.Client.Core.MessageEngine.MessageScheduling.SendChatMessage(chat, msgText, toJid, messageType, isSaveMessage, msgGuid, IsReceipt)
```
Observed behavior from IL:
- reads local font/style settings;
- builds message body via `UtilsText.GetMessageStyleBodyText`;
- decides `chat` vs `groupchat`;
- creates `smack.packet.XmppMessage`;
- sets body, subject, offline flag, receipt flag, time, and message type;
- calls `chat.SendMessage(xmppMessage)`;
- optionally persists/parses message through `MessageManager.MessageParsing`.
This is the cleanest lower-level B candidate if the sidecar can obtain a valid `Chat` object from the running client context.
### Protocol bottom
`smack.dll` provides the real wire-send chain:
```text
com.vision.smack.Chat.SendMessage(XmppMessage)
-> com.vision.smack.XMPPConnection.send(string)
-> Sharp.Xmpp.Core.XmppCore.Send(string)
-> stream.Write(...)
```
This proves that the send capability exists in the installed client binaries. It also shows why an external reimplementation is less attractive: it would need the same active connection, stanza format, message id, receipt, and session state.
## Important trap: plugin interface text send is not enough
`IMPP.Interface.dll` declares:
```text
IAppContextManager.SendP2PMessage(string pJid, string pMessage)
IAppContextManager.SendP2PFiles(UserInfo[] pUserInfo, string[] pFiles)
IAppContextManager.SendChatRoomMessage(string pRoomJid, string pMessage, string pluginID)
IAppContextManager.SendPluginMessage(string pluginID, int command, string para)
```
But the implementation of:
```text
IMPP.Client.Core.Plugins.Manager.AppContextManager.SendP2PMessage(...)
```
records the plugin call and then throws `NotImplementedException`.
Therefore the B route must **not** choose `SendP2PMessage` as the text-send entry. The sidecar should prefer `SendTxtMessageByJid` or, if necessary, `MessageScheduling.SendChatMessage` with an existing `Chat` object.
## File-send call chain
File sending appears split into upload plus file-message notification.
### Upload engine
`IMPP.ServiceBase.dll` defines `IFileTransfer`.
`IMPP.Service.dll` implements:
```text
IMPP.Service.BLL.FileTransfer.FileUpload(...)
IMPP.Service.BLL.FileTransfer.AsynFileUpload(...)
IMPP.Service.BLL.FileTransfer.FileDownload(...)
IMPP.Service.BLL.FileTransfer.SyncFileTransfer(...)
```
### Offline file send
`IMPlatformClient.exe` contains:
```text
IMPP.Client.OffLineFileSend.sendFile()
IMPP.Client.OffLineFileSend.trans_UploadCompleted(fileId)
```
Observed behavior:
- builds a generated file GUID;
- builds remote name as `filename____guid.ext`;
- uses `Config.ServerInfo.HTTPFileServer` and `FileTransferManager.GetDomain`;
- creates `FileUploadPara`;
- calls `IMPPManager.Instance.Service.FileTransfer.FileUpload(uploadPara)`;
- after upload completion, creates an `OFFLINE_FILE_TRANSFER;...` message body;
- calls `Chat.sendFileMessage(...)`;
- sets packet id to `fileId` and records metadata.
### Native TCP file transfer
`TcpFileTransfer.dll` is native and exposes strings/exports indicating:
```text
Send
SendFile
GetTcpFileMsgGuid
_StartWaitForSendFileThread@8
_StartRecvFileThread@24
```
This is a candidate for online/TCP file transfer, but the first B route should use the managed offline-file path because it is closer to the normal client business flow and already integrates upload metadata with XMPP file messages.
## Proposed sidecar architecture
```text
Go MCP Server
-> controlled action contract and audit
-> C# WinHelper / Sidecar command
-> B-route sidecar adapter
-> running IMPlatformClient context
-> existing AppContextManager / MessageScheduling / OffLineFileSend / smack.dll
```
The sidecar should be a narrow adapter, not a general-purpose remote code executor.
### Candidate sidecar operations
```text
version
self_check
probe_client_runtime
probe_send_entrypoints
resolve_runtime_jid
preview_send_message
send_message_after_approval
preview_send_file
send_file_after_approval
read_send_audit
```
The first implementation slice should stop before real production send:
```text
version
self_check
probe_client_runtime
probe_send_entrypoints
```
Then run a live sandbox observation before enabling `send_message_after_approval`.
## B-route validation gates
### Gate B1: runtime discovery
Pass condition:
- find a running `IMPlatformClient.exe` process;
- record PID, bitness, install path, module list summary;
- confirm the discovered path matches the extracted evidence family;
- confirm whether the sidecar must be x86 or x64.
Stop condition:
- no logged-in process;
- process bitness incompatible with available sidecar build;
- module names differ too much from the extracted package.
### Gate B2: entrypoint availability
Pass condition:
- confirm `IMPlatformClient.exe` exposes `AppContextManager` and `MessageScheduling` in the runtime module set;
- confirm `smack.dll` is loaded or loadable from the same install directory;
- confirm `IMPP.Interface.dll` and `IMPP.Service*.dll` are present.
Stop condition:
- app is not .NET Framework / cannot inspect CLR runtime safely;
- required assemblies are missing;
- only `SendP2PMessage` is available but `SendTxtMessageByJid`/`MessageScheduling` cannot be reached.
### Gate B3: non-mutating dynamic observation
Pass condition:
- during one operator-approved manual test send, observe the expected chain:
- `SendTxtMessageByJid` or UI send method;
- `MessageScheduling.SendChatMessage`;
- `Chat.SendMessage`;
- `XMPPConnection.send`;
- capture only redacted method names, argument shape, message id/hash, and success/ack indicator.
Stop condition:
- method chain differs from static evidence;
- no success/ack indicator can be mapped;
- message id/idempotency cannot be correlated.
### Gate B4: sidecar dry run
Pass condition:
- sidecar can resolve the target JID/chat type;
- sidecar can build a preview object without sending;
- preview contains target, content hash, connector, and proposed entrypoint;
- audit event is appended.
Stop condition:
- no deterministic target resolution;
- preview cannot distinguish direct vs group chat;
- no auditable dry-run object.
### Gate B5: one sandbox send
Pass condition:
- one explicit test target only;
- one idempotency key only;
- one message content hash only;
- success/ack or local sent-record evidence captured;
- duplicate retry with same idempotency key is rejected locally.
Stop condition:
- duplicate send risk;
- wrong target risk;
- no post-send verification.
## A-route fallback
A remains the backup route if B cannot be made reliable.
A should not be arbitrary desktop control. It should expose only:
```text
search_contact(query)
open_conversation(target)
write_draft(text)
verify_draft(target, text_hash)
send_after_approval(approval_id)
upload_file(path)
verify_send_result()
```
A-route fallback is activated when any of these happen:
- B cannot attach/probe safely;
- required runtime classes are unavailable in the logged-in process;
- B can observe but cannot invoke without unstable side effects;
- B cannot provide idempotency/audit guarantees;
- sidecar bitness/runtime mismatch is too costly to resolve in the current cycle.
## MCP contract impact
The MCP business tools should not expose B/A internals directly. They should keep the stable user-facing shape:
```text
isphere_send_message(target_type, target_id, content_text, mode, idempotency_key, content_sha256)
isphere_send_file(target_type, target_id, file_path, mode, idempotency_key, file_sha256)
```
Connector metadata should show the route:
```text
connector = "implatform-sidecar" | "uia-rpa"
connector_stage = "preview" | "sandbox_send" | "production"
```
## Next implementation loop
R6c local preparation is now complete. Because the local development machine cannot log in, the online sandbox-send gate was converted into a returnable evidence package rather than a local live-send attempt.
Next node should be **R6d returned sandbox evidence intake** after the package is run in an online/internal sandbox and returned:
1. Check completed `SANDBOX-SEND-INPUTS.json`.
2. Check before/after read-only recorder outputs.
3. Confirm one explicit sandbox target, one content hash, and one idempotency key.
4. Confirm `success_ack_or_sent_record`.
5. Confirm no second send was made.
6. Do not enable production `send_message_after_approval` until returned evidence passes.
## Current status
B is selected as the primary send/file-send route. Runtime discovery, entrypoint metadata preflight, preview/dry-run audit, and the online sandbox evidence package are in place. A remains the fallback and now has concrete UIA selector/offline-blocker classifier support. Production send remains blocked until the returned sandbox package proves success/ack behavior.