文件历史

11 次代码提交

作者 SHA1 备注 提交日期
Rohit C Prasad 9553e0c0c1 Apply black formatting repo-wide 2026-07-14 22:11:09 -07:00
Rohit C Prasad 28ca416980 channel display names on subscription rows everywhere
/v1/subscriptions rows carry channel_name (from the channel buffer's
names map; the address stays the identifier), and every surface that
lists a subscribed channel renders '#ocw-test' with the address as a
tooltip/sub-label: the session drawer's Slack-channels panel, the
composer chip popover, the Messaging-routing table, and the connector
card's Sessions-listening block.
2026-07-13 08:36:44 -07:00
Rohit C Prasad 6e144c9c26 names everywhere: allow-list chips + channel dropdown show display names
Two ID-only surfaces from the owner's Slack pass:

- Allow-list chips showed 'U07JK68S4BH'. New persisted people directory
  (people.json: platform:user_id → display name) noted on every inbound
  — authorized or parked — and seeded from already-parked items;
  /v1/connectors carries allowed_user_names and backfills recent-sender
  names from it. Chips render the name (id in the tooltip).

- The recent-channels dropdown showed only 'slack:C0…' addresses.
  ChannelBuffer now records the resolved channel display name
  (channels.json grows a names map; the first shipped bare-messages
  format still loads) and /v1/channels/recent carries it. The picker
  shows '#ocw-test' with the address as a sub-label and filters on
  name, address, or last message text.
2026-07-13 08:36:44 -07:00
Rohit C Prasad 5b0714b479 two-way connectors: park unauthorized messages, listening sessions, gateway hot-reload (§19)
First contact on Slack took a double-send: the allow-list (closed by
default, correctly) silently DROPPED the first message; the sender only
surfaced under Recent senders and had to message again after being
allowed.

- ParkedStore (parked.json, capped): the gateway's allow-list drop now
  parks the message via an on_unauthorized callback instead of losing
  it. The connector card resolves each item inline: Allow & deliver
  (allow-list the sender and re-inject the original message through the
  normal inbound path — buffer + subscriptions, no re-send), Allow
  only, or Dismiss.
