Skip to content

fix ci errors - #1486

Closed
JayGarland wants to merge 69 commits into
chenhg5:mainfrom
JayGarland:fix-ci-errors
Closed

fix ci errors#1486
JayGarland wants to merge 69 commits into
chenhg5:mainfrom
JayGarland:fix-ci-errors

Conversation

@JayGarland

Copy link
Copy Markdown
  • fix(copilot): probe.close() deadlock on Windows — /list now works
  • feat(relay): bidirectional Copilot↔Reasonix relay with dynamic prompt injection
  • fix(relay): use --data-dir instead of --config, add OpenCode relay injection
  • fix(reasonix): relay session ID persistence — multi-turn context now survives
  • fix(reasonix): skip /new on session resume — multi-turn relay context works
  • test(reasonix): add regression test for empty sessionID UUID generation
  • fix(reasonix): always call /new — serve state corrupts on interruption
  • fix(relay): stop over-aggressive relay injection — direct messages now answered
  • fix(relay): per-source burst limit + CAS identity injection (audit pass)
  • ci: nexus-race workflow for the private fork
  • Inject seat-specific persona file on first Send() (copilot + opencode)
  • Inject CC_PERSONAS_DIR; read persona files from data/personas/ not workDir
  • Add reaction carry-forward + group context injection infrastructure
  • Add reaction debug logging to diagnose missed reaction events
  • Add session handoff injection: prepend handoff_file on cold-start
  • Add on-mention context injection from aggregated seat sessions
  • Fix on-mention context injection: WasMentioned field bypasses stripBotMention
  • Fix handoff cold-start: drop AgentSessionID check, add slog
  • Fix silent heartbeat: DropReply field suppresses agent reply at engine level
  • Fix silent heartbeat: mutePlatform wrapper + suppress history saves
  • Fix privacy P0: filter on-mention context by chat_id
  • Add progress_style = "single": keep all streaming text in one Telegram message
  • Add project-level disabled flag to skip a seat at startup
  • Fix on-mention context truncating messages at 200 bytes (not chars)
  • Add relay auto-ack + @-mention handback routing
  • Suppress relay tool output when hidden
  • Inject relay handbacks into source sessions
  • Add feature-start board workflow
  • Make feature-start refresh seats lazily
  • Remove feature-start seat flags
  • Remove feature-start shortcut alias
  • Add engine-level smoke tests for lazy feature refresh
  • Restrict feature-start to chef
  • Add config skill_dirs support for per-seat skill discovery.
  • Test chef-seat vs shared skill discovery for Nexus handoff move.
  • feat: refresh chef-flash seat on feature start
  • Add redirection hint when running feature-start on chef-flash-seat
  • Fix duplicate newline separators in streaming text and refine feature-start redirection logic
  • Rename secondary Chef feature start routing functions to peer Chef
  • Fix duplicate spacing by using joined trimmed text parts suffix check
  • Fix relay concurrency bug by checking session lock in HandleRelay
  • Extend TestHandleRelay_SessionLocking regression test to cover multi-session concurrency
  • Add TestInteractiveSessionQueueing_SequentialProcessing integration test
  • Document same-session follow-up queueing behavior in usage docs
  • Implement status API endpoint, status CLI command, and enhanced relay timeout error messages
  • Refine status endpoint to report working/crashed/hung/idle states and configure default relay timeout to 60s
  • Implement proactive push notifications for process crashes and session timeouts
  • Implement Telegram Forum Mode and thread-scoped workspace pattern routing
  • Remove auto-ack feature and fix relay test race condition
  • Skip persona file injection for Copilot CLI sessions when using --agent
  • Implement Auto-bind Relay Config by parsing [[relay.bindings]] from config.toml
  • fix(core): fix race between Send goroutines and cleanupInteractiveState (fix(core): fix race between Send goroutines and cleanupInteractiveState #1436)
  • fix(core): throttle message recall fallback probes (fix(core): throttle message recall fallback probes #1321)
  • Increase on-mention message truncation limit to 5000 characters
  • Fix topic session isolation: Use MessageThreadID directly when non-zero, bypassing Chat.IsForum check
  • Fix relay session key parsing under Telegram Topic thread context
  • Normalize relay binding lookups and registrations using base chat ID
  • Implement pre-flight base git pull hook before workspace creation
  • fix: resolve THREAD_ID template parsing and improve workspace resolution in engine
  • feat: add Telegram slash command /prune to prune abandoned task worktrees
  • fix: enforce disabled_commands block on built-in command case blocks
  • refactor: remove permanently disabled cmdFeatureStart and associated tests
  • test: update inline disabled commands test to match feature-start deletion
  • feat: implement General @chef automatic topic creation intake
  • fix: propagate static [projects.agent.options.env] to per-workspace agent instances
  • feat: per-event hung detection + event-triggered status board (disabled)
  • Add dispatch interception flow
  • fix: add role-to-project alias map for [DISPATCH] To: (B2)
  • fix: resolve nexus-race build error and Node.js 20 deprecations in CI

Jie added 30 commits June 27, 2026 16:23
- agent/copilot: add probeCloseTimeout=5s to prevent permanent block on <-ps.done
  when copilot --headless --stdio doesn't exit cleanly after context cancel
- agent/reasonix: implement SessionEnvInjector, dynamic identity/relay/send
  prompt injection from session env vars (no file dependency, no hardcoded paths)
- agent/reasonix: SSE text dedup via cumulative delta extraction (turnTextBuf)
- core/engine: cmdList diagnostics + replyWithError fallback (Reply->Send)
- core/engine: inject CC_CONNECT_BIN, CC_CONNECT_CONFIG, CC_RELAY_TARGET into
  session env (auto-resolved from relay bindings)
- core/engine: add configPath field + SetConfigPath() setter
- cmd/cc-connect: wire engine.SetConfigPath(absConfigPath)
- platform/telegram: KeepPreviewOnFinish() -> true (eliminates message flash)
… injection

Core fixes:
- agent/copilot: dynamic relay injection (identityInjected atomic.Bool, CRITICAL RULES)
- agent/copilot: probeCloseTimeout=5s (probe deadlock fix)
- agent/reasonix: Send() select+ctx (block forever fix)
- agent/reasonix: buildSubmitBody \\ -> / for bash compat (crash fix)
- core/engine: HandleRelay goroutine+timeout (Send blocking fix)
- core/engine: subcommandIndex() skip --config before dispatch (instance lock fix)
- core/relay: sendToGroup DEBUG->WARN, diagnostic logs

Quality:
- Self-audit passed: build, vet, agent/CUJ/Relay tests
- data race fix: identityInjected bool -> atomic.Bool
- relay CLI: --config support in runRelaySend
- unsolicited events: e.send() -> e.sendWithError() with error logging
- FIXME markers for multi-turn context and session ID persistence
- config.toml: both seats bypassPermissions
…jection

- agent/copilot/reasonix/opencode: relay command uses --data-dir instead of
  --config (--config consumed by subcommandIndex, unreachable in runRelaySend)
- agent/opencode: add dynamic relay injection (sessionEnv, identityInjected
  atomic.Bool, CRITICAL RULES) — same pattern as Reasonix/Copilot
- agent/opencode: pass sessionEnv from Agent to newOpencodeSession
- agent/reasonix: parse CC_DATA_DIR in identity injection, forward-slash convert
- agent/opencode/session_test.go: update newOpencodeSession call signature

All relay directions now work: Copilot↔Reasonix, Reasonix↔OpenCode, Copilot↔OpenCode
…survives

- agent/reasonix/session.go: add newReasonixSessionID() (UUID v4) to generate
  a client-side session ID when sessionID is empty (relay case). Previously
  CurrentSessionID() returned empty string, so saveRelaySessionID never persisted it
  and every relay started a fresh reasonix serve session.
- agent/reasonix/session.go: always call /new in newSession() (reasonix serve
  does not support session resume). Removed FIXME from CurrentSessionID().
- agent/reasonix/session_test.go: add POST /new handler to
  TestReasonixSession_httpPost_ErrorIncludesBody mock server (previously
  skipped /new when sessionID=test).
- core/engine.go: remove resolved FIXME comment block about Reasonix relay
  session ID persistence.
… works

Previous commit (99d1432) always called /new even with a persisted session ID,
defeating multi-turn relay. Reasonix serve keeps the session alive across SSE
disconnects — calling /new discards it.

- Restore conditional /new: only call when sessionID is empty or "new".
  When a persisted UUID is passed (relay resume), skip /new so the existing
  serve session is reused.
- Verified end-to-end with daemon logs:
  Round 1: session_id="" → generated rs-9bb13d30... → /new
  Round 2: session_id=rs-9bb13d30... → reusing existing session (skipped /new)
reasonix serve breaks permanently after the first model call fails or the
SSE connection is killed mid-turn. Reusing sessions (skip /new) caused
subsequent relays to hang. Always calling /new gives a clean serve session
each time. The client-side session ID is still generated and tracked for
cc-connect session manager persistence.

Root cause: reasonix serve internal state corruption bug — only fixable
by process restart.
…w answered

Copilot and OpenCode identity injection told the model to relay messages
but did not explicitly say "ONLY relay when user says relay to X". Direct
questions were being forwarded as relays instead of answered.

Added explicit rules:
- ONLY relay when user EXPLICITLY says "relay to X: message" or "relay to X"
- If it is a direct question (no "relay to" prefix), ANSWER IT YOURSELF
Findings from a fresh-instance audit after the 3-seat Nexus trial:

* Loop defense: no hop count, no rate limit. An agent that misinterprets a
  normal reply as a relay command (Known Issue #11) can cascade silently.
  Adds per-source rolling-window burst limit in core/relay.go (default
  10 relays / 60s per <chatID>::<from>). Configurable via [relay].burst_max
  and [relay].burst_window_secs in config.toml; burst_max=0 disables.
  Regression tests in core/relay_test.go cover reject-after-budget,
  disabled-when-zero, and per-source isolation.

* Check-then-act race: the prior atomic.Bool fix on identityInjected
  prevented torn reads but not the logical race — two concurrent Send()
  calls could both observe false, both run the ~80-line injection block,
  and both Store(true), producing a double-prefixed prompt. Replaced
  Load()/Store() with CompareAndSwap(false, true) in
  agent/copilot/session.go and agent/opencode/session.go to atomically
  claim the injection slot.

* gofmt -w on all six touched files.

Build: go build ./... PASS
Tests: go test ./core/ -run "TestRelayManager|TestCUJ|TestRelay" PASS
       go test ./agent/copilot ./agent/opencode (touched suites) PASS
       (Pre-existing opencode TestAvailableModels_* and config Windows
       path-normalization failures unchanged — tracked as HANDOFF #12/#13.)
Runs go build + go test -race on the Nexus audit's touched packages
(core, agent/{copilot,opencode,reasonix}, config), plus a focused -v run
on TestRelayManager_Burst|TestCUJ|TestRelay. Linux runner because the
dev host (Windows) has CGO off and cannot run -race locally.

Guarded by `if: github.repository == 'JayGarland/cc-connect'` so it stays
inert when rebased onto upstream/main or further forked.
Reads {workDir}/{project}.md if it exists and appends its content to
the identity injection on the first message. Enables per-seat persona
instructions (e.g. chef-seat.md for the Chef orchestrator role) without
hardcoding seat names in the injection code. File is optional — silently
skipped when absent, so existing seats are unaffected.
…rkDir

Adds CC_PERSONAS_DIR = data_dir/personas to the session env injected in
core/engine.go (both initial-session and relay-session paths). Copilot
and OpenCode session agents now resolve the persona file from
CC_PERSONAS_DIR first, falling back to {workDir}/{project}.md for
backwards compatibility. This eliminates the per-worktree manual copy
footgun: persona files live in one place (F:\nexus\data\personas\) and
are read by all seats regardless of which worktree they operate in.
- core/message.go: add ReactionEmoji field to Message struct
- core/session.go: add PendingReaction, AddReaction(), DrainReactions() to Session
- core/context_inject.go: new file — formatPendingReactions, aggregateSeatMessages, formatGroupContext
- core/engine.go: reaction early-return in handleMessage; drain+prepend on next real message
- platform/telegram/telegram.go: subscribe to message_reaction updates; handleMessageReaction()
  filters by allow_from, extracts first emoji, dispatches as ReactionEmoji signal

Reactions (up to 3) are stored in session state and prepended to the next outbound
message as "[Pending reactions: 👎 3min ago]" before reaching the agent.
handleMessageReaction now logs at Info level on every fired event
(user, chat, msg ID, emoji) and Debug level on every skip path. This
lets us distinguish: update never arrived (Telegram bot not admin in
group) vs. update arrived but filtered.
On the first message of a brand-new session (empty history + no agent
session ID), read the seat's configured handoff_file and prepend its
content as `[Handoff: <filename>]` before the user message. Gives the
agent session context that would otherwise be lost across cc-connect
restarts or /new resets.

Config: add handoff_file to [projects.agent.options] per seat (chef-seat,
copilot-seat). File is read silently on cold-start; missing file is a no-op.
New Engine.SetHandoffFile(path) setter; read from agent options map in main.
When a message to any seat contains "@", cc-connect reads the last N
history entries across all seat session files and prepends them as
[Group context (last N)] before forwarding to the agent. N is
configured per-seat via on_mention_context in [projects.agent.options].

Uses the existing aggregateSeatMessages()/formatGroupContext() from
core/context_inject.go (already committed). New Engine.SetOnMentionContextN
setter; default 0 = disabled. Applied to secretary/chef/copilot/opencode
(N=10); reviewer-seat stays at 0 (read-only, no context needed).

Also removes secretary-seat.md Mechanism 2 (raw session file paths).
cc-connect auto-injects context on @-mention; no manual file reads needed.
…tMention

Telegram's stripBotMention() removes @botName from message text before the engine
sees msg.Content, so strings.Contains(msg.Content, "@") was always false.

- core/message.go: add WasMentioned bool to Message struct
- platform/telegram/telegram.go: capture mentioned = strings.Contains(text, "@"+botName)
  before stripping, set WasMentioned on all three dispatch paths (text, photo, document)
- core/engine.go: replace strings.Contains(msg.Content, "@") with msg.WasMentioned
- cmd/cc-connect/main.go: defensive type switch for on_mention_context (int64 + int)
The opencode engine pre-populates AgentSessionID at startup by reconnecting
to its last running session even when the cc-connect session file is deleted.
This caused the cold-start condition to always evaluate false.

History length alone is the right sentinel: it reflects cc-connect's own
recorded turns, which are always zero on a genuinely fresh session, regardless
of what the agent backend may have reconnected to.

Also adds slog.Info on injection (observable in logs) and slog.Warn on
unreadable handoff file to aid diagnostics.
…e level

silent = true in heartbeat config was only suppressing the emoji notification,
not the agent's response. Added Message.DropReply bool that gates the isSilent
check in processInteractiveEvents so the platform send is skipped entirely when
the heartbeat is configured silent. The agent still runs its check internally
(tools, awareness), it just posts nothing to chat.

Also added DropReply to the function signature of processInteractiveEvents
(queued messages always pass false).
Two separate concerns, now properly separated:
- Platform sends: ExecuteHeartbeat wraps targetPlatform with mutePlatform
  when silent=true. This stops ALL outbound Telegram sends before they
  happen — no streaming preview is created, no final reply is posted,
  no send-then-delete ghost messages.
- Session history: processInteractiveMessageWith gates the user-turn
  AddHistory on !msg.DropReply; processInteractiveEvents gates both
  assistant-turn AddHistory sites (EventResult + abnormal exit) on
  !dropReply. Heartbeat content never appears in session JSON, so
  aggregateSeatMessages() for on-mention context sees only real messages.

The DropReply bool on Message remains as the carrier from ExecuteHeartbeat
to the message-processing pipeline. isSilent || dropReply checks in the
send path are kept as a harmless fast path (mutePlatform would discard
anyway, but early return avoids hook emissions and workspace pool calls).
aggregateSeatMessages was reading ALL sessions across ALL seats without
any chat boundary. A private DM to Chef leaked into the group on-mention
context injected for Secretary because both sessions lived in the same
session JSON file (keyed by workDir hash, not chat_id).

Fix: use UserSessions (sessionKey → []internalID) to build an allowed-ID
set before reading history. extractSessionChatID() parses the chat-id
component from any "platform:chatID:..." key, handling workspace-prefixed
variants. Only sessions whose sessionKey's chatID matches the inbound
message's chatID are included.

Hard privacy rule: on-mention in a group → only group sessions.
on-mention in a DM → only that DM's sessions. No cross-chat bleed.

Also fixes test call sites for processInteractiveEvents which gained a
dropReply bool parameter in a prior commit.
…m message

In compact mode, every hidden tool event calls freeze()+detachPreview() which
clears the preview handle and forces finish() to fall back to p.Reply() — the
user sees fragmented multi-message output. The quiet display mode already uses
appendSeparator to keep text in one card, but quiet is global and changes other
display behavior.

New "single" progress_style for Telegram adopts the quiet appendSeparator path
for hidden tool/thinking events while otherwise behaving like compact (streaming
edits at 500ms interval). On finish(), the one preview message is edited to the
full response — no second message.

Changes:
- core/progress_compact.go: add progressStyleSingle constant + normalize
- core/interfaces.go: add StreamPreviewIntervalOverride optional interface
- core/streaming.go: honor per-platform interval override; upgrade finish()
  success logs to Info with message_id
- core/engine.go: use appendSeparator path when progressStyle == "single"
- platform/telegram: parse "single", implement StreamPreviewIntervalMs()=500
disabled = true in a [[projects]] block causes cc-connect to skip
that project entirely without removing the config. The block stays
in place for easy revival by flipping the flag back to false.

Disabled projects are filtered before all indexed engine loops so
cfg.Projects and engines[] stay in sync. A startup info log
confirms which seat was skipped.
Two bugs in formatGroupContext:
1. len(c) > 200 measures bytes, not characters — a Chinese char is
   3 bytes, so the effective limit was ~66 Chinese characters, which
   cut off most messages after 1-2 sentences.
2. c[:200] byte-slices a UTF-8 string, which can split a multi-byte
   character and produce a corrupted string.

Fix: convert to []rune before truncating, and raise the limit from
200 bytes to 500 runes (~500 Chinese chars / ~100 English words).
With on_mention_context = 20, the injected block stays under ~10k
chars while providing meaningful context per entry.
Auto-ack: RelayManager.Send() now posts an immediate "[<target>] ✅ received
— implementing: <task>" from the target engine before HandleRelay starts.
Boss sees receipt confirmation without waiting for the full response.

Handback: After HandleRelay completes, if the source engine exposes a bot
username via BotUsernameProvider, the response visibility label is prefixed
with @<sourceUsername>. On Telegram this routes the handback as an @-mention
to the source bot (Chef), so Chef's session receives the relay result as an
incoming message rather than requiring a manual close-the-loop reply.

Infrastructure:
- BotUsernameProvider interface in core/interfaces.go
- BotUsername() on telegram.Platform (wraps private botUsername())
- Engine.BotUsernameForPlatform() for relay manager lookup
- Relay tests updated to expect ack as first target message
Jie and others added 22 commits June 30, 2026 18:07
…te (chenhg5#1436)

cleanupInteractiveState sets state.agentSession = nil under state.mu,
but three Send goroutines read state.agentSession without holding the
lock. When an agent process exits before the Send goroutine is
scheduled, cleanup can nil agentSession, causing a nil pointer
dereference panic.

Fix: capture agentSession into a local variable under state.mu, then
use the local in the goroutine. If the captured value is nil, the
goroutine returns an error instead of panicking.

Co-authored-by: tanghongliang <tanghongliang@citos.cn>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(core): throttle message recall fallback probes

Avoid repeatedly probing the platform for the same active message while retaining recall detection for new turns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(core): address recall probe review feedback

Document the Feishu recall probe fix and keep the monitor interval unchanged while relying on per-message throttling.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
…gent instances

getOrCreateWorkspaceAgent (multi-workspace mode) built new per-Topic agent
instances via WorkspaceAgentOptions(), which omitted opts["env"] entirely —
static env vars configured under [projects.agent.options.env] (e.g.
CC_PERSONAS_DIR, CC_PROJECT) never reached workspace-scoped instances,
so persona injection silently fell back to no-persona default behavior
for any seat using workspace_pattern.

Add WorkspaceConfigEnvProvider, an optional interface agents can
implement to expose their static configured env map; getOrCreateWorkspaceAgent
uses it as a fallback when WorkspaceAgentOptions doesn't already carry
opts["env"]. Implemented for the copilot agent (chef-seat's harness,
the seat that first surfaced this bug via task-855/task-886).

Includes regression coverage in multi_workspace_test.go and
copilot_test.go.
L-0083/L-0085: Session gains LastOutputAt, touched on every agent event
(not just turn boundaries like the existing UpdatedAt). /status now
reports last_output_ms_ago and classifies "hung" using a tighter,
independently-configurable threshold (relay.hung_after_secs, default
90s) alongside the original relayTimeout-based check — catches a
genuinely stalled turn long before the old turn-boundary-only signal
would (see the L-0081 dev-pro incident this was built to catch).

L-0092/L-0094/L-0097: New core/statusboard.go — a standing per-seat
status message, event-triggered (turn start/end, relay dispatch, hung
transitions) rather than timer-polled. Deliberately not wired through
the [[projects]] seat/engine machinery: no persona, no LLM calls, posts
via its own dedicated bot token so it sidesteps the General-channel
execution/advisory seat split entirely. Ships with status_board.enabled
= false in config.toml pending bot registration and a stability window
on the hung-detection signal it depends on.

L-0103/L-0104: relay.go Send() now delivers the handback injection and
group visibility message on a fresh context.Background()-derived
context instead of reusing the caller's ctx — a result HandleRelay
already produced must not be silently dropped just because something
unrelated (e.g. a new message preempting the target session's event
reader) cancelled the original context before delivery ran. Mirrors the
context.Background() pattern already used in the adjacent crash/hung
notification branch.

Regression coverage: statusboard_test.go (6 cases), api_test.go (2 new
cases proving the LastOutputAt signal fires independently of UpdatedAt),
relay_test.go (delivery survives an already-cancelled caller context).

Also: .gitignore now excludes core/test_ws_*.json, generated by
TestCmdShell_MultiWorkspaceUsesSharedBindingWorkDir at test time.
@JayGarland
JayGarland requested a review from chenhg5 as a code owner July 3, 2026 00:14
Copilot AI review requested due to automatic review settings July 3, 2026 00:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a broad “CI fixes + Nexus workflow” bundle that extends CC-Connect’s relay/status/dispatch infrastructure, adds Telegram topic/intake and streaming modes, and updates multiple agents to support dynamic identity/persona/relay prompt injection.

Changes:

  • Add Telegram enhancements: forum-topic intake from General, reaction updates, improved thread scoping, and a new progress_style = "single" streaming mode.
  • Extend core relay + status capabilities: shorter relay timeout, “hung” detection via last output, per-source burst limiting, /project/status API + CLI, and an (optional) Telegram status-board poster.
  • Add Nexus-oriented automation: dispatch interception flow, feature-start workflow/context propagation, skill_dirs configuration support, and multi-workspace env propagation fixes.

Reviewed changes

Copilot reviewed 51 out of 52 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
platform/telegram/telegram.go Adds reactions handling, forum-topic intake, thread scoping updates, and “single” streaming mode hooks.
platform/telegram/telegram_test.go Adds tests for general-topic intake, thread ID behavior, and progress style option.
docs/usage.zh-CN.md Documents same-session message queueing behavior (ZH-CN).
docs/usage.md Documents same-session message queueing behavior (EN).
core/workspace_pattern_test.go Adds tests for workspace pattern thread ID extraction/routing.
core/streaming.go Adds stream preview interval override support and adjusts finish logging.
core/statusboard.go Introduces an optional Telegram-backed status board component (new).
core/statusboard_test.go Unit tests for StatusBoard transitions/rendering behaviors.
core/skill_dirs.go Adds parsing/normalization/merging for configurable skill discovery directories.
core/skill_dirs_test.go Tests for skill_dirs parsing/merging and engine application.
core/session.go Adds last-output timestamp tracking and pending reaction carry-forward storage.
core/relay.go Refines relay timeout defaults, adds hung/burst-limit controls, binding normalization, and delivery context behavior.
core/progress_compact.go Adds single as a recognized progress style.
core/multi_workspace_test.go Adds regression coverage for propagating config env to per-workspace agent instances.
core/message.go Adds ReactionEmoji / WasMentioned / DropReply fields to Message.
core/interfaces.go Adds BotUsernameProvider, StreamPreviewIntervalOverride, TaskTopicCreator, and WorkspaceConfigEnvProvider interfaces.
core/i18n.go Adds i18n key for feature-start and formatting adjustments.
core/feature_start.go Implements feature-start seat refresh + lazy context injection pipeline (new).
core/feature_start_test.go Tests feature board + lazy refresh behavior.
core/feature_board.go Implements persisted feature board store/state (new).
core/dispatch.go Adds strict [DISPATCH] interception, ledger, and polling watcher (new).
core/dispatch_test.go Tests dispatch block parsing and result detection.
core/context_inject.go Adds reaction/group context aggregation helpers for prompt injection (new).
core/cmdopts.go Adds helper to convert config env slices into a map for agent options.
core/api.go Adds /project/status endpoint and polling integration for status board hung detection.
core/api_test.go Adds tests for /project/status classifications incl. stale output detection.
config/config.go Adds status_board config, relay bindings/burst/hung config, per-project disabled flag, workspace_pattern field.
config/config_test.go Adds config fixture/test for relay.bindings parsing.
cmd/cc-connect/worktree.go Adds cc-connect worktree prune CLI for pruning abandoned task worktrees.
cmd/cc-connect/status.go Adds cc-connect status CLI that calls /project/status.
cmd/cc-connect/relay.go Lets relay CLI derive data dir from --config when provided.
cmd/cc-connect/main.go Improves subcommand parsing, supports new commands, filters disabled projects, wires status board + new engine options.
CHANGELOG.md Adds changelog entry for Feishu recall fallback probe throttling.
agent/reasonix/session.go Adds session env forwarding, UUID session IDs, streaming dedupe, and first-send injection.
agent/reasonix/session_test.go Updates tests for newSession signature and /new route behavior.
agent/reasonix/session_id_test.go Adds regression test for empty session ID generation.
agent/reasonix/reasonix.go Forwards session env into sessions (implements SessionEnvInjector pattern).
agent/opencode/session.go Adds first-send identity/relay injection and persona loading support.
agent/opencode/session_test.go Updates constructor signature test coverage.
agent/opencode/opencode.go Passes sessionEnv separately into session constructor.
agent/copilot/session.go Adds first-send identity/relay injection, persona loading (skippable with --agent).
agent/copilot/session_test.go Adds test coverage for persona injection skip behavior.
agent/copilot/copilot.go Adds bounded probe close timeout; propagates config env into workspace options; adds ConfigEnvMap.
agent/copilot/copilot_test.go Adds test verifying WorkspaceAgentOptions propagates env map.
agent/claudecode/session.go Adds seat persona loading into system prompt assembly.
agent/claudecode/session_test.go Adds test ensuring persona content is included in prompt file.
.gitignore Ignores multi-workspace test fixture JSON files.
.github/workflows/nexus-race.yml Adds fork-scoped race/build workflow for Nexus fork.
.github/workflows/ci.yml Updates GitHub Actions versions for checkout/setup-go/setup-node.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/statusboard.go
Comment on lines +15 to +17

tgbot "github.com/go-telegram/bot"
)
Comment thread core/relay.go
Comment on lines 505 to +510
func parseSessionKeyParts(sessionKey string) (platform, chatID string, err error) {
// Format: "platform:chatID:userID"
// Relay session format: "relay:sourceProject:chatID"
parts := strings.SplitN(sessionKey, ":", 3)
parts := strings.Split(sessionKey, ":")
if len(parts) < 2 {
return "", "", fmt.Errorf("invalid session key format: %q", sessionKey)
}
if parts[0] == "relay" && len(parts) == 3 {
// For relay sessions, chatID is the third part: "relay:sourceProject:chatID"
return parts[0], parts[2], nil

Comment on lines +429 to +432
if update.MessageReaction != nil {
p.handleMessageReaction(ctx, update.MessageReaction)
return
}
Comment on lines +713 to +723
key := fmt.Sprintf("%d:%d", msg.Chat.ID, msg.ID)
p.topicIntakeMu.Lock()
if p.topicIntakeSeen == nil {
p.topicIntakeSeen = make(map[string]struct{})
}
if _, ok := p.topicIntakeSeen[key]; ok {
p.topicIntakeMu.Unlock()
return true
}
p.topicIntakeSeen[key] = struct{}{}
p.topicIntakeMu.Unlock()
Comment thread core/streaming.go
Comment on lines 438 to 441
if finalText == sp.lastSentText && sp.lastSentViaUpdate && statusFooter == "" {
slog.Debug("stream preview finish: text unchanged and no footer, skipping",
"text_len", len(finalText))
slog.Info("stream preview: final message sent (no update needed)",
"message_id", sp.previewMsgID, "text_len", len(finalText))
return true
Comment thread core/streaming.go
Comment on lines 456 to 459
if err := sfu.UpdateMessageWithStatusFooter(sp.ctx, sp.previewMsgID, finalText, statusFooter); err == nil {
slog.Debug("stream preview finish: success via UpdateMessageWithStatusFooter")
slog.Info("stream preview: final message sent",
"message_id", sp.previewMsgID, "text_len", len(finalText))
return true
Comment thread core/streaming.go
Comment on lines +482 to 484
slog.Info("stream preview: final message sent",
"message_id", sp.previewMsgID, "text_len", len(finalText))
return true
Comment thread cmd/cc-connect/main.go
Comment on lines +206 to +208
// Returns the index of the subcommand, the subcommand string, and the
// full tail args starting from the subcommand (including any --config that
// preceded it, so subcommands can use it).
Comment thread core/statusboard.go
Comment on lines +190 to +203
if time.Since(sb.lastEditAt) < sb.minInterval {
sb.mu.Unlock()
return // coalesce bursts; the next transition after the window will catch up
}
text := sb.renderText()
if text == sb.lastText {
sb.mu.Unlock()
return
}
sb.lastText = text
sb.lastEditAt = time.Now()
messageID := sb.messageID
chatID := sb.chatID
sb.mu.Unlock()
@JayGarland

Copy link
Copy Markdown
Author

不应该提交到 upstream,已关闭

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.

4 participants