Registration now goes through the Agnet auth proxy (/api/heicode-auth/*)
instead of the local Manager API. Email verification is always required
(太极 mandates it). After successful registration, tokens are used
directly to establish the Manager session without a redundant login call.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When user clicks the X button, the app now hides to the system tray
instead of exiting. Users can restore the window by clicking the tray
icon or selecting "显示 Heicode" from the tray menu. To fully quit,
use "退出 Heicode" from the tray menu.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OAuth: safe type assertions for session state and affiliate code
(oauth.go, github.go, discord.go, linuxdo.go, oidc.go)
- Model: Scan() methods handle string values from DB drivers, not just []byte
(channel.go ChannelInfo, task.go Properties/TaskPrivateData)
- Model: safe type assertion in CleanupChannelPollingLocks sync.Map iteration
- Relay: safe type assertions in audio_handler, AWS InvokeModel,
ollama ConvertClaudeRequest, claude stop sequences, zhipu token cache
- Service: fix slice bounds panic in Gemini->OpenAI stop sequences conversion
- Service: safe type assertion in CleanupFileSources middleware
- Middleware: add missing c.Abort() in turnstile session save failure
- Middleware: safe type assertion in distributor channelId
- Middleware: safe int comparison in auth helper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace all encoding/json direct calls with common.Marshal/Unmarshal/DecodeJson per Rule 1
- Fix Dify nil pointer dereference on remote image upload (relay-dify.go)
- Fix Claude relay file content type detection for text/* and PDF (relay-claude.go)
- Fix unsafe type assertions in Claude relay and Vertex GetModelRegion
- Fix StreamScanner unconditionally resetting pre-existing StreamStatus
- Add inferMimeTypeFromFilename() for proper MIME type handling in DTO
- Fix Mac build script hardcoded DMG version (now reads from tauri.conf.json)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- relay/channel/aws/relay-aws.go: add bounds check before accessing
Content[0] — empty response array caused index-out-of-range panic
- dto/embedding.go: convert Seed, TopK, NumPredict, NumCtx from int
to *int so explicit zero values survive omitempty marshaling (Rule 6)
- dto/video.go: convert Fps, Seed, N from int to *int (Rule 6)
- dto/suno.go: convert ContinueAt from float64 to *float64 (Rule 6)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Client (cc-haha):
- server/api/sessions.ts: use path.basename() instead of split('/').pop()
for extracting project/repo names on Windows
- server/api/filesystem.ts: use os.tmpdir() and os.homedir() instead of
hardcoded '/tmp' and process.env.HOME which don't exist on Windows
- utils/plugins/pluginVersioning.ts: split on /[/\]/ for Windows paths
- utils/plugins/loadPluginCommands.ts: handle backslash separators in
plugin namespace construction
- cli/handlers/autoMode.ts: add optional chaining on response.content
to prevent crash when API returns null content
Manager (heicode):
- auth/api.ts: fix status always returning 1 regardless of active state
(was `? 1 : 1`, now `? 1 : 2`)
- users/api.ts, redemption-codes/api.ts, profile/api.ts: use
URLSearchParams for query string encoding to prevent breakage with
special characters in search keywords and email addresses
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- middleware/auth.go: session.Get() returns interface{} which can be nil;
use safe type assertions with ok checks to prevent panics on corrupted sessions
- controller/misc.go: replace json.NewDecoder with common.DecodeJson per project
convention, add error check before using decoded struct
- handle-server-error.ts: add optional chaining on error.response.data to prevent
crash when response body is undefined
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump version to 0.2.1 across tauri.conf.json, package.json,
Cargo.toml, updater manifest, and Mac fallback URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Windows, Tauri returns paths with backslash separators. Multiple
components used .split('/') to extract filenames/segments, which
returned the entire path as a single element on Windows. Changed all
instances to .split(/[/\]/) to handle both Unix and Windows paths.
Affected: ProjectContextChip, ToolCallBlock, PermissionDialog,
ToolCallGroup, FileSearchMenu, InlineImageGallery,
LocalSlashCommandPanel, ProjectFilter, StatusBar, DirectoryPicker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same bug as ChatInput — inserting filename at cursor without removing
the @filter trigger text, producing @foofile.ts instead of file.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The onSelect handler inserted the filename at cursor position without
removing the @filter trigger text, producing "@foofilename.ts" instead
of replacing the whole "@foo" with "filename.ts".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Guard empty thinking events from creating blank ThinkingBlock rows
- Skip empty assistant_text from history loading
- Hide ToolResultBlock when content is empty (non-error)
- Add component-level null returns as safety net
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix CJK font rendering: add PingFang SC, Microsoft YaHei, Noto Sans CJK SC
fallbacks to all CSS font stacks (headline, body, label, mono)
- Clear docs_link default (was pointing to upstream docs)
- Remove user-facing "NewAPI" text from en/zh i18n strings
- Bump desktop version to 0.2.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Manager web:
- Replace favicon.ico (both default & classic themes) with new H icon
- Compact heicode-logo.svg from 204KB base64 blob to 5KB
- Replace classic theme logo.png (was still old NewAPI icon)
Client (cc-haha):
- Fix isInBundledMode() to detect Bun-compiled sidecars that have no
explicit embeddedFiles — checks process.execPath basename instead
- Add well-known ripgrep install paths (/opt/homebrew/bin, /usr/local/bin,
~/.cargo/bin, etc.) as fallback when PATH is incomplete in Tauri sidecar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- assets/logo.tsx: drop the hand-drawn "H" path SVG and render
/logo.png (already swapped to the new gradient icon when we
refreshed branding). One source asset across desktop + favicon +
Manager web.
- desktop_download.go: Mac fallback URL points at HeiCode_0.1.9_
aarch64.dmg now (drag-to-Applications installer), not the
.app.tar.gz updater payload that confuses end users.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Balance pill was rendering nothing for admin/root accounts because
mcp-server's §4 returns HEICODE_USER_NOT_FOUND for users that never
came through from-agnet onboarding. Fall back to Heicode NewAPI's
own /api/user/self when that happens; reshape into the same envelope
so the UI is path-agnostic.
New balanceStore (zustand) — single polling loop, BalanceBar +
avatar ring share it. AppShell starts it once auth bootstraps.
BalanceBar now shows a "loading…" placeholder on first fetch so
the widget is visible from frame one.
SidebarUserCard avatar wears an SVG ring whose arc length tracks
remaining/(remaining+used) and color hits the same green→amber→red
thresholds as the bar.
Manager: /desktop-client drops the manifest-notes wall of text, the
old HEICODE_DESKTOP_FILE_* subtitle goes away, and a Mac fallback
entry is always spliced in when the live manifest is Win-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Today's 0.1.6/0.1.7/0.1.8 all built but never shipped a manifest
(some were superseded mid-iteration; 0.1.8 had Win binary on blob
but Mac wasn't ready). 0.1.9 ships the full stack as one release:
- BalanceBar under the composer now has a real progress bar (was
text only). Fill = remaining/(remaining+used); color shifts
green → amber → red below 30%/10%.
- (from 0.1.8) AskUserQuestion early-return moved below all hooks
so the render order is stable across input mutations.
- (from 0.1.8) chatStore content_delta throttle is now per-session
(Map<sessionId, {pending, timer}>); no more cross-tab text bleed.
- (from 0.1.8) endpoints array trimmed to blob-only — SWA URL gone
so a fallback failure no longer flashes a third-party domain.
- (from 0.1.7) new app icon — already on disk in icons/ + public/.
- (from 0.1.6) Manager desktop_download.go reads blob manifest so
the /desktop-client page tracks releases without env wrangling.
- Build pipeline: `tauri build --bundles nsis` is the release path
(skips MSI/WiX, ~2-3 min/build saved). sccache wired into
~/.cargo/config.toml; next build is the first with warm cache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P0: AskUserQuestion.tsx had `if (questions.length === 0) return null`
sitting between the `useState` calls and the `useMemo` calls below.
On any render where parseInput(input) flipped between empty and
non-empty (e.g. streaming permission_request input mutates) React
threw "Rendered more hooks than during the previous render". Moved
the early return after every hook call.
P1: chatStore.ts had `pendingDelta` + `flushTimer` at module scope,
shared across every active session. When two sessions streamed at
the same time (e.g. user has a team-member tab open alongside their
own), session B's content_delta would queue onto the same module
buffer as session A; whichever flush timer fired first emptied the
buffer into its own session. Result: text leaked between
conversations.
Replaced with a `Map<sessionId, { pending, timer }>` so each stream
owns its own throttle buffer. `consumePendingDelta(sessionId)` and
`dropBuffer(sessionId)` keep the API similar to before; nine
callsites updated.
Both issues caught by the bug-audit agent run after 0.1.7 build.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visual brand refresh: replaced the desktop client + Manager web logos
with the new glossy gradient H glyph.
Desktop (cc-haha):
- src-tauri/icons/*: regenerated via `tauri icon` from new-icon.png
(center-cropped 1024 square). Updates icon.ico/icns/png + 8 Windows
Square*Logo sizes + iOS + Android mipmaps.
- public/app-icon.png + app-icon.svg: replaced. SVG is now a wrapper
around the embedded PNG so existing <img src="…app-icon.svg"> refs
keep working without recoloring tooling.
- Version bumped to 0.1.7 across tauri.conf.json, package.json,
Cargo.toml.
Manager web (heicode/web/default):
- public/logo.png: 256x256 of the new glyph.
- public/favicon.ico: multi-size ico (16/32/48/64/128/256).
- public/heicode-logo.svg: same SVG-wraps-PNG trick as desktop.
Backend (heicode/controller/desktop_download.go):
- Already redeployed earlier today — the Manager web "Heicode 桌面客户端"
page now sources its download URLs from the Azure Blob updater manifest
instead of VM-local files. This is the fix for "still downloading
Heicode_0.1.0_windows_x64_msi.msi"; the page will reflect 0.1.7 the
moment the manifest publishes below.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cc-haha desktop (0.1.6):
- New sidecar route `/api/heicode-auth/balance` proxies mcp-server
§4 `/api/user/heicode/balance` using the active provider's stored
access token. Returns null silently on 401 / network failure so
the UI doesn't flash error strips.
- New BalanceBar component renders a compact pill right below the
ChatInput: `[wallet icon] $X.XX 剩余 · 已用 $Y.YY · N 次`. Polls
every 60s. Hidden when not logged in.
- Quota → USD display uses NewAPI convention (500_000 units = $1).
heicode Manager (Go controller):
- `GetDesktopDownloads` rewritten to pull from the same Azure Blob
updater manifest the in-app updater uses (`heicodeblob/.../
updater/latest.json`). 5-min in-process cache; stale-on-error
fallback. Stops the Manager web from showing stale
`Heicode_0.1.0_x64-setup.exe` after fresh releases.
- `DownloadDesktopFile` kept for back-compat — it now 302s to the
manifest's blob URL instead of streaming a VM-local file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per product-package design docs, IM integration (Lark / Feishu /
Telegram) is not a Heicode surface. Hide UI without ripping out
plumbing — keep AdapterSettings.tsx + adapterStore + types in the
source tree so re-enabling is a one-flag flip.
- Settings.tsx: drop the "IM 接入" tab button (`'adapters'` route
still resolves internally if someone forces it, but no nav).
- NewTaskModal.tsx: remove the IM notification channel pickers
(Feishu / Telegram checkboxes + "no channel configured" warning).
Existing task `notification` config is preserved on edit; it's
just no longer mutable from this UI.
- Unused imports (useEffect, useAdapterStore) cleaned to satisfy
TS strict mode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Sidebar search row gains a recency-filter chip (All / 1d / 3d /
7d). Active filter highlights brand color; selection persists in
localStorage (`cc-haha.sidebar.timeFilter`). Default `all` keeps
current behavior.
- SidebarUserCard becomes a button — clicking the avatar/name pill
opens https://code.xinghanlab.com/ in the system browser via
@tauri-apps/plugin-shell, with window.open fallback for dev builds.
Collapsed-sidebar avatar circle also clickable.
- i18n: `sidebar.timeFilter.title/all` + `sidebar.userCard.openProfile`
added in zh + en.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- updateStore: arm a 2-hour periodic checkForUpdates after the
startup pass so users who keep the client open for days still
see new releases without restarting. Idempotent, silent — only
the top-right popup surface fires.
- Sidebar: bucket session history into Past 24h / Past 3 days /
Past 7 days / All older (was Today/Yesterday/7d/30d/Older).
Rolling windows from `Date.now()` instead of calendar bounds.
- i18n: replace `today/yesterday/last7days/last30days` keys with
`within1day/within3days/within7days`; ScheduledTasksList and
ScheduledTasksEmpty migrated to the new keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Roll-up of tonight's desktop fixes shipped as 0.1.1:
- PermissionDialog: cap diff/command preview at max-h-360 so the
allow/deny buttons stay above the fold on huge writes (user
reported scrolling 900 lines to find the buttons).
- SessionTaskBar + cliTaskStore: hover row shows ✓ manual-close
button for tasks the agent forgot to mark completed.
- AskUserQuestion: honor schema's `multiSelect: true` — toggle
membership across options, switch round → square indicator,
show "可多选" hint, join answers with comma.
- HeicodeLoginPage: removed dead `'official'` filter that blocked
the typescript build (legacy provider id no longer in the union).
- Settings About: removed the third-party social-media block and
unused openUrl helper.
- i18n: replaced misleading "GitHub Releases" wording with
"Heicode 官方更新源" / "Heicode update source" — actual channel
is Azure Blob (msi/updater/latest.json) per tauri.conf.json.
- release-desktop.mjs: az invocations now run with shell:true so
the Windows `az.cmd` resolves; also uploads the manifest to
blob (primary endpoint) as the script finishes.
- tauri.conf.json + package.json + Cargo.toml: version bumped to
0.1.1.
- website/public/updater/latest.json: now reflects 0.1.1 + new
signed NSIS URL (mirrored to blob by the release script).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First populated updater manifest. Points at the signed NSIS bundle
hosted on Azure Blob (heicodeblob/msi). Smoke-verified: HTTP 200 on
both .exe and .exe.sig public URLs.
Notes: §4 wallet usage+logs live, StreamingIndicator now shows
active-tool description, updater channel end-to-end exercised.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- pubkey: rotated to the keypair stored at
C:\Users\陈晨\.heicode-updater\heicode_updater.key (private side
is the user's; only the pubkey ships in tauri.conf.json).
- scripts/release-desktop.mjs: one-shot release helper —
uploads the signed bundle artifacts to Azure Blob (account
heicodeblob, container msi, public-blob-read) and rewrites
website/public/updater/latest.json to point at the new URLs.
- UPDATER.md: rewritten with the concrete URLs, container, key
paths, and step-by-step commands. No more generic placeholders.
Azure Blob setup (done out-of-band, not in this commit):
- Storage account heicodeblob set allowBlobPublicAccess=true
- Container msi set to public-blob read
- Smoke-tested: https://heicodeblob.blob.core.windows.net/msi/<x>
returns 200 anonymously.
The actual Azure connection string + private-key path live in
scripts/.env.release, which is gitignored under .env.* and was
verified excluded before this commit.
NOTE: pubkey was rotated. Any MSI already in the wild signed by
the *previous* key cannot self-update to this signing chain —
those users need a fresh manual install. This is acceptable for
pre-GA where no public release exists yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tauri.conf.json: endpoints now points at the website-hosted
manifest (https://<azure-swa>/updater/latest.json) so the
already-built UpdateChecker actually has somewhere to check.
- website/public/updater/latest.json: placeholder manifest with
empty platforms map (clients will see "up to date" until a real
release ships).
- scripts/build-updater-manifest.mjs: helper that ingests signed
Tauri bundle artifacts and emits the manifest, so future releases
are one node command instead of hand-edited JSON.
- scripts/UPDATER.md: step-by-step for every release — what to set,
what to upload, where to commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the random spinner verb with a friendly description of the
tool currently running — "Reading foo.ts", "Editing bar.tsx",
"Running npm", "Searching <pattern>". Falls back to the verb / chatState
label when no tool is active.
Why: users were seeing "Percolating... 51s" with no idea whether the
agent was stuck or genuinely working. The chatStore already tracks
activeToolName + streamingToolInput, the indicator just wasn't using
either.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- task-card-view: refetchInterval now adapts to state — 3s for
running/awaiting_approval, stop on completed/failed, 15s otherwise.
Approval requests now surface within 3s instead of up to 15.
- docs: collect every Manager-side compat patch (deeplink mapping,
redact fallback, polling-vs-SSE) so mcp-server team can fold them
back into the contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add HeicodeUsageCard between balance and subscription plans:
- 14-day usage sparkline from /api/user/heicode/usage
- Last 6 requests from /api/user/heicode/logs
Both endpoints come from the product-package §4 contract (mcp-server),
so the figures match what the desktop sidecar sees.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Try getHeicodeBalance() (mcp-server /api/user/balance per product-package §4)
before falling back to local /api/user/self. Aligns Wallet page with the
Heicode product-package contract so balance numbers come from the same source
the desktop sidecar uses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/Heicode-对接进度与待办.md §7.5 point 7 named the Manager team as
responsible for wiring P1 resource UI to mcp-server (§2 ResourceBinding,
§3 ResourceGrant) — without this, the deeplink that the desktop client
puts on the task card (`/manager/resources?from=task`) ends in a 404.
Changes:
1. lib/heicode-mcp.ts: typed wrappers for the 9 P1 endpoints
- §2 ResourceBinding: list / get / create / update / revoke
- §3 ResourceGrant: list / get / create / revoke
- Field shape verified against live mcp-server with test account
55@55.com — 7 smoke cases pass including the 422 sensitive-keyword
enforcement and the §3 subset rule.
2. features/agnet-console/pages.tsx AgnetSKSourcesPage rewritten to
read /api/resources (filtered to status=active) instead of the
legacy Heicode-local git_sources controller:
- Card 1 代码 = resources filter type='git'
- Card 2 文档SK = resources filter type∈{sk,project_doc}
- Card 3 云账号 = resources filter type∈{cloud_account,
cloud_resource}, "auto-discovery coming soon"
hint shown when empty (current state)
- Card 4 推荐摘要 = unchanged
3. Advanced sheet form rewritten for mcp-server ResourceBinding shape:
{type, name, external_ref, metadata, permission_scope, constraints,
secret_ref, status}. Old (provider, repo_url, ref, paths, usage,
tenant_id) maps in:
name → name
repo_url → external_ref
provider → metadata.provider
ref → metadata.default_branch + constraints.ref
paths → constraints.allowed_paths (comma-joined)
usage → type ('git'/'sk'/'project_doc')
tenant_id → dropped (server uses auth.user_id)
— → permission_scope ['repo:read'] minimal default
— → secret_ref blank for now (server fills once
OpenBao Secret Broker lands per §2.1 TODO)
Form also surfaces the §2.1 422 RESOURCE_GRANT_SECRET_REJECTED
server-side error to the user.
4. Removed unused imports (GitSource{,Payload,Usage}, createGitSource,
deleteGitSource, listGitSources) — legacy git_sources controller is
still in the Go backend for now but the Manager no longer consumes it.
5. RecommendationSummaryDialog now takes ResourceBinding[] for project /
sk source counters instead of GitSource[].
Smoke verified end-to-end against live mcp-server:
list / create (incl. metadata+constraints+permission_scope) / get /
delete-binding all 200 with expected shapes; 422 secret rejection
fires on metadata.{name containing 'token'}; §3 subset rule on
allowed_actions outside binding.permission_scope returns
RESOURCE_GRANT_INVALID.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end smoke against mcp-server (test account 55@55.com) revealed
the actual task.card shape:
{ goal: string,
scope: string[],
generated_artifacts: string[],
manager_actions: Array<{label, deeplink}> }
My earlier TS types and TaskCardView assumed
{objective, first_version_scope, auto_generated, pending_context} —
keys that don't exist in the real response. Tasks created via the
intent flow would have rendered with empty bullet lists.
Changes:
- lib/heicode-mcp.ts: rewrite HeicodeTaskCard to the live shape, add
HeicodeManagerAction type
- features/tasks/task-card-view.tsx:
- read card.goal / card.scope / card.generated_artifacts
- new readManagerActions() helper renders mcp-server's
{label, deeplink} buttons in place of the hardcoded action row,
with normalizeDeeplink() mapping /manager/resources →
/sk-sources etc. to Manager-side routes
- dropped pending_context (no such field); follow-up "Pending
context" footnote is now a plain explanatory line per docs §10
Smoke verified end-to-end:
login (POST /api/auth/login) → 200 + JWT
intent (POST /api/user/tasks/intent) → 200 + configuring task
list (GET /api/user/tasks) → items shape matches type
detail (GET /api/user/tasks/{id}) → follow-ups parse correctly
answer × 2 (POST .../answer) → state machine flips to running,
card materialises with the 4
actual fields above
audit (GET /api/agnet/audit-logs) → {items, total, next_cursor}
agnet (GET /api/agnet/deployments) → {items, total}
balance (GET /api/user/heicode/balance) → HEICODE_USER_NOT_FOUND
(test account, expected; my code catches this and returns null)
UTF-8 body through the proxy works fine (earlier "parse body" error
was a Windows shell quoting issue, not a proxy bug).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After reading the Heicode-接口契约文档 v2.2 in
~/Desktop/taijigit/taiji-AI-PAD/Docs/ the right data sources are clear:
§1 /api/auth/* — already wired (features/auth/api.ts)
§4 /api/user/heicode/* — model balance + usage + logs
§5 /api/agnet/* — Agnet platform stub (deployments / audit / etc)
§6 /api/user/tasks/* — HeicodeTask intent → followups → card
The first wave UI work (commit 2df233b) used Heicode-local controllers
as the data source (AgnetDeployment.orchestration_plan as a stand-in
for the task object). That was wrong — the contract document is clear
that HeicodeTask (§6) is the canonical user-facing task object, and the
mcp-server stub at §5 is the canonical deployment source.
This commit redirects the data plumbing without touching the UI shells:
1. New lib/heicode-mcp.ts — typed client that calls mcp-server through
the existing same-origin /api/heicode-auth/* proxy. Implements the
subset of §4/§5/§6 the Manager UI needs:
createTaskFromIntent / listHeicodeTasks / getHeicodeTask / answer
getHeicodeBalance / getHeicodeModels / getHeicodeUsage / getHeicodeLogs
listMcpAgnetDeployments / listMcpAuditLogs
2. HomeHero (features/dashboard/components/home-hero.tsx):
- Idea input now POSTs /api/user/tasks/intent and routes the user
to /tasks/$id once the server returns the new task with its first
round of follow-ups. Previously it only stashed the idea in
localStorage which the docs §10 didn't actually require.
- ContinueTasks + TodayFocus now consume listHeicodeTasks output
(HeicodeTask.status / status_caption / updated_at:ms) instead of
AgnetDeployment shape.
3. TaskCardView (features/tasks/task-card-view.tsx):
- Reads getHeicodeTask(id) from mcp-server (refetch every 15s).
- When status=configuring renders the open follow-ups from the most
recent heicode thread entry as clickable option chips; clicking
POSTs answer to /api/user/tasks/$id/answer and the server-side
state machine advances. high-risk options get a red badge per §6.
- When status=running (followups answered, card materialised) the
four blocks docs §10 任务卡 mandates are rendered from task.card:
目标 / 第一版范围 / 自动生成 / 待确认上下文.
4. AgnetAuditPage (features/agnet-console/pages.tsx):
- Switched queryFn from local getAgnetAuditLogs to mcp-server
listMcpAuditLogs. The redacted-card renderer already accepts any
{resource_id, allowed_actions, constraints, secret_ref} shape so
no UI change needed; banner still announces no plaintext.
Notes:
- /wallet refactor to §4 deferred — it pulls multiple legacy series
from the local NewAPI controllers and the rewrite is a separate
pass. Manager users see local data for now; the call is identical
shape so swap is mechanical once we get there.
- Local TS check clean. Not deployed.
- Earlier 2df233b's UI structures (HomeHero shape, TaskCard layout,
recommendation dialog, audit redacted view) stay verbatim — only
the data fetching layer moved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five gaps closed against the updated product-package spec
(http://gitee.ath.cx:3000/xiaohei/heicode/src/branch/main/docs/product-package):
P1. /dashboard hero rewritten per §10 §"初始首页"
features/dashboard/components/home-hero.tsx replaces the technical
CockpitView with four blocks the spec mandates: 主输入 / 继续任务 /
今日焦点 / 辅助入口. Main input is "你想把什么想法变成可以上线的软件?".
Submit only stashes the idea to localStorage + toast — the actual task
conversation belongs in the desktop client per §13 §5.1.
P2. /tasks/$id TaskCard route per §10 §"任务卡" + §11 §3
features/tasks/task-card-view.tsx renders one AgnetDeployment as the
user-facing task object: 目标 / 第一版范围 / 自动生成 / 待确认上下文 +
Manager 辅助按钮. Linked from Home hero's 继续任务 list.
P3. /sk-sources 推荐摘要 dialog per §10 §"推荐确认卡"
features/agnet-console/pages.tsx RecommendationSummaryDialog. Five
blocks (本次会做 / 本次允许使用 / 本次不会做 / 高危规则 / 预计消耗) +
Launch Agnet button with "参数由 Heicode 自动生成" caption. No JSON
editor, no permission manifest — §10 高级展开禁令.
P4. /audit redacted card view per §10 §"任务用量与审计" + §6
features/agnet-console/pages.tsx AgnetAuditPage. Old裸 table replaced
with脱敏 cards exposing only the fields docs allows: resource_id /
resource_type / allowed_actions / constraints / secret_ref. Helper
function maskIfSecret() catches any stray plaintext credential the
backend might leak. Banner says explicitly "明文密钥从不展示".
P5. Login screen filters Claude Official provider per §8
cc-haha/desktop/src/components/login/HeicodeLoginPage.tsx hides the
legacy `official` preset so the login carries Heicode brand alone.
i18n fix (BIG):
i18next defaults to `defaultNS = 'translation'`. The earlier custom
keys had been written to the JSON root, NOT into translation, so
every t('Preparation checklist') was returning the English key as
fallback all along. Moved 67 orphan keys (zh+en, both files) into
the translation namespace where they're actually resolvable. Verified
by loading i18next + zh.json in bun and confirming all keys resolve
to the expected Chinese strings.
Cache-busting from earlier session (already deployed via SFTP, never
committed): index.html / constants.ts / footer.tsx now hold the
?v=h-glass-2 suffixed asset URLs in git, so future docker rebuilds
preserve them.
Per user directive: tested locally only (TS check clean, i18next
resolves correctly). NOT deploying to the VM in this commit — user
asked to keep production untouched until they verify the changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier UserPill landed in the TitleBar tab strip, but the main
desktop layout (HeicodeTasksHome shell) renders its own chrome without
that strip — so the user info ended up invisible despite mcpAuth
having the right data after a2deeb6. Pull the same identity (avatar
gradient + name + email + role badge) straight into the bottom of
Sidebar.tsx, right above the logout/settings rows, so it's visible
on every screen regardless of which top bar is mounted.
Two layouts:
- expanded: 36px gradient avatar + name + email + admin/root badge
- collapsed: 36px avatar circle only, title tooltip carries the email
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
resolveBundledCliPathFromExecPath / resolveClaudeCliLauncher hardcoded
startsWith('claude-sidecar') / startsWith('claude-server') checks. After
the 3a358ba rebrand the bundled binary is heicode-sidecar-*.exe — the
match fails, the function returns null, and resolveCliArgs falls back
to the Windows --preload script branch which spawns the bun-compiled
sidecar with arguments it doesn't understand. The process exits 2
silently (empty stderr), surfacing in the UI as:
Error: CLI 进程启动失败。
CLI exited during startup with code 2.
Symptom user report: pilac69779@codoteam.com freshly registered, OAuth
login succeeded but every chat attempt failed at sidecar startup.
Fix: accept both `heicode-` and legacy `claude-` prefixes for sidecar /
server / cli binaries so MSI #15+ launchers work AND
CLAUDE_CLI_PATH=/path/to/legacy/claude-sidecar still works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of the recurring "I don't see the logged-in user info in the
desktop client" complaint: the credentials-login path builds an mcpAuth
record with email / displayName / role from the mcp-server /api/auth/login
response and the TitleBar UserPill renders fine. But the OAuth-login path
(the typical browser-redirect flow) only received an sk- API key in the
callback query string — no user fields. So:
status.user == null
UserPill: if (!user) return null
→ blank space where the user pill should be.
Fix on backend (heicode_oauth.go HeicodeOAuthAuthorize):
- After issuing the sk- token, load the authenticated user from the
session and embed email / name / role (root|admin|user) / channel_id /
user_id as query params on the redirect URI.
Fix on client (cc-haha/src/server/api/heicode-auth.ts handleOAuthCallback):
- Read those query params (pickUserFromQuery), build an mcpAuth record
(buildMcpAuthFromOAuthQuery), and pass it through loginAndActivate the
same way the credentials path does. The accessToken slot holds the sk-
key as a placeholder — OAuth flow doesn't deliver a refreshable JWT
pair, and this mcpAuth exists purely to surface identity on the
TitleBar.
After this, OAuth-route users see the same gradient-avatar pill with
their email / name / role badge that credentials-route users already see.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bin/azure_vm_deploy.sh pulled from `origin` (xiaohei/heicode.git) when
GIT_REF was set. Production VM now tracks heicode-win/main and that's
where the rebrand + bug fixes ship — pulling from origin would silently
revert all of it on the next deploy. New GIT_REMOTE env var (defaults
to heicode-win) lets operators override for one-off cherry-pick deploys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. /api/heicode-auth/* proxy: CriticalRateLimit (20/20min) → GlobalAPIRateLimit
(180/180s). The Heicode external-identity proxy is hit on every page
render for /me + /refresh plus the login burst — CriticalRateLimit is
sized for sensitive ops (password reset, 2FA) and trips at ~5 quick
page loads, returning 429 to a normal user. APIM upstream rate-limits
itself, so a second tight layer here adds no security and just
manufactures 429s.
2. JIT-create user group: seed "default" instead of me.Data.ChannelID.
Companion to 578a68f which only patched the every-login overwrite
path. New users (yj2824269760@gmail.com et al, JIT-created after
578a68f) still landed in a UUID group → empty /v1/models response →
desktop client showed the static 3-Claude fallback list.
Both fixes were applied on the production VM directly today (sed +
python patch) — committing them so the next docker rebuild on VM keeps
them instead of reverting to the buggy file via git checkout.
DB hot-fix already applied: 6 affected users moved to group=default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Beyond the i18n strings rebrand (b55d103) this pass cleans up everything
that still surfaced "Claude" in the built artifact:
Theme tokens (cc-haha/desktop/src/theme/globals.css)
- light-mode text-selection-bg rgba(197,165,114,*) → rgba(123,107,227,*)
(selecting ANY text in light mode used to paint a gold highlight)
- dark + light diff-highlight-bg / -gutter switched from gold to brand
violet rgba(123,107,227,*) — keeps semantic "highlight" without leaving
the brand
Sidecar binary rename: claude-sidecar → heicode-sidecar
- desktop/sidecars/claude-sidecar.ts renamed (git mv)
- internal log prefix strings (12 occurrences) updated
- desktop/scripts/build-sidecars.ts: entrypoint / outfileBase / productName
('Heicode Sidecar') / publisher ('Heicode') — these last two are embedded
in the .exe metadata you see in File Properties
- src-tauri/capabilities/default.json: 9 binaries/claude-sidecar references
rewritten + description "Default capabilities for Heicode Desktop"
- src-tauri/src/lib.rs: 2 .sidecar("claude-sidecar") + 3 packaged sidecar
exe filename strings + 1 doc comment
- desktop/sidecars/launcherRouting{,.test}.ts
After this rebuild, Task Manager shows heicode-sidecar-*.exe child
processes (was claude-sidecar-*.exe) and right-click → Properties on
that binary shows "Heicode Sidecar" / "Heicode" instead of "Claude Code".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[data-theme=light] block in globals.css still defined --color-primary as
#8E7547 with gold gradient buttons and gold focus rings. Dark mode was
correctly on the brand violet #7B6BE3 since the Iris overhaul, but the
moment a user switched to (or was OS-defaulted to) light theme they got
the legacy gold mark on login button, focus ring, sidebar active state,
shadow-button-primary, etc.
Rewrote the light override to mirror the dark brand palette:
- --color-primary #7B6BE3, primary-container #5B4FB8, fixed #9A8DEC
- --color-secondary #6B7CE0 (was slate #5A6B82)
- --gradient-btn-primary 3-stop violet→blue (matches dark mode + Manager)
- --gradient-brand-wordmark also added in light scope (was inheriting
but explicit avoids cascade surprises)
- All rgba(142,117,71,*) shadows / borders rewritten to rgba(123,107,227,*)
Code-syntax / diff highlight golds left alone (those tint code text, not
brand UI).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Theme tokens (theme.css)
- --primary retuned to the brand violet oklch(0.58 0.17 278) ≈ #7B6BE3
(was a saturated pink oklch ... 286 that read off-brand against the H
glass logo)
- New tokens: --brand-from #B888E5, --brand #7B6BE3, --brand-to #6B7CE0,
--gradient-brand (135deg three-stop), --gradient-brand-btn for primary
CTAs. Both light and dark modes hold the same brand identity.
Login page (auth-layout.tsx + sign-in/index.tsx + user-auth-form.tsx)
- Inlined H glass mark SVG replaces the abstract ShieldCheck pictogram
- Brand wordmark uses gradient text-fill so the word "Heicode" reads as
the same gradient as the logo
- "Tenant access" pill and h2 heading both pick up brand violet via
border / bg / gradient text
- Sign-in button switches from solid var(--primary) to the three-stop
--gradient-brand-btn with violet drop shadow + lift-on-hover
Header user info (profile-dropdown.tsx)
- The right-side trigger used to be a bare 36px avatar — invisible user
identity unless you click. Now it is a pill: gradient-filled avatar
initials + display name + email + role badge, always visible on >=sm
- Avatar fallback fills with --gradient-brand so even pre-image, the
user pill carries Heicode color identity
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per docs §10 "禁止普通用户体验出现:模型供应商配置 / 模型提供方
选择" — the dashboard landing hero showed "OpenAI / Claude /
Gemini / DeepSeek / Qwen / Llama" as a marketing matrix, which
leaks upstream provider branding into the user-facing surface.
Replaced with Heicode capability tags (通用 / 长上下文 / 推理 /
代码 / 多模态 / 高性价比). This is the landing the user lands on
after login; admins still see real upstream provider names in
the system-settings → models tabs (those are technically the
channel protocol names and removing them would mislead admins).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CORS unblock — add /api/heicode-auth/*proxyPath backend proxy to
HEICODE_AUTH_BASE_URL. Frontend defaults to same-origin path so
the browser never hits APIM directly.
Sidebar — replace backend jargon (Git sources / Deployments /
Events / Wallet / Available models / Profile) with the user-facing
labels docs §10 mandates: 总览 / 准备清单 / 任务总览 / 审计 /
模型与余额 / 客户端 / 账号安全.
/sk-sources rewritten as 4-card preparation wizard with progress
meter; full Git form moves into a 高级补充 sheet. Drops JSON
editor, permission manifest, snapshots and resource-grant pills.
/deployments simplified to 任务总览: objective + status + last
update. Drops risk / budget / scope / secret_ref pills and the
RunDetailPanel; manifest details only in audit/advanced views.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>