Files

93 KiB
Raw Permalink Blame History

Changelog

All notable changes to tmeet will be documented in this file, following the Keep a Changelog convention.

[v1.0.18] - 2026-09-11

Added

  • New event subcommand group — real-time event subscription via a per-host bus daemon (cmd/event/, cmd/root.go, internal/event/**): Introduces a full "per-host bus daemon + shared WSS long connection + multi-consumer fan-out" framework so agents can subscribe to Tencent Meeting real-time events (e.g. meeting.started, meeting.end) and consume them as NDJSON. All event consume consumers share a single WSS connection managed by the bus daemon, which centralises handshake / heartbeat / auto-reconnect.
    • event list (cmd/event/list.go) — Lists subscribable EventKeys from the local built-in registry; does not require login and issues no remote calls. Optional --domain filters by domain (e.g. meeting); unknown domain exits with code 1 and prints the set of known domains.
    • event schema <EventKey> (cmd/event/schema.go) — Prints params_schema / resolved_output_schema / jq_root_path for the given EventKey; does not require login. Unknown key exits with code 1 pointing at event list. This is a mandatory pre-step before writing --jq / --param: a wrong jq_root_path does not error, it silently drops events (e.g. meeting.started / meeting.end have jq_root_path=.payload and payload itself is an array — jq must first do .[0] before descending into fields).
    • event consume --event-id <EventKey> (cmd/event/consume.go, cmd/event/consume_runner.go) — The actual subscription entry point; requires login (owner_hash is bound to the bus). Supports both batch mode (--max-events / --timeout) and long-running mode (neither flag, exit via Ctrl-C or event stop). Ships with --jq client-side projection, --param key=value for subscription parameters, --output-dir for NDJSON tee-to-disk (relative paths only, .. segments rejected — hard-validated), and --quiet to suppress non-fatal stderr. Strict stdout / stderr separation: business events go only to stdout NDJSON; ready markers / exit summaries / diagnostics go only to stderr. Once ready, stderr emits [event] ready event_key=<key> (not muted by --quiet); on exit, stderr emits [event] exited — received <N> event(s) in <duration> (reason: limit|timeout|signal|shutdown). Does not read stdin at all: closing stdin / < /dev/null / nohup / setsid will not trigger exit in either bounded or unbounded mode, making the command naturally compatible with agent / background scenarios. The stream does not replay historical events — only events newly produced after subscription are delivered.
    • event status (cmd/event/status.go, cmd/event/status_stop_test.go) — Inspects the local bus daemon state; does not require login and reads only the local bus directory. Lists buses[] with state: running / stale_owner (credential OpenId disagrees with bus owner) / orphan (pid dead but artefacts left behind) / refused / errored. Optional --fail-on-orphan exits with code 2 when an orphan is detected, useful for CI / self-healing scripts.
    • event stop (cmd/event/stop.go) — Stops the local bus daemon; does not require login. --force is treated as a write operation (enters the dangerous-operation confirmation table) and is used to reap orphan / stale_owner remnants; refused / errored states return exit code 2.
    • event _bus (cmd/event/bus.go) — Hidden subcommand forked automatically by event consume; agents must never invoke it directly. Owns the single WSS connection to the server, multiplexes subscribers, and fans events out to consumers over IPC (Unix socket / Windows named pipe).
    • Event framework internals (internal/event/bus, internal/event/source, internal/event/protocol, internal/event/protocol/wsspb, internal/event/busdiscover, internal/event/spawner, internal/event/transport, internal/event/cleanup, internal/event/jqfilter): Hub / subscription registry / connection management / WSS + protobuf codec (tmeet_wss.proto) / bus discovery + PID file / cross-platform detach fork (unix / windows) / cross-platform IPC transport / session keep-alive with token refresh / event de-duplication / jq projection engine with unit tests.
    • New exit-code contract: 0 (normal) / 1 (fatal: Hello rejected, unknown EventKey, IO error, subscription failure, ...) / 2 (only for event status --fail-on-orphan and event stop on refused / errored).
    • New SKILL section and reference doc (skills/tmeet-skill/references/tmeet-event.md, 483 lines): full coverage of use-case routing, mandatory call sequence, subprocess contract (ready / exited markers, exit codes, stdin EOF behaviour, stderr diagnostics), red lines (schema-before-jq, no kill -9, multi-account switching, path validation, privacy in output, _bus never called directly), bus-remnant self-heal flow, EventKey field reference, and a common-failure lookup table.
  • Event-related client error codes (internal/exception/client_code.go, internal/exception/errors.go): ClientCodeEventInternal = 4000 / ClientCodeEventBus = 4001 / ClientCodeEventBusNotRunning = 4002, along with matching EventInternalError / EventBusError / EventBusNotRunningError constants.
  • WSS token-expiry server codes (internal/exception/server_code.go): ServerCodeWssTokenExpired = 200010203 / ServerCodeWssHeadTokenExpired = 10006, consumed by the WSS long-connection side to detect expiry and trigger token refresh.
  • WSS endpoint constant (internal/core/endpoints.go): Endpoints.WSS and GetWSSEndpoint(), pointing to meeting.tencent.com, used by the event framework to open the WSS long connection.
  • config.ResourceReleaseHook teardown-hook mechanism (internal/config/user.go, internal/config/resource_hooks_test.go): New RegisterResourceReleaseHook / ResetResourceReleaseHooksForTest and a ClearUserConfigFunc type. Credential teardown (ClearUserConfig) now runs every registered hook first in registration order (fail-fast: any hook error or panic aborts teardown and leaves keychain + active_open_id intact so the user can retry), and only proceeds to keychain / meta cleanup if all hooks succeeded. cmd/root.go registers an event-bus hook via registerResourceReleaseHook() (dispatching to internal/event/cleanup.OnUserCleared) so auth logout and the refresh-token failure fallback gracefully stop the outgoing account's bus, preventing residue from leaking into the next login session. Deliberately placed in internal/config (the lowest package in the dependency graph) to avoid an internal/event → internal/config cycle.
  • Process-lifetime exclusive lock filelock.ProcessLock (internal/core/filelock/processlock.go, internal/core/filelock/filelock.go, internal/core/filelock/filelock_unix.go, internal/core/filelock/filelock_windows.go): Distinct from the existing short-lived WithLock (poll + defer auto-release), ProcessLock acquires non-blockingly and holds for the rest of the process; the OS releases it at process exit. Foundation for the bus daemon "am I alive?" probe (the bus takes a ProcessLock on bus.alive.lock at startup and never releases it; any subsequent TryLock succeeding means the previous holder died). The cross-platform lock primitives lockFDNonBlocking / unlockFD are extracted so both WithLock and ProcessLock share a single well-tested implementation. Exports ErrHeld for errors.Is to distinguish contention from real syscall failures.
  • log.InitNamed and instance-level Logger helpers (internal/log/logging.go): New InitNamed(logDir, subDir, prefix, level) lets long-running auxiliary processes (notably the event bus daemon) own their own log namespace while still benefiting from rotation / retention / async writes. The returned *Logger is not registered as the global defaultLogger; callers own it and must call Close(). Added instance-level (*Logger).Debugf/Infof/Warnf/Errorf and a (*Logger).Close() that is idempotent (double-close does not panic).
  • thttp.DefaultNoProxyHttpClient direct-connection fallback client and per-request WithRequestClient option (internal/core/thttp/client.go, internal/core/thttp/request.go): New *http.Client with Proxy: nil (all other transport tunables match DefaultHttpClient) for use as a retry path when a CGI request fails at the network layer; WithRequestClient(clt) is a new per-request option that overrides the client-level *http.Client for a single request without mutating the shared client.
  • common.BuildUniqueID(openID, machineID) unifies Tmeet-Unique-ID / cli_uniq_id formatting (internal/common/system.go): The "<openId>*<machineId>" concatenation is centralised in one helper. The REST proxy's Tmeet-Unique-ID request header and the WSS AuthBindReq.cli_uniq_id field now share the same implementation, ruling out format drift between the two paths.
  • build.sh now runs unit tests before compilation (build.sh): Runs go test -count=1 ./... up front and aborts on failure (via the script's set -e), baking "test before build" into the release flow.

Changed

  • ClearUserConfig is now a two-phase "release resources, then clear credentials" flow (internal/config/user.go, internal/auth/auth.go, internal/proxy/rest-proxy/proxy.go): The new ClearUserConfig runs (1) read ActiveOpenId; (2) dispatch every registered ResourceReleaseHook fail-fast — any failure or panic leaves keychain + active_open_id intact so the user can retry; (3) remove the keychain entry; (4) clear active_open_id from meta. The former implementation is preserved as ClearUserConfigUnResource. auth.RefreshToken now takes a ClearUserConfigFunc argument (wired to config.ClearUserConfig at the call site) so it no longer hard-depends on a specific teardown function, and so event / test paths can inject different teardown strategies.
  • Record-state enum descriptions refined (internal/utils/enumerate/record_state.go, internal/utils/enumerate/record_state_test.go, internal/utils/converter_test.go): 录制中 → 录制中,不可查看或申请, 转码中 → 转码中,不可查看或申请, 转码完成 → 转码完成,可根据录制文件权限进行下一步; unit tests updated in lock-step.
  • log.Init / log.Close internals refactored around newLogger and instance-level Close (internal/log/logging.go): Package-level Init now delegates to the shared newLogger constructor, and package-level Close delegates to defaultLogger.Close(). Logger gains subDir / prefix fields with filePrefix() / fileSubDir() fallbacks so path composition, rotation and cleanup all route through instance methods instead of the hard-coded package-level logPrefix / logSubDir, unlocking the standalone log namespace used by the event bus daemon.
  • minute package and CLI command renamed to minutes (cmd/minute/ → cmd/minutes/, cmd/root.go): CLI entry point changes from tmeet minute search|get to tmeet minutes search|get; skills/tmeet-skill/references/tmeet-minute.md renamed to tmeet-minutes.md; every minute reference in SKILL and README (command tree, routing decision tables, error hints) rewritten to minutes.
  • cmd/root.go wires the event subcommand group and enables the resource-release hook (cmd/root.go): Adds rootCmd.AddCommand(event.NewBaseCmd(tmeet)); calls registerResourceReleaseHook() at the top of Execute() (deliberately not in init() — registering in init() would activate the hook whenever the cmd/root package is imported, e.g. by tests, and risk accidentally tearing down the user's bus); also drops a redundant fmt.Sprintf wrapper around output.PrintErrorf in favour of the native printf-style signature.
  • README trimmed of the long command-reference section (README.md, README_EN.md, docs/command.md, docs/command_en.md): The ~1000-line "Command reference" section is moved out of README into the new docs/command.md / docs/command_en.md. README keeps only the command tree plus the overview of pagination / --compact / --format. The command tree gains event list|schema|consume|status|stop, and the pagination table renames minute search|get to minutes search|get.
  • SKILL bumped to 1.0.18, fully embracing the event group and the minutes rename (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-event.md, skills/tmeet-skill/references/tmeet-minutes.md):
    • Top-level description extended: adds "实时事件订阅(会议/录制/纪要等事件流)" to the capability description.
    • Login-prerequisite exceptions extended: on top of auth login / auth status, event list / event schema / event status / event stop are also login-free so users can inspect bus remnants from an unauthenticated state.
    • Command tree gains an event branch, linked to the new references/tmeet-event.md.
    • --format section gains an exception clause: the event subcommand family emits bare JSON — no {trace_id, message, data} envelope (e.g. event consume emits NDJSON lines shaped {event, trace_id, payload}; event list emits [{...}, ...] directly) — and does not go through the compact middleware, so --compact has no effect on event output. Use event consume --jq for projection instead.
    • Yuanbao-minutes routing and the "minutes vs recording" decision table: every minute get|search reference rewritten to minutes get|search.
    • Common-errors table gains an event consume failure entry: missing ready marker / exit code 1 / event stop returning refused, etc., all delegate to tmeet-event.md's common-failure lookup table.

Fixed

  • Base64DecodeConverter no longer leaves RawStdEncoding / URLEncoding fields as raw ciphertext (internal/utils/converter.go): Previously the converter only fell back to RawURLEncoding once when StdEncoding decoding failed — coverage was incomplete: if the server returned RawStdEncoding (standard encoding without padding) or URLEncoding (URL-safe encoding with padding), decoding failed and the raw Base64 ciphertext was returned to the caller, surfacing as "an unreadable blob" in the model / user output. The converter now walks StdEncoding → RawStdEncoding → URLEncoding → RawURLEncoding in order, validating utf8.Valid on each success, and returns the decoded plaintext at the first hit; only if all four encodings fail does the original value pass through untouched.
  • CGI requests would fail wholesale when a stale proxy env var was inherited (internal/proxy/cgi-proxy/proxy.go, internal/core/thttp/client.go, internal/core/thttp/request.go): Agent-sandbox scenarios often set HTTP_PROXY / HTTPS_PROXY / ALL_PROXY pointing at a sidecar proxy, and a long-lived (or sandbox-spawned) tmeet process inherits those env vars. If the sidecar is torn down but the env vars remain, DefaultHttpClient dials a dead proxy on every request and fails at the network layer — the user-visible symptom is "every CGI-backed command times out / connection refused". Fix: introduce DefaultNoProxyHttpClient (Proxy: nil direct-connection client) plus a per-request WithRequestClient option. When RequestProxy detects a network-layer failure (not a non-200 HTTP response — an actual c.clt.Do error, meaning the request never got a response), it retries once over the direct-connection client using a brand-new Request struct so the retry does not inherit the authenticators / headers already appended by the first attempt; non-200 statuses are not retried (the server did respond, so retransmission would not help). The whole path also gains structured log.Errorf / log.Warnf diagnostics, with sensitive headers (Set-Cookie / Authorization / Cookie) redacted via redactHeader before being logged.

[v1.0.17] - 2026-09-09

Added

  • New app subcommand group (cmd/app/, cmd/root.go): Manages the current logged-in user's own CLI app (cli-app) presentation in Tencent Meeting — app display name, homepage URL, and in-meeting open layout. Scope is strictly limited to the current user; it does not affect other participants or the meeting itself. Wired through WithApiCmd(ApiCmdAppInfoGet / ApiCmdAppInfoSet) and the middleware pipeline.
    • app get (cmd/app/get.go) — Retrieves the current CLI app configuration. Takes no parameters; backed by GET /v1/cli/app-detail.
    • app set (cmd/app/set.go) — Updates the CLI app configuration via PUT /v1/cli/app-info. At least one of the three flags is required (MarkFlagsOneRequired); fields not provided are left untouched on the server side.
      • --homepage — App homepage URL. Non-empty: the CLI app is pushed to the meeting client and displayed in-meeting; empty string: the CLI app is no longer pushed in-meeting (this is effectively the "show in meeting" master switch). When non-empty, the URL is strictly validated by the new utils.ValidateURL (must start with http:// or https://, host non-empty) and length-capped to ≤ 200 characters via utils.CharacterLimit.
      • --layout-style — In-meeting open layout, validated client-side via the cmdutil.EnumValue type introduced in v1.0.15. Accepts only sidebar (narrow sidebar, 375px wide) / wide_sidebar (wide sidebar, 735px wide) / popout (standalone popout, 960×540px). When omitted, the CLI does not send the field: server default becomes sidebar if unset, existing value is preserved otherwise.
      • --sdk-name — In-meeting display name for the app. Length-capped at 20 in display width via the new utils.CharacterWidthLimit (ASCII counts as 1, non-ASCII such as Chinese counts as 2); also rejects pure-whitespace and empty strings.
  • New utils.ValidateURL URL validation helper (internal/utils/validate.go, internal/utils/validate_test.go): Built on net/url.Parse. Requires scheme to be http or https (case-insensitive), host to be non-empty, and rejects the empty string outright. Unit tests cover http/https, missing scheme, ftp://, empty host, and empty input. Currently consumed by app set's --homepage.
  • New utils.CharacterWidthLimit display-width limiter (internal/utils/string.go): Complements the v1.0.6 CharacterLimit (which counts UTF-8 runes) by counting display width — ASCII as 1, non-ASCII as 2. On overflow it returns an InvalidArgsError carrying the flag name, the configured limit, and the actual width. Currently consumed by app set's --sdk-name.
  • New ApiCmd constants (internal/cmdutil/api_schema.go): ApiCmdAppInfoSet / ApiCmdAppInfoGet — so the new app command group plugs into --compact and the middleware pipeline.

Changed

  • record list response now carries an extra state_int field (cmd/record/list.go): Before the raw state is transformed into a human-readable Chinese description by RecordStateConverter, the raw integer state code is recursively copied into a sibling state_int field via the new copyStateRaw / copyStateIntRecursive helpers. state_int is automatically appended to the --compact whitelist so it is preserved even in compact mode, letting callers programmatically branch on record state while keeping the human-readable description intact.
  • meeting get record enrichment now scopes recording queries by the meeting's time window (cmd/meeting/record_enrich.go): enrichMeetingWithFullRecords now reads each meeting object's start_time / end_time, parses them via parseUnixSeconds, and forwards them as start_time / end_time query params to the paginated recording-info API. This explicitly constrains the returned records to that meeting's time window and prevents cross-meeting historical records from bleeding into the enrichment.
  • SKILL bumped to 1.0.17 with the new app command group and related rules (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-app.md, skills/tmeet-skill/references/tmeet-meeting.md):
    • Top-level description extended: Adds "CLI 应用信息管理(设置/查询自己的 CLI 应用在会中的展示配置)" to the capability description.
    • Command tree extended with app group, linked to the new references/tmeet-app.md.
    • New references/tmeet-app.md reference doc: Fully covers the use-case table, get / set parameter tables, invocation rules (--homepage on/off semantics, URL legality, the http:// vs https:// protocol-selection decision table, https:// certificate-trust requirement, "--layout-style values must never be inferred"), and the post-success announcement rules (with heavy emphasis on the fact that set changes do not take effect in the currently-joined meeting — the user must rejoin to see the change, preventing agents from retrying set in a loop inside the same meeting).
    • http:// vs https:// client-compatibility decision table: Tencent Meeting client versions prior to 3.45.10 do not support opening http:// pages. For any http:// URL provided by the user, the agent must first confirm the client version before invoking app set, and must not skip the confirmation just because the user verbally claims to be on a "new version".
    • New error code 500294 handling rule (SKILL.md common-errors table + tmeet-app.md): When the current account's meeting plan does not entitle this feature, the backend returns an error description containing an upgrade link. The agent must return the URL verbatim and render it as a clickable Markdown link [升级会议套餐](URL) — no rewriting, no parameter dropping, no wrapping in backticks or code blocks, and no self-invented plan-type reasoning.
    • New error code 500284 handling rule (SKILL.md common-errors table): "该功能暂不可使用" — the current account has no entitlement (possibly ungrayed feature, insufficient commercial rights, etc.). Retries are strictly forbidden — the agent must inform the user truthfully that the feature is unavailable on the current account and that interpretation authority belongs to Tencent Meeting; it must not speculate on the cause or promise activation paths.
    • meeting update --meeting-type 1 usage clarified (references/tmeet-meeting.md): Corrects the over-strict wording from v1.0.16 ("modifying a recurring meeting requires --meeting-type 1"). The refined rule: --meeting-type 1 is required only when modifying recurrence rules (--recurring-type / --until-type / --until-count / --until-date) or modifying a specific sub-meeting (--sub-meeting-id). When only editing subject, time, password, join restrictions, watermark, recording, or invitees — i.e. non-recurrence fields — --meeting-type is not required; the server will not silently downgrade a recurring meeting to a regular one, and the recurrence rule is preserved.
  • README sync (README.md / README_EN.md): Command tree extended with app get / app set; new app section with parameter table and examples (client 3.45.10 compatibility notice, https:// certificate-trust requirement, --sdk-name display-width cap, and the three --layout-style options).

Fixed

  • meeting create / meeting update gating logic for --join-type and --waiting-room corrected (cmd/meeting/create.go, cmd/meeting/update.go): Previously --join-type was gated by > 0 and --waiting-room was gated by the bool value, which caused two bugs: (1) explicitly passing --join-type 0 was silently dropped and never sent to the server; (2) update could not turn waiting-room off once it had been turned on — the bool zero value false was indistinguishable from "flag not provided" and was skipped. Both flags now use cmd.Flags().Changed(...) — consistent with other advanced-setting fields (--water-mark-type, etc.): if the user did not explicitly pass the flag, nothing is sent and the server value is preserved; only explicit input is written into settings. Users can now reliably switch join-type back to "all members" and turn off an already-enabled waiting room.

[v1.0.16] - 2026-09-02

Added

  • New minute subcommand group (cmd/minute/): Yuanbao meeting-minutes capabilities — search across meetings by keyword / time range, and retrieve full minute detail (steady-state or rolling transient) by minute ID / meeting ID / meeting code.
    • minute search (cmd/minute/search.go) — Search Yuanbao minutes by keyword and/or time range. All filter parameters are optional and freely combinable. Backed by GET /v1/mcp/asr/search-minutes-mcp and wired through WithApiCmd(ApiCmdMinuteSearch).
      • --query — Search keyword; bounded by maxQueryLen = 50 runes and validated via utils.CharacterLimit.
      • --start / --end — ISO 8601 window bounds. --start >= --end is rejected client-side with InvalidArgsError.
      • --page-token / --page-size (default 20, max 50, capped via cmdutil.ClampingPageSize + pageSizeMaxMinutes) — Standard pagination.
      • Response is post-processed with WithTotalCountLogic.
    • minute get (cmd/minute/get.go) — Query Yuanbao minute detail by minute ID, meeting ID, or meeting code. Supports two modes via the --short-summary flag, resolved at runtime by FlagSwitchWithDefault into either ApiCmdMinuteGet (stable) or ApiCmdMinuteGetTransient (transient).
      • Stable mode (default): Fetches steady-state minutes via GET /v1/mcp/asr/get-minutes-mcp. Accepts --minute-id, --meeting-id, or --meeting-code (three-way choice); --meeting-code is automatically resolved to meeting_id via GET /v1/meetings. Supports --sub-meeting-id for recurring meetings, --overview / --summary-points / --todos content toggles (all default true), and --page-token / --page-size (default 10, max 30) pagination.
      • Transient mode (--short-summary): Fetches rolling (transient) minutes via GET /v1/mcp/asr/get-transient-minutes-mcp. Requires --minute-id. Automatically loops through all pages (up to maxPages = 100) using next_page_token, merging all items into a single response with has_more: false. Page size defaults to 100, max 300.
  • Meeting list enriched with Yuanbao minute summary (cmd/meeting/minute_enrich.go): meeting get, meeting list, meeting list-ended, and meeting search now automatically enrich each meeting object with minute_total_count and a minutes array (minute ID, start time, overview) by calling POST /v1/mcp/asr/get-meeting-minutes-summary-mcp after the meeting query completes. The enrichment is best-effort — on any failure the original data is returned unchanged so meeting output is never blocked by minute query errors.
    • enrichMeetingsWithMinutes() collects meeting_id (and optionally sub_meeting_id) from the meeting list, batch-queries the minutes summary API, and merges results back into each meeting object.
    • normalizeMinutes() converts minute_start_time from unix timestamp to human-readable YYYY-MM-DD HH:MM format in-place, handling both second-level and millisecond-level timestamps.
    • Under --compact, the client-side injected minutes / minute_total_count fields are preserved via the new compactFieldsWithAllEnrichments() (cmd/meeting/enrich.go), which merges both recordEnrichmentFields and minuteEnrichmentFields into the remote schema whitelist.
  • New agent_init.py script for agent identity initialization (skills/tmeet-skill/scripts/agent_init.py): A standalone Python script that atomically writes agent.json (AI-Agent name + LLM model) to the tmeet config directory. Resolves config dir from TMEET_CLI_CONFIG_DIR env var with ~/.tmeet/ fallback. Uses tmp-file + os.replace for atomic writes, mirrors the Go implementation's behavior. Replaces the previous TMEET_AGENT / TMEET_MODEL environment-variable approach that was wired into auth login.
  • New ApiCmd constants (internal/cmdutil/api_schema.go): ApiCmdMinuteSearch, ApiCmdMinuteGet, ApiCmdMinuteGetTransient — so the new minute commands plug into the --compact and middleware pipeline.

Changed

  • Agent identity initialization decoupled from auth login (cmd/auth/login.go): The TMEET_AGENT / TMEET_MODEL environment-variable reading and config.SaveAgentConfig() call have been removed from the login flow. Agent identity is now managed externally via scripts/agent_init.py, which the SKILL instructs the model to run silently at the start of each new session or on model switch. This decoupling means agent identity can be updated without re-login, and login no longer has a side-effect dependency on environment variables.
  • compactFieldsWithRecords replaced by compactFieldsWithAllEnrichments (cmd/meeting/enrich.go, cmd/meeting/record_enrich.go): The per-enrichment compactFieldsWithRecords() helper has been removed from record_enrich.go and replaced by a unified compactFieldsWithAllEnrichments() in the new enrich.go, which merges both recordEnrichmentFields and minuteEnrichmentFields into the compact whitelist. meeting list, meeting list-ended, and meeting search now call compactFieldsWithAllEnrichments() instead of compactFieldsWithRecords().
  • package.json version bumped to v1.0.15 (package.json): npm package version updated from v1.0.14 to v1.0.15.
  • SKILL bumped to 1.0.16 with Yuanbao minutes routing and agent-init overhaul (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-minute.md, skills/tmeet-skill/references/tmeet-auth.md):
    • New minute command group in command tree: minute search and minute get added to the command overview with a link to the new references/tmeet-minute.md.
    • New "元宝纪要查询" routing section in SKILL.md: A comprehensive decision table distinguishing "known meeting" (permission-based routing: can_view → record smart-minutes, otherwise → minute get) from "cross-meeting content search" (dual-path: minute search + record search --query-field transcript_content). Includes explicit rules for dual-search fallback, source labeling, and terminology discipline ("元宝纪要" vs "智能纪要").
    • New "纪要 vs 录制" disambiguation table: Inserted above the existing "查询命令选择准则" to route "纪要/总结/要点/待办" requests to the correct pipeline before the user's query is misclassified as a meeting-metadata query.
    • New references/tmeet-minute.md: Full reference doc for the minute command group — routing guidelines, search / get parameter tables, response field tables, stable vs transient mode documentation, typical workflows, and terminology rules.
    • Agent-init guidance rewritten: The TMEET_AGENT / TMEET_MODEL environment-variable instruction replaced with a python3 ./scripts/agent_init.py --agent ... --model ... invocation rule — must run silently at session start or model switch, failure is ignored, same combination is not repeated.
    • tmeet-auth.md updated: Agent identity note changed from env-var approach to agent_init.py script invocation.
  • README sync (README.md / README_EN.md): Command tree updated with minute search / minute get; pagination quick-reference table extended with minute search (default 20, max 50) and minute get (default 10, max 30); full parameter tables and usage examples added for both commands in both Chinese and English READMEs.

[v1.0.15] - 2026-08-07

Added

  • New control waiting-room subcommand (cmd/control/waiting_room.go, internal/cmdutil/api_schema.go, internal/utils/enumerate/waiting_room_operate_type.go): Manage waiting room members in an ongoing meeting. Backed by PUT /v1/real-control/meetings/{meeting_id}/waiting-room and wired through WithApiCmd(ApiCmdControlWaitingRoom). Classified as a write operation and listed in the SKILL dangerous-operations table.
    • --meeting-id (required) — Meeting ID.
    • --operate-type (required) — One of enter-meeting (admit into meeting) / back-to-waiting (move back to waiting room) / expel (expel from waiting room). Uses the new reusable cmdutil.EnumValue cobra flag type for client-side validation.
    • --allow-rejoin (optional, --operate-type=expel only) — Whether expelled members are allowed to rejoin. Only sent to the server when the flag is explicitly changed by the user.
    • --users / --sip-users / --pstn-users (at least one required) — Map respectively to regular members' open_id, SIP devices' ms_open_id (instanceid=9), and PSTN devices' ms_open_id (instanceid=0). Total across all three capped at MeetingControlUsersListMax = 20, reusing PackageMeetingControlUsers / PackageMeetingControlSpecialUsers. The kick-target source hard rule from v1.0.8 applies here as well: user identifiers must come from report participants.
    • --operate-type is converted from the CLI string to the downstream API's uint32 enum via the new WaitingRoomOperateTypeValue lookup, backed by the WaitingRoomOperateType enum and its bidirectional name/value map.
  • New report participants-export subcommand (cmd/report/participants_export.go, internal/cmdutil/api_schema.go): Export meeting participants list as an async task. Backed by POST /v1/meetings/export-participants-list and wired through WithApiCmd(ApiCmdReportParticipantsExport).
    • --meeting-id (required) — Meeting ID.
    • --sub-meeting-id (optional) — Sub-meeting ID for recurring meetings.
    • --start / --end (optional) — ISO 8601 window bounds, each converted to a unix timestamp via utils.ISO8601ToTimeStamp before being sent to the server.
    • --file-type (default xlsx) — Export format: xlsx or json; validated client-side.
  • New report job-result subcommand (cmd/report/job_result.go, internal/cmdutil/api_schema.go, internal/utils/enumerate/export_job_status.go): Query the result of an async export task. Backed by GET /v1/export/{job_id} and wired through WithApiCmd(ApiCmdReportJobResult).
    • --job-id (required) — Task ID returned by report participants-export.
    • The response status field is post-processed with the new ExportJobStatusConverter, mapping integer status codes (1=成功 / 2=失败 / 3=处理中) to Chinese display names via the ExportJobStatus int enum.
  • Meeting list enriched with recording basic info (cmd/meeting/record_enrich.go): meeting get, meeting list, meeting list-ended, and meeting search now automatically enrich each meeting object with records_total_count and a records array (recording subject, duration, start time, state, type) by calling the recording APIs after the meeting query completes. The enrichment is best-effort — on any failure the original data is returned unchanged so meeting output is never blocked.
    • meeting get uses the paginated GET /v1/mcp/records/meet-basic-info-list API (capped at maxFullRecordPages = 2 × fullRecordPageSize = 100 = 200 records max) to fetch the full recording snapshot.
    • meeting list / meeting list-ended / meeting search use the batched POST /v1/mcp/records/meet-basic-info API for a lightweight preview.
    • normalizeRecords() inlines state_int / type_int → human-readable state / type string renaming and drops the _int fields, so the output never leaks raw integer codes.
    • Under --compact, the client-side injected records / records_total_count fields are preserved via compactFieldsWithRecords(), which merges recordEnrichmentFields into the remote schema whitelist — the remote schema does not know about these fields since they are injected client-side.
  • New reusable EnumValue cobra flag type (internal/cmdutil/enum.go): A pflag.Value implementation that validates the flag input against a predefined allowlist at parse time, returning a clear InvalidArgsError listing the allowed values when the input is unrecognized. Used by control waiting-room's --operate-type and available for future enum-constrained flags.
  • New DurationSecondsConverter (internal/utils/converter.go): Seconds-based counterpart of the existing HHMMSSConverter (which expects milliseconds). Converts float64 or string second values to HH:MM:SS / MM:SS format. Applied to the duration field in recording objects enriched into meeting output.
  • New ExportJobStatus int enum and ExportJobStatusConverter (internal/utils/enumerate/export_job_status.go, internal/utils/converter.go): Maps export job status IDs to Chinese display names; wired into report job-result output at path status.
  • New WaitingRoomOperateType enum (internal/utils/enumerate/waiting_room_operate_type.go): Defines the three waiting room operations (enter-meeting=1 / back-to-waiting=2 / expel=3) with a bidirectional name↔value map; WaitingRoomOperateTypeName for display and WaitingRoomOperateTypeValue for CLI-to-API conversion.
  • New ApiCmd constants (internal/cmdutil/api_schema.go): ApiCmdReportParticipantsExport, ApiCmdReportJobResult, ApiCmdControlWaitingRoom — so the new commands plug into the --compact and middleware pipeline.

Changed

  • Windows DPAPI keychain now self-heals on unrecoverable master-key failure (internal/core/keychain/keychain_windows.go): When the DPAPI master key becomes permanently unusable (e.g. Windows password force-reset by an administrator, account type switched between local/Microsoft account, Windows Hello/PIN rebuilt, system restored to a pre-credential state), the keychain now detects the condition via the new isDPAPIKeyInvalid() helper — classifying NTE_BAD_KEY_STATE, ERROR_INVALID_DATA, and NTE_BAD_DATA as "permanently dead" — and auto-purges every registry value under the keychain path via purgeStaleRegistryEntries(), then returns ErrNotFound. The upper layer transparently generates a fresh master key on the next login, so the user is simply prompted to re-login instead of being permanently stuck on a "Key not valid for use in specified state" error. Only unambiguous "key permanently unusable" errors trigger self-healing; transient failures (permission denied, EDR interception, DLL not loaded) are surfaced as-is and do not wipe recoverable ciphertext.
  • Windows keychain Remove now cascade-deletes master key on last logout (internal/core/keychain/keychain_windows.go): When removing the last remaining credential (only master_key left in the registry), the master key is now automatically deleted as well. This prevents a leftover undecryptable zombie master key from permanently blocking the next login after a future DPAPI credential change. Regenerating a master key on the next login is cheap (<10ms) and avoids a hard-to-diagnose failure mode. Multi-account scenarios (other business ciphertexts still present) are unaffected — master key is only deleted when it is the sole remaining value.
  • Base64DecodeConverter hardened against non-Base64 and non-UTF-8 input (internal/utils/converter.go): The converter now validates that the decoded output is valid UTF-8 before returning it (prevents garbled output when a field happens to look like Base64 by coincidence). The fallback path switched from URLEncoding (with padding) to RawURLEncoding (no padding), matching the encoding used by some backends. Empty string inputs now short-circuit without a decode attempt.
  • Record state enum descriptions now carry actionable hints (internal/utils/enumerate/record_state.go): RecordStateRecording → 录制中,不可查看或申请, RecordStateTranscoding → 转码中,不可查看或申请, RecordStateDone → 转码完成,可根据录制文件权限进行下一步. These descriptions are surfaced in the meeting recording enrichment output so the user immediately knows whether a recording is actionable.
  • Build script now hardens binary permissions with self-check (build.sh): After compilation, all non-.exe binaries are automatically chmod-ed to 0755, and a mandatory -x check follows — if any binary lacks the executable bit, the build fails with a clear error. This prevents broken releases caused by umask anomalies, post-processing steps, or publishing environments that strip the x bit.
  • npm wrapper tmeet.js now uses a three-layer executable fallback (scripts/tmeet.js): (1) accessSync(X_OK) probe — if the binary is already executable, use it directly (fast path for publish artifacts with baked-in 0755); (2) in-place chmodSync — for conventional environments that permit chmod; (3) copy to os.tmpdir() + chmod — for sandbox/read-only-FS environments where the original path rejects chmod with EROFS/EPERM/EACCES/ENOSYS. A simple size + mtime cache avoids redundant IO on every cold start.
  • WithCompact documentation improved (internal/output/options.go): Now clearly documents that the compact field list is used as-is and that callers with client-side injected fields (e.g. records enrichment) must merge them into the slice beforehand rather than relying on the option to do it.
  • SKILL bumped to 1.0.15 with major structural overhaul (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-meeting.md, skills/tmeet-skill/references/tmeet-record.md, skills/tmeet-skill/references/tmeet-report.md, skills/tmeet-skill/references/tmeet-control.md, skills/tmeet-skill/references/tmeet-contact.md, skills/tmeet-skill/references/tmeet-tshoot.md):
    • New "命令总览与详情索引" section: A full command tree with clickable Markdown links to every reference doc, replacing the scattered per-module descriptions.
    • New "安全规则" centralized section: Gathers all safety rules from scattered locations into one chapter — dangerous-operation confirmation table (with explicit "wait is hard requirement" anti-pattern table covering self-Q&A, fabricated user instructions, and default-option auto-selection), member-id privacy rule (meeting_id must not be exposed to users), contact-usage gate, kick-target source constraint, multi-result confirmation rule, and missing-parameter prohibition.
    • New "参数规范" section: Consolidates time format, --format, --compact, and pagination rules under a single heading.
    • "查询命令选择准则" relocated and expanded: Now directly below the command tree with an explicit "list vs search" decision table, a recording-query routing pointer to tmeet-record.md, and anti-pattern rules (no keyword-cramming into list, no keyword-as-time confusion, clarify ambiguous input).
    • "核心概念" section removed: The concepts are now covered inline in the respective reference docs.
    • tmeet-report.md updated: Added parameter tables and usage notes for participants-export and job-result, documenting the export→poll-result async workflow.
    • tmeet-control.md updated: Added waiting-room command with parameter table and the three operation-type scenarios.
    • tmeet-meeting.md updated: Documented the 90-day query interval limit for meeting search's --start / --end (server returns 190004 when exceeded; caller must split into ≤90-day windows). Recording enrichment fields (records, records_total_count) documented in meeting output schemas.
    • tmeet-record.md updated: Added "录制查询路由总则" defining the multi-tier routing rules across meeting get/meeting search/meeting list-ended/record list/record search/record transcript-search, plus permission_status permission-judgment guidance.
    • tmeet-contact.md and tmeet-tshoot.md: Refined error-handling guidelines and simplified document structure.

Fixed

  • meeting get recording duration field now uses DurationSecondsConverter (cmd/meeting/get.go): Previously the recording duration field (expressed in seconds by the backend) was mistakenly routed through HHMMSSConverter (which expects milliseconds), producing durations that were 1000× too small. The new DurationSecondsConverter correctly interprets the value as seconds and produces the canonical HH:MM:SS / MM:SS output.

[v1.0.14] - 2026-07-28

Added

  • New meeting search subcommand (cmd/meeting/search.go, internal/cmdutil/api_schema.go): Keyword / meeting-code / time-window search for meetings, complementing the pure time-window meeting list and meeting list-ended. Backed by the new GET /v1/meetings/search-meetings endpoint and wired through the standard middleware chain (WithApiCmd(ApiCmdMeetingSearch) + WithCompact). All filter flags are optional and freely combinable.
    • --query — Search keyword; bounded by maxQueryLen = 50 runes and validated via utils.CharacterLimit.
    • --query-field (default all) — Field to match --query against: subject / creator (creator nickname or remark name) / note (user's private note on the meeting) / all.
    • --meeting-code — Exact-match meeting code; normalized via the new cmdutil.FormatMeetingCode before being sent to the server.
    • --start / --end — ISO 8601 window bounds. The server matches a meeting if any of its scheduled start, actual start, or the current user's join time falls inside the window. --start >= --end is rejected client-side with InvalidArgsError.
    • --page-token / --page-size (default 30, max 30, capped via cmdutil.ClampingPageSize + cmdutil.PageSizeMaxMeetings) — Standard v1.0.5 pagination.
    • Response is post-processed with MeetingTypeConverter + the new MeetingStatusInSearchConverter at meetings.status, then WithCompact and WithTotalCountLogic (see below).
  • New record search subcommand (cmd/record/search.go, internal/cmdutil/api_schema.go): Keyword / meeting-code / meeting-id / time-window / file-type search across recordings and their AI-derived artifacts (transcript / smart minutes / timeline). Backed by GET /v1/mcp/records/search and wired through WithApiCmd(ApiCmdRecordSearch) + WithCompact.
    • --query (bounded by maxQueryLen = 50 runes, validated via utils.CharacterLimit) and --query-field (default all) — Field set is subject / creator / transcript_content / smart_minutes / timeline / all; the last three let the user grep the recording's derived content, not just its metadata.
    • --file-type (default all) — Restrict results to video / audio / transcript / upload / external / all.
    • --meeting-id / --meeting-code — Optional filters; --meeting-code is normalized via cmdutil.FormatMeetingCode.
    • --start / --end — ISO 8601 window bounds with the same start >= end client-side guard.
    • --page-token / --page-size (default 30, max 30, capped via cmdutil.PageSizeMaxRecords).
  • New MeetingStatusInSearch string enum and converter (internal/utils/enumerate/meeting_status_in_search.go, internal/utils/enumerate/meeting_status_in_search_test.go, internal/utils/converter.go): Maps the search endpoint's status values unknown / upcoming / in_progress / closed to their Chinese display names (未知状态 / 待开始 / 进行中 / 已结束). Unrecognized inputs fall back to Unknown. Wired into meeting search output at path meetings.status via the new MeetingStatusInSearchConverter (stringEnumConverter-backed).
  • New output.WithTotalCountLogic output option (internal/output/options.go): When the response's top-level total_count is 0, the field is stripped from the payload via utils.DeleteFields (maxDepth=10) so the caller does not see a misleading zero (typically emitted by the server as a placeholder before the real count is computed). Wired into meeting search, record search, and record list. Non-zero counts are preserved as-is.
  • New cmdutil.FormatMeetingCode normalization helper and companion unit tests (internal/cmdutil/meeting_help.go, internal/cmdutil/meeting_help_test.go): Accepts the four common ways users copy a meeting code — #腾讯会议:295-150-176, 295-150-176, 295 150 176, 295150176 — and reduces every form to the bare digit string. Strips the #腾讯会议: / #腾讯会议: prefix (full-width and half-width colon), then removes hyphens, spaces, tabs, and single quotes via a single strings.NewReplacer. Applied at every entry point that accepts a meeting code: meeting get, meeting search, record list, record search. Replaces the ad-hoc single-quote stripping added in v1.0.13.
  • JSON output stops HTML-escaping < / > / & (internal/output/print.go): New internal marshalNoEscape helper wraps json.Encoder with SetEscapeHTML(false) and strips the trailing newline that Encoder.Encode appends, so the top-level FormatPrint output preserves URLs, query strings, and other verbatim text exactly as the server returned them. Both json and json-pretty formats use the new helper.
  • New output.MetaKeyFilterField exported constant (internal/output/options.go): The v1.0.12 metaKeyFilterField sentinel is now exported so downstream callers (and the SKILL / docs) can reference the reserved filter_field meta key by name instead of hard-coding the string.

Changed

  • meeting get and record list now normalize --meeting-code via cmdutil.FormatMeetingCode (cmd/meeting/get.go, cmd/record/list.go): Previously these commands forwarded the flag verbatim, so #腾讯会议:295-150-176 or 295 150 176 would silently miss on the server. They now share the same normalization pipeline as the new search commands.
  • record list output pipeline gains WithTotalCountLogic (cmd/record/list.go): The option chain becomes WithCompact → WithConvert → WithTotalCountLogic, so a server-reported total_count = 0 is stripped from the response, matching the new search commands' behavior.
  • SKILL bumped to 1.0.14 with a new "list vs. search" decision guide and hardened pagination / contact / write-op rules (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-meeting.md, skills/tmeet-skill/references/tmeet-record.md, skills/tmeet-skill/references/tmeet-contact.md, skills/tmeet-skill/references/tmeet-control.md, skills/tmeet-skill/references/tmeet-report.md):
    • New "查询命令选择准则(list vs search)" section: A decision table mandates that any query carrying a keyword (subject / meeting code / creator / note / …) — even when combined with a time window — must go through meeting search or record search; pure-time queries stay on list / list-ended / record list. Explicitly forbids "cramming" keyword conditions into list commands and calls out the composite-condition case ("上周和张三的会议" → --query "张三" --start ... --end ...), plus a "when in doubt, ask" rule for ambiguous numeric inputs (meeting code vs. meeting id).
    • Pagination discipline tightened: Unless the user has explicitly asked for "all / 全部 / 所有 / 完整" results, the model must not silently follow next_page_token across pages; it must first show the current page's key info together with a "there is more" hint and ask the user before paging further. The deprecated-parameters list is corrected to --page / --pos / --size, and a new callout clarifies that record transcript-get's --pid / --limit are not pagination and are not deprecated.
    • Contact commands demoted from "member lookup" to "openId resolver": contact search / contact lookup-by-phone / contact lookup-by-email are now strictly limited to two downstream actions — meeting invitation (meeting invitees-add / meeting invitees-replace) and calling a non-participant into the meeting (control call). A new "调用前置门禁" self-check requires the model to confirm the current turn contains such a downstream action before invoking any contact command; when the user's request stops at "who is this person / their department / their phone / do they exist", the model must refuse with a fixed rejection template rather than call the contact API. The "back-fill invitee names via contact search" affordance from v1.0.8 has been removed. tmeet-contact.md gains the front-matter block and rejection template; tmeet-meeting.md's invitee-mutation reply template no longer permits falling back to contact search for name resolution.
    • meeting invitees-add added to the confirm-before-execute table and meeting update's reference doc now carries an explicit "写操作,执行前请确认用户意图" callout listing "which field is changing from A to B" as part of the required confirmation payload.
    • report participants reference doc now documents that the endpoint returns all attendees (both still-in-meeting and already-left) and that left_time being empty means "still in meeting" vs. non-empty meaning "already left at that time"; a matching "returned more people than expected" entry is added to the troubleshooting table.
    • tmeet-control.md no longer opens with the generic "look up members via contact search" line; the guidance is scoped down and moved inline to the control call section only.

Removed

  • v1.0.13's ad-hoc single-quote sanitization in the meeting-code path is gone — its behavior is now subsumed by the more general cmdutil.FormatMeetingCode helper, which also handles the #腾讯会议: prefix, hyphens, spaces, and tabs.

[v1.0.13] - 2026-07-22

Added

  • New internal/crash package: CLI panic capture and server-side reporting (internal/crash/recover.go, internal/crash/reporter.go): Introduces a global panic safety net for the CLI process.
    • crash.RecoverAndReport(ctx, tmeet, r) — Handles a value already recovered by the caller's deferred closure. Captures the panic value together with runtime.Version() / runtime.GOOS / runtime.GOARCH and debug.Stack(), head-truncates the resulting stack to maxCrashStackLen = 8192 bytes to avoid oversized request bodies, forwards it to Report, prints a single-line Error: tmeet crashed: <value> to stderr, and returns (PanicExitCode, true). The function intentionally does not re-panic and must not call os.Exit itself so that sibling defers (most importantly log.Close()) still run during the normal function unwind. recover() is required to be called directly inside the caller's deferred closure — moving it into RecoverAndReport would make recover() return nil and let the panic keep propagating.
    • crash.Report(ctx, tmeet, errorCode, crashStack) — Best-effort POST to /v1/cli/crash via the existing REST proxy (so it inherits auth, retry, and the Tmeet-Cli-Ver / Tmeet-Trace common headers; cli_version and cli_cmd are therefore deliberately omitted from the body). Wrapped in an inner defer recover() for defence in depth, guarded against nil tmeet / tmeet.UserConfig, and bounded by an independent 1-second reportTimeout so a slow network never delays CLI exit. On failure the error is silently downgraded to a log.Warnf line.
    • crash.PanicExitCode = 2 — Dedicated exit code for panic-terminated runs, deliberately distinct from the 1 used for regular business failures so operators can tell crashes apart from expected errors by exit code alone.
  • New ClientCodePanic = 9000 client error code (internal/exception/client_code.go): Sits above the existing 2xxx / 3xxx client-error ranges and is used as the error_code field of the /v1/cli/crash payload.

Changed

  • cmd.Execute() wired into the crash safety net and switched to a named return value (cmd/root.go): Execute() now returns (exitCode int) as a named return so the deferred panic handler can override the exit code without breaking the normal return 0 / return 1 flow. log.Init / defer log.Close() is registered before the crash-recovery defer, relying on LIFO defer ordering so that RecoverAndReport (which itself logs via the file logger and the REST proxy) finishes before log.Close() runs — writing to an already-closed logger would panic and be silently swallowed by the reporter's inner recover(). The top-level defer closure owns recover() and passes its result into crash.RecoverAndReport; when the return value indicates a crash, exitCode is set to crash.PanicExitCode. All existing subcommand error paths continue to return 1 via log.Errorf + return 1.
  • crash.Report body simplified (internal/crash/reporter.go): operator_id is read directly from tmeet.UserConfig.OpenId with a fixed operator_id_type = 2 (openId), removing an earlier intermediate config-lookup helper. When tmeet.UserConfig is nil (i.e. the user has never logged in), the report is skipped instead of sending an empty operator, since the server cannot attribute an anonymous crash to a specific installation anyway.
  • SKILL bumped to 1.0.9 and auth login guidance rewritten (skills/tmeet-skill/SKILL.md, skills/tmeet-skill/references/tmeet-auth.md): The v1.0.12 note already flipped auth login from background-only to foreground-only; this release finishes that migration in the reference doc: the 2>&1 & background examples are replaced with plain foreground invocations, and a new bullet clarifies that auth login prints the authorization URL and then blocks for up to 300 s waiting for the browser callback, so backgrounding it detaches the process from the controlling terminal and prevents credentials from being persisted. Two additional agent-facing rules were added: (1) on Hermes without a default browser, the agent must not invoke tmeet auth login itself and must instead instruct the user to run it manually in a terminal; (2) on first auth login (or whenever the model changes), the agent must write the current agent identifier (Cursor / Claude Desktop / Cline / CodeBuddy / …) and model name (Claude 3.5 Sonnet / GPT-4o / DeepSeek / …) into TMEET_AGENT / TMEET_MODEL without asking the user.

[v1.0.12] - 2026-07-20

Added

  • Server-driven response field filtering for report participants (cmd/report/participants.go, internal/output/options.go): The downstream API may now return a top-level filter_field string in the response body — a comma-separated list of field names or dot paths (e.g. participants.email,participants.phone) that the CLI must strip before exposing the data to the caller. The behavior is entirely driven by the server and is not surfaced as a CLI flag; the filter_field meta field itself is always removed so it never leaks out. This lets the backend tighten per-endpoint field visibility without a CLI release.
  • New output.WithMetaFieldFilter(metaKey) output option and exported output.MetaKeyFilterField constant (internal/output/options.go): Generic "strip a reserved meta key from the response" helper, with a documented special case: when metaKey == "filter_field", the value of that meta key is additionally parsed as a comma-separated field list and each entry is stripped from the response via utils.DeleteFields with maxDepth=10. Wired into report participants and layered before WithConvert / WithCompact in the option chain, so field renaming and compact trimming operate on the already-filtered payload.
  • New internal/cmdutil/apicmdctx sub-package (internal/cmdutil/apicmdctx/apicmdctx.go): Extracts the "resolved ApiCmd on context" primitive out of internal/proxy/rest-proxy into a dependency-free sub-package. Exposes apicmdctx.Inject(ctx, apiCmdName) / apicmdctx.Get(ctx) with an unexported key type. Both internal/cmdutil/middleware and internal/proxy/rest-proxy now depend on this package instead of the middleware depending on the proxy, breaking the previous import cycle risk.

Changed

  • WithApiCmd middleware and REST proxy switched to apicmdctx (internal/cmdutil/middleware/api_cmd.go, internal/proxy/rest-proxy/proxy.go, cmd/tshoot/log.go): middleware.WithApiCmd now writes the resolved ApiCmd via apicmdctx.Inject; the REST proxy reads it via apicmdctx.Get when composing the Tmeet-Cli-Name header; tshoot log's upload flow also injects ApiCmdTshootLogUpload via apicmdctx.Inject instead of the old restProxy.InjectApiCmdContext. The v1.0.11 restProxy.InjectApiCmdContext / restProxy.GetApiCmdFromContext helpers have been removed from the proxy package.
  • report participants output option ordering hardened (cmd/report/participants.go): Reordered FormatPrint options to WithMetaFieldFilter → WithConvert → WithCompact, so server-side field stripping happens first, then enum/base64 conversion runs on the trimmed payload, then --compact whitelist filtering is applied last. This gives a well-defined single "filter → transform → project" pipeline instead of the ad-hoc pre-FormatPrint JSON manipulation that lived in participants.go before.
  • math/rand upgraded to math/rand/v2 (internal/core/thttp/authenticator.go, internal/proxy/rest-proxy/proxy.go): The auth nonce generator and the REST proxy's random component now use math/rand/v2's auto-seeded top-level rand.IntN, removing the manual rand.New(rand.NewSource(time.Now().UnixNano())) boilerplate. Behavior is unchanged; the code paths are simpler and per-goroutine safe.
  • WithFilterFields output option removed (internal/output/options.go): The v1.0.10 output.WithFilterFields([]string) variant — which required the caller to hard-code the list of fields to strip — has been deleted; its use case is now covered by the more flexible, server-driven WithMetaFieldFilter. No caller in this repository relied on the old option.
  • SKILL bumped to 1.0.8 (skills/tmeet-skill/SKILL.md): The auth login guidance has been rewritten. The previous instruction "must run in the background with 2>&1 &" was wrong — background execution detaches the process from the controlling terminal and causes the OAuth callback to fail to persist credentials. The new guidance clarifies that auth login prints the authorization URL and then blocks for up to 300 seconds waiting for the user to complete the browser authorization, and that it must be run in the foreground. Related wording tweaks were also applied to the top-level description.

[v1.0.11] - 2026-07-08

Added

  • AI agent identity tracking via AgentConfig (internal/config/agent.go, internal/config/agent_test.go): New module that records the calling AI agent name and LLM model in plaintext agent.json, separate from user credentials. Provides atomic write (tmp + rename), idempotent clear, and GetAgentConfig / SaveAgentConfig / ClearAgentConfig APIs. Companion unit tests in agent_test.go (9 test cases: missing file, round-trip, nil save, overwrite, clear, idempotent clear, corrupted JSON, path resolution, atomic write content).
  • auth login now persists agent identity (cmd/auth/login.go): After successful login, reads TMEET_AGENT / TMEET_MODEL environment variables and saves them to agent.json. Write failure is logged but non-fatal — login succeeds regardless.
  • auth status now displays UserName (cmd/auth/status.go): When the refresh token is still valid, calls /v1/cli/get-user-info to fetch and display the user's name alongside OpenId. Re-reads user config after the API call to reflect any token refresh that occurred in-flight.
  • meeting update supports --sub-meeting-id for single sub-meeting modification (cmd/meeting/update.go): New --sub-meeting-id flag (recurring meetings only) modifies a single sub-meeting's start/end time without altering the recurring rule. Mutually exclusive with --recurring-type / --until-type / --until-count / --until-date — validated at runtime with a clear error message. Extracted buildRecurringRule() helper for cleaner payload assembly.
  • Tmeet-Cli-Name request header and ApiCmd context passthrough (internal/proxy/rest-proxy/proxy.go, internal/cmdutil/middleware/api_cmd.go): The REST proxy now attaches a Tmeet-Cli-Name header carrying the resolved ApiCmd identifier. New InjectApiCmdContext / GetApiCmdFromContext utilities use an unexported key type. The WithApiCmd middleware writes the resolved ApiCmd to both the cobra annotation and cmd.Context(), decoupling the proxy from annotation reads.
  • New ApiCmd constants for tshoot commands (internal/cmdutil/api_schema.go): ApiCmdTshootLogUpload (covers the two-step log upload flow: fetch upload token + notify upload complete) and ApiCmdTshootFeedback. tshoot feedback is now wrapped with WithApiCmd middleware (cmd/tshoot/feedback.go); tshoot log injects ApiCmdTshootLogUpload into context before the upload flow (cmd/tshoot/log.go).

Changed

  • AccessToken proactive refresh with 60-second leeway (internal/auth/auth.go): Introduced accessTokenRefreshLeeway = 60s. RefreshToken now triggers 60s before the declared expiry to absorb clock skew and in-flight network latency, preventing server-side TokenExpired rejection that the rest-proxy treats as fatal (clears local credentials).
  • GetSystemInfo accepts *config.AgentConfig (internal/common/system.go, internal/common/system_test.go): Agent and Model are now resolved via env var (TMEET_AGENT / TMEET_MODEL) → agent.json fallback → empty string. All test call sites updated to pass nil.
  • control kick --allow-rejoin default flipped to true (cmd/control/kick.go): The flag now defaults to true (allow rejoin). Users must explicitly pass --allow-rejoin=false to disallow rejoin. Help text updated accordingly.
  • internal/tmeet.go loads AgentConfig at startup (internal/tmeet.go): Missing agent.json is treated as "not set" and does not block CLI startup. GetSystemInfo is called with the loaded config so agent/model metadata is available for all subsequent REST requests.
  • README and SKILL references synced (README.md / README_EN.md / skills/tmeet-skill/SKILL.md / skills/tmeet-skill/references/tmeet-auth.md / skills/tmeet-skill/references/tmeet-control.md / skills/tmeet-skill/references/tmeet-meeting.md): Added --sub-meeting-id parameter tables and usage examples to meeting update sections. Updated --allow-rejoin default value across all documentation surfaces. auth status output example now includes UserName. control kick description simplified in SKILL.md. SKILL bumped to 1.0.7.

[v1.0.10] - 2026-06-19

Added

  • meeting create / meeting update now expose four advanced meeting-settings flags (cmd/meeting/create.go, cmd/meeting/update.go): --water-mark-type (text watermark, 0-single row / 1-double row / 2-off, default 2; when not 2, allow_screen_shared_watermark is set in tandem), --audio-watermark, --auto-record-type (none / local / cloud), and --auto-asr. These fields are only written to the request settings when cmd.Flags().Changed(...) reports they were explicitly provided, so unspecified flags will not silently override the enterprise defaults; explicitly turning a bool flag off requires the = form, e.g. --audio-watermark=false.
  • Hints output channel for enterprise-locked settings (internal/cmdutil/hints/, internal/cmdutil/meeting.go, internal/output/): Introduced the HintProvider interface and output.WithHints(fn) option, and added a top-level hints field to formatOutput. meeting create / meeting update plug into this channel: when the response settings.corp_lock_mask is non-zero, the output emits messages like Enterprise has locked the [Text Watermark] setting, covering text watermark / audio watermark / auto recording / auto speech recognition. Both top-level settings and meeting_info_list[].settings response shapes are supported, with duplicate entries deduplicated.
  • corp_lock_mask bitmask enum (internal/utils/enumerate/corp_lock_mask.go): Defines four constants (bit0~bit3) with CorpLockMaskName (single bit → label) and CorpLockMaskNames (combined mask → labels in canonical bit order). Companion unit tests in corp_lock_mask_test.go.
  • meeting_join_type / show_all_sub_meetings enum mappings (internal/utils/enumerate/): 1=all / 2=invited / 3=internal and 0=no / 1=yes. The new MeetingJoinTypeConverter / ShowAllSubMeetingsConverter are wired into meeting list (for only_user_join_type, is_show_all_sub_meetings) and into meeting create / meeting update outputs (for only_user_join_type).
  • New utils.DeleteFields JSON blacklist utility and output.WithFilterFields output option (internal/utils/filter.go, internal/output/options.go): The dual of the v1.0.5 KeepFields whitelist — supports both field-name mode and dot-path mode (arrays auto-expand) and uses maxDepth to bound recursion; WithFilterFields exposes it to the output layer with maxDepth=10.

Changed

  • meeting update help-text fix (cmd/meeting/update.go): Corrected the --recurring-type help text from 1-weekday to 1-weekdays, aligning it with meeting create and the actual semantics.
  • README and SKILL references synced for the new advanced meeting-settings flags (README.md / README_EN.md / skills/tmeet-skill/references/tmeet-meeting.md): Parameter tables and examples for --water-mark-type / --audio-watermark / --auto-record-type / --auto-asr added to the meeting create / meeting update sections, with two notes called out: (1) personal vs. enterprise (organization) accounts behave differently depending on whether the enterprise has set the option to "forced"; (2) explicitly turning a bool flag off requires the =false form.
  • SKILL bumped to 1.0.6 (skills/tmeet-skill/SKILL.md).

[v1.0.9] - 2026-06-12

Added

  • meeting create now supports inviting participants at creation time (cmd/meeting/create.go): A new --invitees flag accepts an open_id list (comma-separated or repeated, max 100), reusing v1.0.8's cmdutil.PackageApiInviteesUsers for dedup and upper-bound checks.
  • meeting update now supports mutating the invitee list as part of the update call (cmd/meeting/update.go): Added the --invitees + --invitees-type flag pair, supporting add / remove / replace strategies; both flags must be set together. Complements the v1.0.8 meeting invitees-add/remove/replace subcommands by avoiding a second round-trip when the meeting body and the invitee list change in the same operation.

Changed

  • REST proxy error handling and retry policy adjusted (internal/proxy/rest-proxy/proxy.go): HTTP 408 / 504 gateway timeouts are now classified as NetworkError to trigger retries; all other non-200 responses (except token expiration) are uniformly classified as NotRetryRequestError and no longer retried. The "should-retry" decision shifts from a server-business-code allowlist to the more conservative "anything other than a network error is not retried", reducing the risk of write operations (calls, kicks, invitee mutations) being silently replayed.
  • README and SKILL references synced for the new invitation flags (README.md / README_EN.md / skills/tmeet-skill/references/tmeet-meeting.md): Parameter tables and examples for --invitees / --invitees-type added to the meeting create / meeting update sections.
  • SKILL bumped to 1.0.5 (skills/tmeet-skill/SKILL.md).

Removed

  • Removed the "specific business-code non-retry" allowlist mechanism introduced in v1.0.7 (internal/exception/server_code.go): Deleted the ServerCodeRecordNotExist (500277) constant, the notRetryCodeMap, and the IsNotRetryCode function; their semantics are now subsumed by the broader "non-200 is never retried" policy above.

[v1.0.8] - 2026-06-11

Added

  • New contact subcommand group (cmd/contact/): Address-book lookup capabilities, primarily intended to resolve names ↔ open_id for the "meeting invitation" and "in-meeting call" scenarios.
    • contact search (cmd/contact/search.go) — Search enterprise address-book members by username, with optional job-title / department filtering when the username matches too many people; when only a single member is matched, the response is trimmed to keep open_id only (via the new output.WithContactSearchLogic hook).
      • --username (required) — Username to search
      • --job-title (optional) — Job title filter when the username yields too many matches
      • --department-name (optional) — Department filter when the username yields too many matches
    • contact lookup-by-phone (cmd/contact/lookup_by_phone.go) — Batch-look up users by phone number; up to 50 numbers per call; each number is pre-validated by utils.ValidatePhone before the request is sent.
      • --phones (required) — Phone-number list, comma-separated or repeat the flag, max 50
    • contact lookup-by-email (cmd/contact/lookup_by_email.go) — Batch-look up users by email address; up to 50 emails per call; each email is pre-validated by utils.ValidateEmail before the request is sent.
      • --emails (required) — Email list, comma-separated or repeat the flag, max 50
  • New control subcommand group (cmd/control/): In-meeting real-time control capabilities.
    • control call (cmd/control/call.go) — In-meeting batch call to bring members into the meeting, up to 20 members per call. Classified as a write operation and listed in the SKILL dangerous-operations table.
      • --meeting-id (required) — Meeting ID
      • --users (required) — Member open_id list to call
    • control kick (cmd/control/kick.go) — Kick members out of an ongoing meeting, supporting three identity types (regular member / SIP / PSTN), capped at 20 in total per call. Classified as a write operation, listed in the SKILL dangerous-operations table, and SKILL further enforces a hard rule that the kick targets must come from report participants and must not come from any contact lookup result.
      • --meeting-id (required) — Meeting ID
      • --allow-rejoin (optional) — Allow kicked members to re-join
      • --users / --sip-users / --pstn-users (at least one required) — Map respectively to regular members' open_id, SIP devices' ms_open_id (instanceid=9), and PSTN devices' ms_open_id (instanceid=0)
  • New invitee-management subcommands under meeting (cmd/meeting/): Building on the existing invitees-list from v1.0.7, this release adds incremental add / remove / full-replace capabilities.
    • meeting invitees-add (cmd/meeting/invitees_add.go) — Append invitees to an existing meeting.
    • meeting invitees-remove (cmd/meeting/invitess_remove.go) — Remove specified invitees from an existing meeting. Classified as a write operation and listed in the SKILL dangerous-operations table.
    • meeting invitees-replace (cmd/meeting/invitees_replace.go) — Replace the meeting's invitee list with a new one; an empty list clears all invitees. Classified as a write operation and listed in the SKILL dangerous-operations table.
    • All three share --meeting-id (required) and --invitees (the open_id list to apply, comma-separated or repeat the flag, max 100); --invitees is required for invitees-add / invitees-remove and optional (empty meaning "clear all") for invitees-replace.
  • New shared user-list packagers for invitee / in-meeting-control commands (internal/cmdutil/users_help.go): Extracted three reusable helpers — PackageApiInviteesUsers (invitees, capped by InviteesListMax = 100), PackageMeetingControlUsers (regular members, open_id, to_operator_id_type=2), and PackageMeetingControlSpecialUsers (SIP / PSTN, ms_open_id, to_operator_id_type=4 plus instanceid) — that uniformly handle dedup, empty-value validation, and upper-bound checks. The constant MeetingControlUsersListMax = 20 is also defined here.
  • New phone / email format validation utilities (internal/utils/validate.go):
    • ValidatePhone — 11-digit phone-number regex check (^1[3-9]\d{9}$).
    • ValidateEmail — Total length ≤ 100; must contain @ with non-empty local / domain parts; rejects quoted local part, consecutive dots, and IP-address domains; finalizes with a regex format check.
    • SplitAndTrim — Generic "split-by-comma + trim + drop empty" helper.
    • These helpers are wired in as pre-call validators for cmd/contact/lookup_by_phone.go / cmd/contact/lookup_by_email.go.
    • Companion unit tests in internal/utils/validate_test.go cover valid, invalid, and boundary cases for both formats.
  • New output.WithContactSearchLogic output option (internal/output/options.go): Applied to contact search responses; when the users array contains exactly one entry, only the open_id field is preserved, avoiding leakage of unnecessary personal information to the agent (mirroring the SKILL constraint that the address book is not a general-purpose people-info lookup).
  • New API Schema identifiers (internal/cmdutil/api_schema.go): Added the ApiCmd constants meeting_invite_add / meeting_invite_remove / meeting_invite_replace / contact_search / contact_lookup_by_phone / contact_lookup_by_email / control_call / control_kick, so the new commands can plug into the v1.0.5 --compact and middleware pipeline.

Changed

  • Root command registers the contact and control subcommand groups (cmd/root.go): contact.NewBaseCmd and control.NewBaseCmd are added between the existing auth / meeting / report / record / tshoot registrations.
  • SKILL bumped to 1.0.4 (skills/tmeet-skill/SKILL.md):
    • Top-level description now mentions the "address book" and "in-meeting control" capabilities.
    • The command tree is updated with meeting invitees-add/remove/replace, the new contact group, and the new control group.
    • The dangerous-operations table now includes meeting invitees-remove, meeting invitees-replace, control call, and control kick, all requiring user confirmation before execution.
    • Added a "contact search is restricted to specific scenarios" rule: it may only be used for meeting invitations, in-meeting calls, and back-filling invitee names — not as a general-purpose people-info lookup.
    • Added a "hard rule on kick-target source": the open_id / ms_open_id passed to control kick must come from report participants, and must not come from any contact lookup result.
    • Added a "multiple results require user confirmation" rule: when commands such as contact search return more than one candidate, the model must list them and let the user choose, rather than picking one on its own.
    • Added a "reply template for meeting-invitee mutations": after meeting invitees-add/remove/replace succeeds, the reply must follow a strict "topic / time / meeting code / join URL / current invitees" template, and the invitee list must show names rather than open_ids, looking them up via meeting invitees-list + contact search when needed.
  • SKILL reference docs added / synced (skills/tmeet-skill/references/): Added tmeet-contact.md and tmeet-control.md; tmeet-meeting.md is updated with parameter tables and usage notes for invitees-add / invitees-remove / invitees-replace.
  • README sync (README.md / README_EN.md): Command tree and command reference updated with meeting invitees-add/remove/replace, contact search/lookup-by-phone/lookup-by-email, and control call/kick, including the corresponding parameter descriptions and examples.

[v1.0.7] - 2026-06-10

Added

  • New record permission-apply-prepare command (cmd/record/permission_apply_prepare.go): When record address / record smart-minutes / record transcript-* fail due to missing permission, this command fetches the approval preview (approval text, meeting topic, recording owner, request notes, etc.) so the agent can show it to the user for confirmation before any write call.
    • --meeting-record-id (required) — Meeting recording ID
    • --meeting-id (optional) — Meeting ID
  • New record permission-apply-commit command (cmd/record/permission_apply_commit.go): Submits the recording-permission approval after user confirmation; returns unique_id / status / approval_url / share_text. Classified as a write operation and listed in the SKILL dangerous-operations table.
    • --meeting-record-id (required) — Must match the value used in prepare
    • --meeting-id (optional) — Meeting ID
  • Non-retryable server error mechanism (internal/exception/): Introduced ServerCodeRecordNotExist (500277) together with notRetryCodeMap / IsNotRetryCode(code); added a new client-side error NotRetryRequestError and error code ClientNotRetryRequest (2008).

Changed

  • REST proxy retry policy (internal/proxy/rest-proxy/proxy.go): RetryIf now also excludes NotRetryRequestError in addition to TokenExpiredError; server-side business errors that hit IsNotRetryCode are surfaced as NotRetryRequestError immediately, avoiding pointless retries on cases such as "recording does not exist".
  • Documentation sync: README.md / README_EN.md command tree and command reference updated with the two new permission-apply commands; skills/tmeet-skill/SKILL.md bumped to 1.0.3 with a "recording permission apply" capability note and record permission-apply-commit added to the dangerous-operations list requiring user confirmation; skills/tmeet-skill/references/tmeet-record.md adds the "permission apply workflow" (prepare → user confirm → commit → show approval_url) and error-handling guidance.

[v1.0.6] - 2026-06-03

Added

  • New tshoot feedback command (cmd/tshoot/feedback.go): Lets the agent (or end user) report troubleshooting feedback to the server, closing the loop between CLI usage and product iteration.
    • --category (required) — Feedback category, one of: tool_not_found / tool_error / tool_inadequate / unexpected_result / suggestion
    • --intent (required) — Original intent of the agent (max 200 characters)
    • --actions-tried — Actions the agent has already tried (max 500 characters)
    • --result — Result or blocker of the tried actions (max 500 characters)
    • --tool-name — Tool/command name involved in the feedback
    • --error-code — Error code returned by the tool, if any
  • New CharacterLimit string validation utility (internal/utils/string.go): UTF-8 rune-count based length check; returns InvalidArgsError with the offending flag name, the configured limit, and the actual length when exceeded. Used by tshoot feedback to enforce the --intent / --actions-tried / --result length caps.
  • Unit tests for CharacterLimit covering ASCII, multi-byte (CJK), boundary, and over-limit cases (internal/utils/string_test.go).

Changed

  • Error logging on failure paths: cmd/root.go now logs execute failed: %v via log.Errorf whenever rootCmd.Execute() returns a non-nil error, and internal/auth/auth.go logs refresh token failed: %v before clearing the local config in TmeetAuth.RefreshToken. Combined with the file-based logging introduced in v1.0.4, these traces are now recoverable through tmeet tshoot log.
  • cmd/tshoot/base.go registers the new feedback subcommand alongside log under the tshoot group.
  • Updated README.md / README_EN.md, skills/tmeet-skill/SKILL.md, and skills/tmeet-skill/references/tmeet-tshoot.md with tshoot feedback usage, parameter tables, and category guidance.

[v1.0.5] - 2026-05-16

Added

  • New global --compact output mode: Added a persistent --compact flag on the root command. When enabled, responses keep only the key fields advertised by the server and strip redundant content. Wired into meeting list / meeting list-ended / meeting invitees-list / record list / report participants / report waiting-room-log.
  • New Cobra command middleware framework (internal/cmdutil/middleware/): Provides Chain (onion-model composition), WithApiCmd (resolves the ApiCmd and writes it to the command annotation), and WithCompact (fetches the schema for the resolved ApiCmd and propagates the compact-field whitelist via context). All API-backed subcommands' RunE are now built on this chain.
  • New API Schema module (internal/cmdutil/): Pulls per-ApiCmd compact fields, field metadata, and cache policy from /v1/api/compact-schema. Ships with ApiCmdResolver (StaticApiCmd / FlagSwitch / ResolverFunc), command annotation helpers (annotations.go), and pagination helpers (pagination.go: ClampingPageSize / ChoosePageOrToken / ChoosePosOrToken).
  • New local file cache primitive (internal/core/filecache/): TTL-based file KV cache; cross-process safety via core/filelock write locks, in-process safety via per-key mutex; writes use temp-file + rename for atomic replacement; corrupted files transparently degrade to a miss. Used as the on-disk store for API Schema responses.
  • New KeepFields JSON whitelist utility (internal/utils/filter.go): Recursively keeps only the specified fields, supporting both field-name mode and dot-path mode (e.g. meeting.recurring_rule.recurring_type); the underlying primitive that powers --compact field trimming.
  • New MeetingUserJoinRoleConverter enum mapping: Converts creator / hoster / invitee into "创建者 / 主持人 / 被邀请者" Chinese display names; applied in meeting list.
  • Options-style output layer: output.FormatPrint now accepts variadic ...Option, with two built-ins — WithCompact and WithConvert — moving compact trimming and field conversion out of individual commands and into the output layer.
  • Unit tests covering the middleware, file cache (concurrent writes, expiration, corruption fallback, cross-process locking), KeepFields, and the new enum.

Changed

  • Unified pagination flags to --page-token + --page-size: All paginated commands now use the new pagination protocol (page_type=1); the legacy --page / --pos flags are marked deprecated (still honored but with lower priority than --page-token). --page-size defaults / caps are aligned accordingly:
    • meeting list / meeting list-ended / meeting invitees-list / record list / record address: default raised to 30, max unified to 30;
    • report participants / report waiting-room-log: default raised to 100, max 100.
  • All API subcommands now flow through the middleware chain: RunE is built as middleWare.Chain(opts.Run, WithApiCmd(...), WithCompact(...)), and the per-command utils.ConvertFields(...) calls have been pushed down into output.FormatPrint(..., WithConvert(...)).
  • Field mapping tweaks for meeting list: Removed the base64 decoding of time_zone (the server now returns plaintext); added the join_meeting_role Chinese mapping; dropped the no-longer-required cursory query parameter.
  • Annotation migration for auth login / auth status: The hand-written cmd.Annotations = map[string]string{"skipPreCheck": "true"} has been replaced with cmdutil.InjectSkipPreCheckAnnotation(cmd) so that annotation keys are managed in one place.
  • Updated README.md / README_EN.md with a new "Pagination" section, the --compact description, and a per-command --page-size default/max quick-reference table; skills/tmeet-skill/ reference docs synced to the new pagination flags and defaults.

[v1.0.4] - 2026-04-24

Added

  • New tshoot log command (cmd/tshoot/): Autonomous log-based troubleshooting support
    • tshoot log — Query and package local log files for self-service troubleshooting
    • --start / --end — Filter logs by ISO 8601 time range
    • --upload — Package and upload logs to the server (login required); returns a log_id
    • Without --upload, logs are packaged into a zip file and saved to the user's home directory
    • Supports pre-filtering by date in file names + precise filtering by in-line timestamps
    • Upload flow: fetch upload token → PUT file to COS → notify server of upload completion
  • New file-based logging module (internal/log/):
    • logging.go — Structured file logging with four levels: DEBUG / INFO / WARN / ERROR
    • Daily log rotation with 10 MB per-file size limit and auto-incrementing sequence numbers
    • Automatic cleanup of log files older than 7 days
    • Asynchronous writes via background goroutine; callers are never blocked
    • Multi-process safety: atomic writes with O_APPEND + cross-process file lock for rotation
    • trace_id.go — TraceID generator (8-char timestamp + 4-char PID + 20-char random hex), propagated via context
  • New CalcFileInfo utility (internal/utils/file.go): Compute file size, SHA256, and MD5 in a single pass
  • New error code ClientUploadToCos (2007) and UploadToCosError error definition (internal/exception/)
  • Unit tests for logging module, TraceID generator, and file utility functions

Changed

  • Refactored log package → output package: Renamed internal/log/log.go to internal/output/print.go; package name changed from log to output. All 16 call sites across cmd/auth/, cmd/meeting/, cmd/record/, cmd/report/ updated with the new import path
  • Enhanced cmd/root.go: Initialize file logging system (log.Init) with defer log.Close(); register tshoot subcommand group; add commandOperationLog() to record operation logs after command execution; inject TraceID into command context via rootCmd.SetContext()
  • Enhanced internal/tmeet.go: Inject TraceID via context.WithValue in NewTmeet(); add TCtx field
  • Enhanced REST proxy (internal/proxy/rest-proxy/proxy.go): Add file logging for HTTP requests and responses
  • Updated README.md / README_EN.md with tshoot log command usage instructions
  • Updated skills/tmeet-skill/ with new tmeet-tshoot.md reference documentation

[v1.0.3] - 2026-04-16

Added

  • Auto-open browser on auth login: After obtaining the OAuth2 authorization URL, tmeet auth login now automatically attempts to open the system default browser to the authorization page, eliminating the need for users to manually copy and paste the URL
  • New --no-browser flag for auth login: Added --no-browser option to disable auto-opening the browser; when set, only the authorization URL is printed (preserving the original behavior)
  • Cross-platform browser opening (internal/core/browser): New platform-specific implementations:
    • macOS (browser_darwin.go): Uses open command
    • Windows (browser_windows.go): Uses rundll32 url.dll,FileProtocolHandler
    • Linux (browser_other.go): Uses xdg-open with graphical environment detection (DISPLAY / WAYLAND_DISPLAY)
  • Unit tests for browser package using fakeExecCommand pattern to mock system commands

Changed

  • Updated README.md / README_EN.md auth login documentation to describe auto-open browser behavior and the new --no-browser flag
  • Updated SKILL.md with --no-browser usage example and Hermes agent-specific note for environments without a default browser
  • Updated tmeet-auth.md parameter table to document the --no-browser option

[v1.0.2] - 2026-04-15

Added

  • Enum mapping for API responses: New internal/utils/enumerate package with human-readable name mappings for API enum fields, including meeting_type, meeting_status, meeting_recurring_type, meeting_recurring_until_type, meeting_user_role, record_state, record_type, record_audio_detect, etc.
  • Time-range filtering: New FilterMeetingsByTimeRange utility (internal/utils/filter.go) to filter meeting lists by start/end time boundaries
  • Path-mode field converter: Enhanced ConvertFields (internal/utils/converter.go) to support dot-separated path mode (e.g., meeting.recurring_rule.recurring_type) for precise field matching
  • Comprehensive unit tests for all new enum mappings, converter, and filter utilities

Changed

  • Console output optimization: Replaced cmd.Println with fmt.Fprintln(cmd.OutOrStdout()) / fmt.Fprintln(cmd.OutOrStderr()) for proper stdout/stderr separation; added structured FormatPrint function with formatOutput struct supporting trace_id, message, and data fields
  • Refactored all command handlers (cmd/meeting/*, cmd/record/*, cmd/report/*) to use the new output methods
  • Renamed internal/utils/json.go → internal/utils/converter.go with enhanced path-mode support
  • Removed unused FormatPrint/FormatPrintPretty from internal/proxy/rest-proxy/proxy.go and cleaned up unused imports
  • Moved existing instanceid.go into the new enumerate sub-package
  • Revised descriptions in README.md, README_EN.md, and skills/tmeet-skill/ references; added security risk warnings

Fixed

  • Removed unused install phony target from Makefile (Fixes #1)
  • Fixed typos across the codebase

[v1.0.1] - 2026-04-07

Added

Initial release of the tmeet CLI tool for Tencent Meeting (WeMeet).

Authentication (tmeet auth)

  • auth login — Log in to tmeet via OAuth2 device-code flow
  • auth logout — Log out from tmeet
  • auth status — Show current authentication status

Meeting Management (tmeet meeting)

  • meeting create — Create a new meeting (supports recurring meetings)
  • meeting get — Get meeting details by meeting ID or meeting code
  • meeting list — List upcoming meetings
  • meeting list-ended — List ended meetings
  • meeting update — Update an existing meeting
  • meeting cancel — Cancel a meeting
  • meeting invitees-list — Get the invitees list of a meeting

Recording Management (tmeet record)

  • record list — List meeting recordings
  • record address — Get recording download addresses
  • record smart-minutes — Get AI-generated smart minutes from a recording
  • record transcript-get — Get transcript details
  • record transcript-paragraphs — Get transcript paragraphs
  • record transcript-search — Search transcript content by keyword

Meeting Reports (tmeet report)

  • report participants — Get the participants list of a meeting
  • report waiting-room-log — Get the waiting room members log

Other Features

  • AES-256-GCM encrypted credential storage (no plaintext on disk)
  • Cross-platform support (macOS / Linux / Windows)
  • File-lock based concurrent access protection
  • Symlink attack prevention
  • Output format support: json (default) and json-pretty