fix ci errors - #1486
Closed
JayGarland wants to merge 69 commits into
Closed
Conversation
JayGarland
commented
Jul 3, 2026
- 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
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
…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>
…ro, bypassing Chat.IsForum check
…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.
There was a problem hiding this comment.
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/statusAPI + 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 on lines
+15
to
+17
|
|
||
| tgbot "github.com/go-telegram/bot" | ||
| ) |
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 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 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 on lines
+482
to
484
| slog.Info("stream preview: final message sent", | ||
| "message_id", sp.previewMsgID, "text_len", len(finalText)) | ||
| return true |
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 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() |
Author
|
不应该提交到 upstream,已关闭 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.