Skip to content

L-0273: cherry-pick 8 upstream reliability fixes - #1500

Closed
JayGarland wants to merge 94 commits into
chenhg5:mainfrom
JayGarland:codex/L-0273-upstream-sync
Closed

L-0273: cherry-pick 8 upstream reliability fixes#1500
JayGarland wants to merge 94 commits into
chenhg5:mainfrom
JayGarland:codex/L-0273-upstream-sync

Conversation

@JayGarland

Copy link
Copy Markdown

Summary

  • Cherry-picks 7 of 8 upstream (chenhg5/cc-connect) reliability fixes requested in L-0273: race fix (Send vs cleanupInteractiveState), drainPendingMessages alignment, absolute file-ref paths, codex app-server write timeout, stream preview resume after permission prompt, hide agent footer lines, per-session idle timeout for live agents.
  • e739e2eb (throttle message recall fallback probes, fix(core): throttle message recall fallback probes #1321) was skipped: its content is already on main via b107669b, a prior sync of the same upstream PR — cherry-pick produced an empty diff and was skipped rather than committed as a no-op.
  • Two adaptation commits on top:
    • test: fix processInteractiveEvents call sites after cherry-pick — two new tests from the footer-hiding commit didn't pass this fork's dropReply bool parameter (added independently of upstream); go vet caught the arity mismatch.
    • fix: recognize POSIX-style absolute paths in AppendFileRefs on Windowsfilepath.IsAbs is OS-native, so the [Bug] 入站图片附件提示路径与实际落盘路径不一致,agent 永远读不到文件 #1459 cherry-pick's own new tests (TestAppendFileRefs_Absolutize*) failed on Windows because /tmp/...-style already-absolute paths got rewritten with a drive prefix instead of passed through. Fixed AppendFileRefs (and the test's own assertion) to also treat a leading / as absolute, matching the function's documented passthrough contract. This fork runs in production on Windows, so this is a real fix, not a workaround.
  • Idle timeout (760079bc) merged cleanly alongside the existing ping-only heartbeat mode — confirmed the two mechanisms are orthogonal (agentSessionIdleTimeoutNanos vs ExecuteHeartbeatPing), no field collision.

Conflict resolution notes

  • core/engine.go (a4b46593): real conflict — our fork's currentPromptLen/currentPromptPreview state fields vs upstream's as := state.agentSession race-fix capture landing at the same location twice (once via clean auto-merge, once via the conflict hunk). Resolved by keeping our fields and de-duplicating the capture to one copy (duplicate as := would have been a compile error).
  • core/engine.go (e739e2eb): trivial struct-field-ordering conflict; both sides' fields kept.
  • core/streaming_test.go (230ee3c6): both sides added a differently-named freeze/unfreeze regression test at the same insertion point; kept both (TestStreamPreview_FreezeAndRecreate + TestStreamPreview_UnfreezeResumesStreaming + 2 more upstream tests). All streaming tests pass.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • go test ./core/... ./daemon/... — 8 failures remain, all confirmed pre-existing on unmodified main (Windows/environment-specific: home-path shortening, PowerShell shell-output format, nexus skill-dir fixture, path-separator normalization, schtasks file-mode simulation) — none introduced by this sync
  • go test ./... full suite — all platform adapter packages pass

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 23 commits July 3, 2026 20:13
* fix: letter workspace pattern naming — L- + letter/ prefix fallback + prune compatibility

- resolveWorkspacePattern: task- → L- fallback when no letter ID found
- branchNameForWorkspace: task- → letter- fallback
- cmdPrune: accept both letter/ and task- branch prefixes
- All tests passing

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

* fix: align claudeContextWindow default (200K→666K) with global config

Per L-0211: default was 200K while fleet context_window=666000, causing
[ctx:~N%] to show disproportionately high compaction. Both fallback paths
(empty model string, non-[1m] model) now return 666_000 instead of 200_000.

Refs L-0211

* fix: repair context window event and letter prune

---------

Co-authored-by: architect-claude <architect-claude@resonova.local>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jie <pjie@presage.care>
* fix: both topic naming paths — handleGeneralTopicIntake (L-0202 Part A) + CreateTaskTopic (L-0202 Part B)

- handleGeneralTopicIntake (line 746): topic.Name instead of
  'letter-' + numeric thread ID — keeps 'letter-new' name
- CreateTaskTopic (line 836): topicTitle instead of
  'letter-' + numeric thread ID — matches L-0200 fix direction
- Both paths: no longer overwrite topic name with a numeric
  thread ID; L-0200 branch diff is now superseded

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

* fix: resolve compilation error and update Telegram platform test for topic naming

---------

Co-authored-by: architect-claude <architect-claude@resonova.local>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jie <pjie@presage.care>
Co-authored-by: Jie <pjie@presage.care>
Co-authored-by: Jie <pjie@presage.care>
…#7)

Adds core.ComposePersona/ResolvePersonaClass so every seat's system prompt
is prefixed with an archive-first preamble (write/read/secretary variant,
selected by workspace_pattern per L-0123's existing execution-seat split)
before its own persona content. Missing preamble files fall back to a
hardcoded one-line truth + WARN log rather than failing the spawn.

Wired into claudecode, copilot, and opencode session constructors via a new
CC_PERSONA_CLASS env var injected alongside CC_PERSONAS_DIR. Codex has no
native persona injection (L-0131), so its adapter instead syncs the same
preamble into a <!-- cc-managed:archive-first --> bounded block in AGENTS.md
on every StartSession spawn via the new core.SyncManagedBlock helper.

Co-authored-by: Jie <pjie@presage.care>
Co-authored-by: Jie <pjie@presage.care>
…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>
(cherry picked from commit a4b4659)
…g5#1436

Follow-up to chenhg5#1436. The third call site in drainPendingMessages was
modeled correctly in spirit (capture into local var before goroutine)
but missed two details that chenhg5#1436 applied to the first two sites:

1. The capture `as := state.agentSession` happened without holding
   state.mu, so the same race the PR set out to fix could still nil
   the field between the unlock above and the capture.
2. The Send goroutine did not have a defensive `if as == nil` check,
   unlike the other two sites; a nil capture would still panic when
   the goroutine ran.

Also folds the existing nil/Alive check into the post-capture path so
the gating uses the local copy (consistent with the new contract).

No behavior change for the happy path; in the racy path the goroutine
returns an error instead of dereferencing nil, which the existing
error handling already covers.

Verified locally:
- go vet ./... clean
- go build clean
- go test -race ./core -run TestCUJ_H2_TwoPlatformsConcurrentNoBleed -count=10 PASS
- go test ./core -run "Drain|Queue" PASS

(cherry picked from commit 5e2d501)
…Refs (chenhg5#1459) (chenhg5#1462)

When a user configured a relative work_dir (e.g. "~/project" or
".cc-connect"), SaveFilesToDisk joined relative paths into the
attachments directory and the resulting paths were passed verbatim
into the agent's prompt. The spawned agent process — typically run
from a different cwd by the platform adapter — could not resolve them
and silently dropped every attachment.

SaveFilesToDisk now calls filepath.Abs(workDir) up front and falls
back to the raw value on error, and AppendFileRefs defensively
absolutizes each entry. Both behaviors are covered by new tests for
relative, absolute, and empty workDir; the empty-workDir case falls
back to the process cwd so misconfigured deploys still get a
writable attachments directory.

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
(cherry picked from commit 7e1b53c)
…enhg5#1451)

* fix(streaming): resume stream preview after permission prompt

Add streamPreview.unfreeze() and call it from the
EventPermissionRequest handler after <-pending.Resolved, so subsequent
EventText in the same turn opens a new streaming card instead of being
buffered until EventResult.

* fix(test): drop unused nextSessionEventsHook type

(cherry picked from commit 230ee3c)
* FEAT: 添加会话空闲关闭live agent进程配置

* CHORE: 处理会话空闲关闭配置PR评审意见

* CHORE: 稳定会话空闲关闭测试

(cherry picked from commit 760079b)
filepath.IsAbs is OS-native only, so upstream's chenhg5#1459 cherry-pick
(7e1b53c) mis-absolutized already-absolute /tmp/... style paths on
Windows into drive-rooted paths. Treat a leading / as absolute on any
OS, matching the function's documented passthrough contract.
@JayGarland
JayGarland requested a review from chenhg5 as a code owner July 5, 2026 22:35
Copilot AI review requested due to automatic review settings July 5, 2026 22:35
@JayGarland

Copy link
Copy Markdown
Author

Closing — this was created against the wrong repo by mistake (should target our fork JayGarland/cc-connect, not upstream). Apologies for the noise.

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 syncs a set of upstream reliability fixes into cc-connect (engine/session safety, streaming behavior, attachment path correctness, heartbeat behavior), plus additional fork-specific adaptations to keep tests and Windows behavior correct.

Changes:

  • Improves core reliability around streaming previews, context indicators, queued message draining, heartbeat execution modes, and session hang detection.
  • Fixes attachment path correctness by ensuring file refs are absolute (including POSIX-style paths on Windows) and adds regression tests.
  • Adds/extends several supporting systems and tests (skill dir parsing, persona/preamble injection, workspace pattern helpers, relay/dispatch helpers, Telegram topic-intake tests, new CLI commands).

Reviewed changes

Copilot reviewed 75 out of 76 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/release_local/turn_contract/turn_contract_test.go Updates expected context-indicator percentage in release-local contract tests.
tests/release_local/config_matrix/config_matrix_test.go Adjusts for EffectiveDisplay signature change.
platform/telegram/telegram_test.go Expands Telegram platform tests (topic intake, thread routing, progress style).
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 helpers and letter/worktree routing.
core/streaming.go Adds preview interval override + unfreeze support; tweaks finish logging.
core/streaming_test.go Adds streaming preview regression tests around freeze/unfreeze behavior.
core/statusboard.go Introduces StatusBoard that posts/edits a pinned Telegram status message.
core/statusboard_test.go Adds unit tests for StatusBoard state transitions/render behavior.
core/skill_dirs.go Adds skill dir parsing/merge/normalization helpers.
core/skill_dirs_test.go Adds tests for skill dir parsing and Engine skill-dir application.
core/session.go Adds LastOutputAt + pending reaction storage/drain APIs; persists new fields.
core/progress_compact.go Adds single progress style normalization.
core/persona.go Adds persona-class resolution + persona composition + managed-block sync.
core/persona_test.go Adds tests for persona composition and managed block syncing.
core/multi_workspace_test.go Adds regression test for workspace agent env propagation.
core/message.go Absolutizes attachment paths and adds reaction/mention/drop-reply fields; adds Event context window field.
core/message_test.go Adds tests for SaveFilesToDisk/AppendFileRefs absolute-path behavior.
core/management.go Exposes heartbeat_type in management endpoints.
core/interfaces.go Adds optional interfaces (bot username, preview interval override, task topic creation, workspace config env provider).
core/i18n.go Adds i18n key for feature-start built-in command and reflows const block.
core/heartbeat.go Adds heartbeat types (ping vs agent) and busy-skip classification.
core/heartbeat_test.go Adds tests for ping heartbeats and busy-skip behavior.
core/feature_board.go Adds a JSON-backed feature board store and task/seat state structures.
core/dispatch_test.go Adds dispatch parsing/archive validation/topic-creation fallback tests.
core/context_inject.go Adds cross-seat context aggregation/formatting helpers and reaction formatting.
core/context_inject_test.go Adds tests for aggregated seat message filtering.
core/context_indicator_test.go Adds tests for configured context window usage in ctx indicator.
core/context_guard.go Adds local history compaction (“context guard”) and optional session rotation.
core/context_guard_test.go Adds tests for context guard compaction, token estimates, and rotation behavior.
core/cmdopts.go Adds env-slice→map conversion helper for workspace agent env propagation.
core/api.go Adds /project/status endpoint + shared project status classification + StatusBoard poller.
core/api_test.go Adds tests for /project/status classifications and stale-output hung detection.
config/config.go Adds new config fields (context_window, status_board, relay extensions, hide_agent_footer, workspace_pattern, idle timeout, project disabled flag).
config/config_test.go Adds tests for new config fields and updated EffectiveDisplay signature.
config.example.toml Documents new display and idle-timeout options.
cmd/cc-connect/worktree_test.go Adds tests for letter extraction and dispatch ledger loading helpers.
cmd/cc-connect/status.go Adds cc-connect status CLI command (daemon /project/status).
cmd/cc-connect/send.go Adds --chat-id/--thread-id/--platform options to derive session key.
cmd/cc-connect/send_test.go Adds tests for new send-arg parsing behavior.
cmd/cc-connect/relay.go Adds --config support to infer data-dir for relay send.
cmd/cc-connect/doctor_runas_test.go Gates run-as doctor tests off on Windows.
cmd/cc-connect/context_guard_config_test.go Adds tests for parsing context guard config.
CHANGELOG.md Documents newly added/changed behavior in Unreleased notes.
agent/reasonix/session_test.go Updates session constructor call sites and adds HTTP error-body coverage.
agent/reasonix/session_id_test.go Adds test ensuring empty session ID generates a usable ID.
agent/reasonix/reasonix.go Adds session env forwarding to reasonix serve session creation.
agent/opencode/session.go Adds per-session env forwarding and one-time identity/relay/persona injection.
agent/opencode/session_test.go Updates constructor signature usage.
agent/opencode/opencode.go Forwards session env to session constructor.
agent/copilot/session.go Adds per-session env forwarding + one-time identity/relay/persona injection; adds probe close timeout.
agent/copilot/session_test.go Adds test that persona injection is skipped when --agent is used.
agent/copilot/copilot.go Forwards session env to session constructor; propagates config env to workspace opts.
agent/copilot/copilot_test.go Extends workspace options test to validate env propagation.
agent/codex/codex.go Syncs archive-first preamble into AGENTS.md managed block for Codex.
agent/codex/appserver_session.go Adds write timeout handling and transport abort for app-server RPC.
agent/codex/appserver_session_test.go Adds regression test for blocked stdin write timing out.
agent/claudecode/session.go Injects persona/preamble into system prompt, throttles session_id events, adds context window to result events, adds debug occupancy monitor.
agent/claudecode/session_test.go Adds tests for persona + archive-first preamble injection.
.gitignore Ignores generated multi-workspace test fixtures.
.github/workflows/nexus-race.yml Adds race-detector workflow for this fork.

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

Comment thread core/statusboard.go
Comment on lines +13 to +17
"sync"
"time"

tgbot "github.com/go-telegram/bot"
)
Comment thread core/context_inject.go
Comment on lines +186 to +189
label := e.Project
if e.Role == "user" {
label = "Jay"
}
Comment on lines +50 to +52
if got := e.branchNameForWorkspace(want); got != "letter/L-2222" {
t.Fatalf("branchNameForWorkspace() = %q, want %q", got, "letter-2222")
}
Comment on lines +941 to +943
if len(stubBot.createTopicParams) != 1 || stubBot.createTopicParams[0].Name != "letter-new" {
t.Fatalf("CreateForumTopic params = %+v, want letter-L-1234", stubBot.createTopicParams)
}
Comment thread agent/copilot/session.go
Comment on lines 110 to +115
child.Env = env
for _, kv := range env {
if strings.HasPrefix(kv, "COPILOT_MODEL=") || strings.HasPrefix(kv, "COPILOT_PROVIDER_") {
slog.Warn("copilotSession: DEBUG child env carries COPILOT_* var", "kv", kv)
}
}
Comment thread core/message_test.go
Comment on lines +137 to +149
// Now also exercise a truly relative workDir.
gotRel := SaveFilesToDisk(filepath.Join("rel", "sub"), files)
if len(gotRel) != 1 {
t.Fatalf("SaveFilesToDisk(relative workDir) returned %d paths, want 1", len(gotRel))
}
if !filepath.IsAbs(gotRel[0]) {
t.Errorf("SaveFilesToDisk(relative workDir) returned non-absolute path %q — agent could not open it (issue #1459)", gotRel[0])
}
// The absolute path must resolve to a real file on disk.
if _, err := os.Stat(gotRel[0]); err != nil {
t.Errorf("SaveFilesToDisk returned path %q that does not exist: %v", gotRel[0], err)
}
}
Comment on lines +515 to +519
case <-ticker.C:
n := len(cs.events)
if n > 0 {
slog.Warn("claudeSession: DEBUG events channel occupancy", "len", n, "cap", cap(cs.events))
}
@JayGarland
JayGarland deleted the codex/L-0273-upstream-sync branch July 19, 2026 06:51
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.

8 participants