- Sessions-listening block on the two-way connector card — the
  per-connector cut of the global Channel-subscriptions table (which
  lives under Messaging routing, where the owner didn't find it) with
  inline unsubscribe.
- Gateway hot-reload: connect/disconnect of a messaging connector
  refreshes the listeners in-process (a platform socket authenticates
  at connect time, so new creds mean reopening that socket — and
  nothing else). Pasting tokens now takes effect immediately; before,
  the listener only started in the app lifespan, so new tokens
  silently did nothing until a sidecar restart.
- Slack setup instructions now name users:read / channels:read /
  groups:read (sender + channel display names resolve via these; the
  owner's bot lacked them, so senders read 'unknown').

Tests: park→resolve lifecycle incl. allow_deliver reaching subscriber
+ buffer; ParkedStore persistence/cap; refresh_gateway swaps
listeners; 3 e2e flows on the connector card.
2026-07-13 08:36:43 -07:00
Rohit C Prasad 08460e2a85 channel picker: durable suggestions, real dropdown, link/name parsing
Three fixes from the owner's Slack-on-PM setup (2026-07-04):

- The recent-channels suggestion list was an in-memory ring buffer that
  emptied on every sidecar restart — useless as a picker source.
  ChannelBuffer now persists (best-effort JSON at channels.json) when
  given a state_path; catch-up messages survive restarts too.

- The suggestions rode a native <datalist>, which WKWebView (the macOS
  desktop shell) doesn't render at all. ChannelPicker now uses a
  hand-rolled popover (focus opens, typing filters on address or last
  message text, mousedown picks) that behaves the same on Mac, Windows
  and web.

- resolve_channel accepts Slack's channel Copy-link URL
  (…slack.com/archives/C0123 → slack:C0123, case-normalized) and
  rejects a bare #name instead of storing it literally — a literal
  #general never matches inbound slack:C… traffic, so it was a dead
  subscription. The subscribe route returns a paste-the-ID hint, the
  Sources drawer shows it inline, and the misleading '#channel'
  placeholder is gone.

Tests: resolve_channel forms, buffer persistence + corrupt-file
startup, route rejection; two new e2e flows (popover, link/name adds)
with backend-parity resolution in the fixture mock.
2026-07-13 08:36:36 -07:00
Rohit C Prasad a40218fe8f tests: isolate the state dir for EVERY test (autouse conftest fixture)
Any test that built a SessionManager without COWORKER_STATE_DIR read the
developer's machine-global SecretStore — including their real cloud
sign-in, so test session creation emitted REAL coworker_session_created
events to prod (discovered as burst noise in the telemetry table). Six
tests turned out to be riding the developer's connected Slack profile for
inbound-delivery gating; they now connect a fake Slack in their own store.
Side effect: the suite is ~45% faster (no live telemetry threads).
2026-07-13 08:35:09 -07:00
Devika 9d7611d91b UI-Refresh Phase 2: structured connector messages
Connector inbound messages carry a display-only source sidecar so the GUI
renders a rich card (logo, channel/person names, ids-on-hover, time) while
the model still receives the framed text. The sidecar is persisted with the
message and stripped unconditionally before the provider feed. Frontend adds
ConnectorMessageCard wired through an extracted, testable itemsFromMessages.
2026-06-29 07:30:38 +05:30
Devika 362b4dfa9d messaging: route DMs to a designated session and retire the super-agent
DMs depended on the legacy always-on super-agent (a hidden __superagent__
session + a GUI surface that wasn't reachable from the sidebar). In the durable-
session model that surface is vestigial: a DM should just drive a real session.

DM routing:
- prefs-backed dm_session designation + GET/POST /v1/messaging/dm-route.
- _dispatch_inbound routes a DM to the designated session (delivered like any
  background turn, so it streams live via the per-session bus) or, with none set,
  parks it as unrouted for visibility.
- GUI 'Direct messages' control in Integrations (pick the session, or park).

Retire the super-agent:
- delete connectors/superagent.py + its exports; remove the SuperAgent build/run,
  _sa_* GUI surface, superagent config/status, SUPERAGENT_SESSION_ID, the
  /ws/superagent socket + /v1/superagent routes; start_gateway keeps the gateway
  + scheduler only. Scheduled-task 'done' notices now broadcast to the run's own
  session view.
- GUI: delete SuperAgentView + the disabled Super-agent manage tab + getSuperagent.
- myhelper persona kept resolvable (persisted sessions may reference it); docstrings
  de-super-agent-ified.
- tests: test_dm_routing.py; updated the DM case in test_subscriptions; dropped the
  super-agent tests in test_connectors.
2026-06-28 12:57:16 +05:30
Devika 8ccc14d0b9 Messaging GUI (manage): subscribe/unsubscribe + recent-channels picker
Turns the view-only subscriptions surface into a managed one.

- REST: POST /v1/subscriptions (subscribe; resolves a Slack #mention or address),
  POST /v1/subscriptions/remove (unsubscribe), GET /v1/channels/recent (the picker's
  recently-seen source from the channel buffer).
- ChannelPicker: an input + datalist of recent channels (pick) with free typing
  (slack:C0123 or #channel) — the "recent list + type-the-id" picker.
- Global (Integrations): each row gets an unsubscribe ×; an add form (session select
  + ChannelPicker + Subscribe).
- Per-session: the composer-head plug chip opens a popover to add/remove this
  session's channels.

Tested (subscribe/unsubscribe/recent endpoints + #mention resolution).
2026-06-28 09:22:28 +05:30
Devika d3007b538e Messaging GUI (view-only): channel subscriptions panel + per-session chip
A human-facing view of the agent-driven subscriptions (creation stays via ask_user;
GUI manage comes next).

- Global: a "Channel subscriptions" table in Integrations — each session → channel it
  listens to (inbound) and the channel its Inbox routes to (outbound), with a warning
  when they collide on one channel. GET /v1/subscriptions (server-joined with session
  title/agent + routing), polled.
- Per-session: list_sessions gains `subscriptions`; the composer head shows a plug chip
  ("📡 N") titled with the channels when the active session listens to any.

Read-only this pass (by design). Next: GUI-managed subscribe/unsubscribe with
a channel picker (recently-seen list + type-the-id). Tested (endpoint + collision flag).
2026-06-28 09:13:53 +05:30
Devika a4989303dc Phase 5: channel subscription (inbound messaging)
A session can subscribe to a messaging channel and receive its messages — the
inbound counterpart of (outbound) Inbox routing.

- SubscriptionStore: persisted (session_id, channel) records, permanent until the
  agent/user unsubscribes or the session is deleted (the one implicit teardown).
  Channel = "<platform>:<chat_id>".
- Agent tools (messaging personas): subscribe_channel / unsubscribe_channel /
  list_subscriptions / get_channel_messages. The agent bootstraps via ask_user to
  learn the channel; subscribe_channel parses a Slack <#id|name> mention or an
  address. A guard warns when a subscription collides with an Inbox-routing target.
- Gateway dispatch: token → Inbox (unchanged); else a channel message is buffered
  (ring buffer for get_channel_messages) and fanned out to every subscribed session
  via manager.deliver_to_session (busy→steer / idle→background turn, shared with
  self-wake — no socket needed); a DM with no subscription → the super-agent.
- Loop prevention is free: the Slack adapter already drops bot-self messages, and
  one bot identity makes agent-↔-agent loops impossible.

v1 filter = the subscription itself (all of a channel's non-bot messages); mention
/thread filtering deferred. Sessions are durable (never end except on explicit
delete) — documented in MESSAGING-AND-SESSIONS.md along with these decisions.

Tests: store CRUD/persistence, parsing, buffer, tools, and the fan-out dispatch.
Full suite 449 passed (3 pre-existing SDK-import failures unrelated).
2026-06-28 03:47:05 +05:30