On macOS the screenshot path lacked a counterpart to win32's
ClientToScreen. Detecting a target inside a cropped GrabScreen(bbox)
image and feeding the in-image (px,py) straight to Click silently
mis-clicks because the crop-origin offset is dropped.
- add CropToScreen(bbox, x, y): in-crop coords -> absolute physical
coords (pure crop-origin add, no scaling)
- GrabScreen docstring: warn about the crop-origin trap and not to
hand-roll screencapture -R (it takes logical points)
- Click 0%-pixel-change warning now names the common causes
- ljqCtrl_sop.md: add macOS subsection mirroring the win32 trap note
- macljqCtrl.py: macOS implementation of mouse/keyboard/screenshot/window
enumeration mirroring ljqCtrl API, plus AX accessibility control tree
(AXElements/AXFind/AXPress) as the macOS equivalent of UIA
- computer_use.md: add section 3 documenting the macOS platform branch
- .gitignore: whitelist memory/macljqCtrl.py
- Anchor file becomes temp/.active_project.<host pid>: dies with the GA process,
so closing GA auto-deactivates; re-activation requires user confirmation per SOP
- Multiple GA instances each keep their own active project, no cross-talk
- Startup sweep removes only legacy unsuffixed anchors and own-pid leftovers
(pid reuse); anchors of other pids are never touched — deliberately no
liveness probe, because os.kill(pid, 0) on Windows terminates the process
Activate a project with a file anchor (temp/.active_project); every turn
the agent_before hook appends an L1 context (rules + memory file pointer
+ wrap-up discipline) to the latest user message, while the full project
memory (L2) stays on disk for the model to read on demand.
Zero core changes: plugin + SOP + gitignore whitelist only.
When the user runs '/continue N' in stapp, the agent's in-memory context
is restored, but the UI previously showed only a single '✅ restored' line
— all prior chat bubbles were missing.
This change parses the target session log and reconstructs the
user/assistant message pairs into st.session_state.messages, so reopening
a session feels like the conversation was never interrupted.
* continue_cmd.py: add extract_ui_messages(path)
- parses model_responses log into [{role, content}, ...]
- groups multi-turn LLM calls (prompts whose text starts with the
'### [WORKING MEMORY]' header) into a single assistant bubble,
inserting the existing '**LLM Running (Turn N) ...**' marker so
fold_turns() renders them as collapsible segments.
- two small helpers (_user_text / _assistant_text) keep parsing local.
* stapp.py: in the /continue branch, resolve the target log path BEFORE
calling handle_frontend_command (which snapshots the current log and
would otherwise shift list_sessions indices), then replace
session_state.messages with the reconstructed history on success.
Falls back to the previous behavior for bare /continue or failure.
Co-authored-by: wjl2023 <wjl2023@users.noreply.github.com>
Rework the Feishu frontend so each user turn renders as a single
collapsible task card that patches itself in place, replacing the
dq-based streaming path that produced many fragmented messages.
- One _TaskCard per turn; hook reacts to summary / exit_reason events
from the agent loop and patches the same card.
- Each step is a foldable panel: header shows the summary, expanding
reveals three sections (auto-hidden when empty):
* Thinking - from response.thinking (separate field, not content)
* Tool Calls - tool name + truncated JSON args
* Output - response.content, with protocol tags stripped so
the header summary is not duplicated inside
- Final reply rendered as a schema 2.0 markdown card for consistency.
- Code-review pass per code_review_principles.md:
* _TaskCard owns only stateful card lifecycle (start/step/done/fail)
* Pure formatting extracted to module-level _build_step_detail and
_fmt_tool_call (no more reaching into card._private from the hook)
* Hook is a ~10-line dispatcher
* Flattened a 4-level nested lambda into a named function