Skip to content

Add LinkedIn Autopilot: read-only LinkedIn connector and loop - #7

Merged
encoreshao merged 18 commits into
mainfrom
linkedin-autopilot
Oct 21, 2026
Merged

encoreshao merged 18 commits into
mainfrom
linkedin-autopilot

Conversation

@encoreshao

Copy link
Copy Markdown
Owner

What changed and why

Adds LinkedIn Autopilot, Stage 1 (read-only): a linkedin connector and a linkedin-autopilot-loop that browse your own signed-in LinkedIn session at a human pace, read the feed, notifications and messages, and leave a summary (topics, posts worth reading, people to know) in the Content tab and Slack. The loop ships disabled. Likes, connection requests, daily caps, a kill switch and an action log are Stage 2 and are not in this PR; nothing here can click, like, post or send.

Connector (bin/connectors/linkedin.py, bin/linkedin_session.py, bin/linkedin_login.py)

  • Connect LinkedIn / Disconnect on the Connectors page. Connect opens a visible Chrome window (via Playwright, real Chrome channel) for the user to sign in, 2FA included.
  • The session is a persistent Chrome profile at <LOOP_ENGINEERING_HOME or ~/.loop-engineering>/linkedin/<id>/profile/ (mode 0700, outside the repo, resolved at call time). No password is stored. Cookies are only weakly protected at rest (Playwright's mock keychain); Disconnect wipes the profile.
  • LinkedIn logo (inline Simple Icons path, 13.0.0, since it was removed from the 16.x set) on the Connectors page and as the loop's sidebar icon.

Human-like browsing (bin/linkedin_humanize.py, bin/linkedin_browser.py)

  • Variable dwell, uneven scrolling, curved cursor movement, one to three extra pages, a minimum gap between page loads, a random start inside the active-hours window, some days skipped. headless is a setting (default off = visible Chrome).
  • HumanPage is the only code that drives the page; an AST test enforces that linkedin_browser.py never calls goto/click/type/etc. itself.
  • Pure, hardened HTML parsers (stack-based, linear time, challenge words matched only in <title>/<h1>/<h2>).

Loop (bin/loop_plugins/linkedin_autopilot.py, loops/linkedin-autopilot/)

  • LoopKit plugin modelled on RSS Watch. LinkedIn content is untrusted: sanitized before the prompt, the model runs sealed, every returned id must be one that was offered, wrong-typed answers are tolerated.
  • A security check or login wall stops that account and disconnects it (no retry until the user reconnects); a never-connected account is not browsed; a repeat same-day run is skipped; an unrecognised page layout is reported instead of looking like "nothing new".
  • Settings: headless, active_hours, topics. Registered disabled in config/loops.json.template (requires: ["social"]), with a Content-tab renderer, docs (docs/tasks/linkedin-autopilot-loop.md), README, TASK.md and CHANGELOG entries, and ja/zh/fr translations.

Risk to know about: LinkedIn has no API for this and its terms prohibit automation, so the account can be restricted. Pacing, the active-hours window, skipped days and stop-on-security-check reduce that risk; they do not remove it. Requires python3 -m pip install playwright and Google Chrome.

Verification

  • python3 -m pytest tests/ -q passes (3101 passed; 2988 on main before this branch)
  • CHANGELOG.md has a line under [Unreleased] (user-visible changes only)
  • Not yet verified against the real LinkedIn site. The CSS selectors for notifications, feed posts and messages are best guesses at LinkedIn's current markup and live in three matcher lambdas in bin/linkedin_browser.py. Please run a sandboxed manual check (scratch LOOP_ENGINEERING_HOME, Connect, run the loop once with --force) before enabling the loop; if a parser returns nothing on a real page the loop now reports "LinkedIn's page layout was not recognised" instead of silently finding nothing.

Golden eval (agent instruction/tool changes only)

This PR adds loops/linkedin-autopilot/prompt.md, which the checklist covers, but a before/after loop eval --golden would not exercise it: the golden suite (evals/golden/*) only runs the GitLab issue agent, and no LoopKit plugin prompt has a case type. I did not run it (it costs real money and would produce an identical before/after). The prompt is covered by unit tests (shape, caps, untrusted-content rule, id validation against offered items). Adding plugin-prompt eval cases is a follow-up for the harness.

Before
not run - see above
After
not run - see above

@encoreshao
encoreshao merged commit 7a6a161 into main Oct 21, 2026
1 check passed
@encoreshao
encoreshao deleted the linkedin-autopilot branch October 21, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant