* feat(auth): improve scope handling and output in login flow
- Add scope validation to check for missing requested scopes
- Implement detailed scope breakdown in login success output
- Add new message strings for scope-related output
- Refactor login success output to handle both JSON and text formats
- Add tests for scope validation and output scenarios
* feat(auth): add requested scope caching for device code login
Implement caching of requested scopes during device code login flow to ensure proper scope validation after authorization. The cache is stored in JSON files under config directory and automatically cleaned up after successful or failed authorization.
Add tests for scope caching functionality and verify proper integration with existing login flow.
* docs(auth): add function comments for login scope handling
Add detailed doc comments to all functions in login scope cache and result handling files to improve code documentation and maintainability.
* refactor(auth): remove pending scopes and improve json output stability
- Remove PendingScopes field and related logic as it's no longer needed
- Add emptyIfNil helper to ensure nil slices are normalized to empty slices in JSON output
- Update tests to verify JSON output stability and fix expected text outputs
* refactor(auth): extract device token polling function for testability
Move device token polling to a package-level variable to enable mocking in tests
Add test case for scope cleanup when token is nil
* fix(auth): return JSON write errors instead of ignoring them
Previously, JSON write errors were only logged to stderr but not returned, causing tests to pass when they should fail. Now properly propagate these errors to callers and update tests to verify error handling.
* refactor(auth): simplify scope handling and improve user messaging
remove redundant scope display and consolidate hint messages to focus on actionable guidance
* refactor(auth): improve scope handling and messaging in login flow
remove ShortHint field and simplify scope hint messages
always display missing scopes section with consistent formatting
add StatusHint for successful login with no missing scopes
update tests to reflect new message structure and content
* refactor: migrate vc/minutes shortcuts to FileIO
- vc_notes: replace vfs.Stat + validate.SafeOutputPath + validate.AtomicWrite
with FileIO.Stat/Save for transcript download
- minutes_download: replace validate.SafeOutputPath + validate.AtomicWriteFromReader
with FileIO.Save, use FileIO.Stat for overwrite checks
- Use WrapSaveError to preserve original error messages
* fix: resolve concurrency races in RuntimeContext
- getAPIClient: replace check-then-act with sync.OnceValues, matching
the factory_default.go convention; use NewAPIClientWithConfig to avoid
post-construction config override; fall back to direct construction
for test contexts that bypass newRuntimeContext.
- outputErr: guard first-error capture with sync.Once to prevent data
races if Out() is ever called from concurrent goroutines.
Change-Id: I99c94c3dcb7663fa61571c9720163e41a5fc0e36
* fix: use tenant token for auth scopes
Change-Id: I83bb677e9a33e906e207679b2ba8d0364bc20fe3
* refactor: migrate common/client/im to FileIO and add localfileio tests
- runner resolveInputFlags: replace validate.SafeInputPath + vfs.ReadFile
with FileIO.Open + io.ReadAll
- SaveResponse: delegate to FileIO.Save + ResolvePath
- cmd/api, cmd/service: pass FileIO to ResponseOptions
- im: replace validate.SafeLocalFlagPath with RuntimeContext.ValidatePath,
migrate download/upload to FileIO.Save/Open/Stat
- Add path_test.go and atomicwrite_test.go for localfileio
- Add validate_media_test.go for im media flag validation
- Adapt test mocks to fileio.FileInfo interface
* fix: reject positional arguments in shortcuts with clear error
Shortcuts silently ignored positional arguments (e.g. `lark-cli docs
+search "hello"`), causing empty results. Add Args validator to all
declarative shortcuts so cobra prints usage and a clear error message
telling users to pass values via flags instead.
Change-Id: I7579f9c871138cf91dd5f5d8c1d51bda3f77a1db
* fix: address PR review comments
- Remove unused *Shortcut parameter from rejectPositionalArgs
- Show all positional args in error message instead of only the first
- Add test case for multiple positional arguments
Change-Id: Ifea92d09ddabcd35fbf2db98d9888d18af59b894
All draft-related shortcuts now support <img src="./local.png"> in --body,automatically resolving relative paths into cid: inline MIME parts. Only relative paths are supported; absolute paths are rejected. Previously only +draft-edit supported this; now extended to +draft-create, +send, +reply, +reply-all, and +forward.
- Add internal/client/api_errors.go with WrapDoAPIError and WrapJSONResponseParseError to classify JSON decode issues vs generic network errors
- Route cmd/api DoAPI errors and HandleResponse JSON parse errors through the new helpers
- Add regression tests in cmd/api and internal/client
Related: https://github.com/larksuite/cli/issues/215
* feat: add FileIO extension for file transfer abstraction
Introduce extension/fileio package with Provider/FileIO/File interfaces
and a global registry, following the same pattern as extension/credential.
- Add LocalFileIO default implementation with path validation and atomic writes
- Wire FileIOProvider into Factory and resolve at runtime via RuntimeContext.FileIO()
- Factory holds Provider (not resolved instance), deferring resolution to execution time
* feat: linux support custom data dir via environment variable
* feat(keychain): support custom log directory via LARKSUITE_CLI_LOG_DIR
* feat(security): validate env dir paths for security
Add validation for environment variable directory paths to ensure they are absolute and safe. This prevents potential security issues from malformed paths. Also add corresponding tests to verify the validation behavior.
* docs(validate): add function and test documentation comments
Add missing documentation comments for SafeEnvDirPath function and related test cases to improve code clarity and maintainability
* refactor(keychain): remove warning logs for invalid env vars
* feat(vc): add +recording shortcut for meeting_id to minute_token conversion
* fix(vc): address PR review feedback for +recording shortcut
* docs(vc): merge Recording and Minutes in resource diagram as they share minute_token
* docs(vc): simplify resource diagram to use Minutes only
* test(vc): add integration eval for +recording execute paths
* docs(vc): fix +recording description to include both input modes
* fix(vc): address review findings for +recording docs and code consistency
+update already calls normalizeDocsUpdateResult to surface board_tokens when
markdown contains mermaid/plantuml/whiteboard blocks. +create was missing the
same call, so callers could not know how many whiteboards were created or
retrieve their tokens. One-line fix: call normalizeDocsUpdateResult after
CallMCPTool in DocsCreate.Execute.
* docs: add v1.0.5 changelog
Change-Id: Ia2c5e8f3d3e5fb95b4509e2f5d62a1ee253cd679
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version to v1.0.5
Change-Id: I8d19ec44311f9bf0e700152beab1fd8d261c3f73
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: (MacOS) add fallback file-based master key storage
* refactor(keychain): improve master key file handling and corruption checks
- Replace temporary file approach with direct file creation
- Add explicit corruption checks for existing keys
- Ensure atomic operations and proper cleanup on failure
* docs(keychain): add comments to clarify constants and variables
Add descriptive comments to explain the purpose of timeout, crypto parameters, and test variables in the macOS keychain implementation.
* fix(keychain): use atomic write for master key initialization
* fix(keychain): add retry logic for reading master key file
Add retry mechanism when reading existing master key file to handle potential race conditions. Return early if read error occurs instead of waiting for all retries.
* refactor(keychain): simplify master key validation logic
Restructure the key validation flow to reduce redundant checks and improve readability. The corrupted key check is moved after the error handling block for better logical flow.
* refactor(keychain): replace os package with vfs for file operations
Use vfs package instead of os for file operations to improve testability and
abstract filesystem access. This change makes it easier to mock filesystem
operations in tests and provides a consistent interface for file handling.
* feat: add transport extension with interceptor pre/post hooks
Add extension/transport package following the same Provider pattern as
credential and fileio extensions. The Interceptor interface uses a
PreRoundTrip/post-closure design that guarantees built-in transport
decorators (SecurityHeader, SecurityPolicy, Retry) cannot be skipped,
overridden, or tampered with by extensions. The original request context
is restored after PreRoundTrip to prevent context tampering.
Change-Id: I2e51ff67a0e2d8d32944a0565c2a6781110f281f
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reset registry test globals more completely, tighten the overlay pollution regressions, and ensure tenant scope coverage tests rebuild a fresh isolated registry before asserting.
* fix(issue-labels): reduce mislabeling and handle missing labels
Make type classification more conservative to avoid incorrect labels, and avoid skipping entire issues when some managed labels are missing.
* test(issue-labels): add more real-world issue samples
Add labeled/unlabeled issue examples to cover question/bug/enhancement and domain inference.
* test(issue-labels): avoid duplicate issue samples
Keep one sample per source_url to reduce confusion and maintain stable regression coverage.
* fix(issue-labels): include missing-label-only items in JSON output
Keep stderr and JSON output consistent under --only-missing when desired labels are missing from the repo.
* feat: add strict mode identity filter, profile management and credential extension
Port changes from feat/strict-mode-identity-filter_3 branch:
- Add strict mode for identity filtering and configuration
- Add profile management commands (add/list/remove/rename/use)
- Add credential extension framework (registry, env provider)
- Add VFS abstraction layer
- Refactor factory default and client options
- Update shortcuts to use new credential and validation patterns
Change-Id: I8c104c6b147e1901d94aefcefe35a174932c742b
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: go mod tidy
Change-Id: I0f610ccea6bc874248e84c24770944a3071dcc57
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fix test failures from credential provider migration
- Remove unused TAT stub registrations in api and service tests
(CredentialProvider manages tokens, SDK no longer calls TAT endpoint)
- Update strict mode integration test: +chat-create now supports user
identity, so it should succeed under strict mode user
Change-Id: Iab51c2e12a97995e0b95dcd71df212d2d1f76570
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: migrate remaining os calls to internal/vfs
Replace direct os.Stat/Open/MkdirAll/OpenFile/Remove/ReadDir/UserHomeDir
with vfs equivalents in shortcuts/minutes, shortcuts/drive, and
internal/keychain. Add ReadDir to the vfs interface and OsFs implementation.
Change-Id: I8f97e5fb3e1731b4684d276644fcb10fae823067
* fix: resolve gofmt and goimports formatting issues
Change-Id: If61578631f5698f7ca2d9a946ca59753651463fb
* feat: add Flag.Input support for @file and stdin input sources
Add framework-level support for reading flag values from files (@path)
or stdin (-), solving the fundamental problem of passing complex text
(markdown, multi-line content) via CLI arguments where shell escaping
breaks content. Closes#239, fixes#163.
- Add File/Stdin constants and Input field to Flag struct
- Add resolveInputFlags() in runner pipeline (pre-Validate)
- Support @@ escape for literal @ prefix
- Guard against multiple stdin consumers
- Auto-append "(supports @file, - for stdin)" to help text
- Apply to: docs +create/+update --markdown, im +messages-send/+reply
--text/--markdown/--content, task +comment --content,
drive +add-comment --content
Change-Id: I305a326d972417542aeadd70f37b74ea456461ef
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fix pre-existing test failures in task, minutes, and registry
- task/minutes: remove unused tenant_access_token httpmock stubs
(TestFactory's testDefaultToken provides tokens directly, so the
HTTP stub was never consumed and failed verification)
- registry: fix hasEmbeddedData() to check for actual services instead
of just byte length (meta_data_default.json has empty services array)
Change-Id: Ic7b5fc7f9de09137a7254fe1ddf47d24ade40587
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: suppress nilerr lint for intentional nil returns
Both cases intentionally return nil on error for graceful degradation:
- profile list: show friendly message when config is not initialized
- service: skip scope check when token resolution fails
Change-Id: I7285c37277c9b0361a421ab00359244c2cd150b3
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- runner.go: fail fast when Input is used on non-string flags
- remote_test.go: rename hasEmbeddedData → hasEmbeddedServices
- profile/list.go: add omitempty to optional JSON fields
- service.go: surface context cancellation errors in scope check
Change-Id: I7072d41f8c711b4b37c542e32dfd8150f42b13c0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: tighten credential resolution and profile flows
Change-Id: I83f6d424540eab9b1708944b9b6e26e8477cc60d
* refactor: centralize identity hint resolution
Change-Id: I38d5f98160b92adb62dc929ae73697ae5b3d64f8
* fix: surface unverified extension identities
Change-Id: Ia86d9bd19add9010176339ec4cc89deb033f5b4f
* fix: honor runtime credential sources in config views
Change-Id: I40b2ffedc5c1db5e08e86b9472ea2b84fa02bb29
* fix: prefer runtime values in config show commands
Change-Id: I5663a53e147577f0f1f533f67d12bea504e6b839
* Revert "fix: prefer runtime values in config show commands"
This reverts commit 4f9db3a227.
* Revert "fix: honor runtime credential sources in config views"
This reverts commit b3bfd526c5.
* fix: harden profile flows and credential boundaries
Change-Id: Ica61cd2730a639f71516cb1b237a639cb6511f7a
* fix: optimize profile and config inspection for agents
Change-Id: I19c368102f19654952638180ab947788a6971563
* refactor: unify credential env contracts
Change-Id: I0ff2c0a650ea53589a0626333e8f6e628ef10a54
* docs: expand AGENTS guidance
Change-Id: I289027dfd364c92205012feef6f05037066c035b
* fix: resolve regression bugs found during PR #252 review
- im: fix double SafeInputPath in resolveLocalMedia → uploadImageToIM/
uploadFileToIM chain that rejected all local image/file uploads
- credential: stop writing plain-text warnings to stderr, preserving
JSON envelope contract for AI agent consumers
- profile add: reject duplicate app-id to prevent keychain credential
collisions across profiles
- profile rename: exclude self when checking name uniqueness so renaming
to own appId works correctly
- config: replace bare fmt.Errorf with output.Errorf in save-failure
paths (default_as, strict_mode ×2, profile add)
- factory: remove unused resolveDefaultAs method (lint)
Change-Id: I6aa0d064414016f367f1edb08dd0604adf7bf13d
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove flaky TestColdStart_UsesEmbedded (race in registry)
The test triggers a data race: resetInit() writes package globals while
a background goroutine from a previous test may still be reading them.
The embedded-data path is covered by other tests.
Change-Id: I7a0c3bf85a9fb337b9279c9053697f40a0c0a0d4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: type-strengthen Brand and DefaultAs across credential chain
Replace raw string fields with typed enums for compile-time safety:
- extension/credential: add Brand and Identity named types
- internal/core: AppConfig.DefaultAs and CliConfig.DefaultAs → Identity
- internal/credential: Account.DefaultAs and IdentityHint.DefaultAs → core.Identity
The full data flow is now typed end-to-end:
extcred.Brand → core.LarkBrand (named-type cast)
extcred.Identity → core.Identity (named-type cast)
No string intermediaries, no implicit conversions.
Change-Id: I715b3b3f033fcb624010f1af9619e3562740ef08
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix gofmt alignment in extension/credential/types.go
Change-Id: Ibfac0703a5a28f3c6ba4a47bf40696028d0f3b90
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove file/stdin input support from task comment content flag
Change-Id: If49704ca4612465a23bd30b755d6e72a35fc2349
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(cmdutil): remove dead code autoDetectIdentity
autoDetectIdentity() is only called from tests, never from production
code. Remove it along with its 3 test cases to reduce surface area
before the upcoming ctx propagation refactor.
Change-Id: I35a188860f17656f3e1fe9874f87f284985ae196
* refactor(cmdutil): add ctx parameter to resolveIdentityHint
Private method resolveIdentityHint now accepts context.Context and
passes it to CredentialProvider.ResolveIdentityHint instead of using
context.Background(). The caller (ResolveAs) still uses
context.Background() temporarily until its own signature is updated.
Change-Id: I14634a4e0dc1d657d56936ba61a7b7a206da8ac4
* refactor(cmdutil): add ctx parameter to ResolveStrictMode
ResolveStrictMode now accepts context.Context and passes it to
CredentialProvider.ResolveAccount instead of using context.Background().
Callers in cobra RunE pass cmd.Context(); callers outside RunE
(cmd/root.go startup, tests) use context.Background() explicitly.
Change-Id: I31be48e548ac5ac5640a65f3bfdde4a53ed1dc7e
* refactor(cmdutil): add ctx parameter to CheckStrictMode
CheckStrictMode now accepts context.Context and forwards it to
ResolveStrictMode. Callers pass cmd.Context() (cobra RunE) or
opts.Ctx (APIOptions/ServiceMethodOptions).
Change-Id: I47888519d4cae8c94054771c32aff075565a8cdc
* refactor(cmdutil): add ctx parameter to ResolveAs
ResolveAs now accepts context.Context as first parameter and forwards
it to ResolveStrictMode and resolveIdentityHint. This completes the
ctx propagation chain: all Factory methods that call
CredentialProvider now receive ctx from cobra cmd.Context().
No more context.Background() calls remain in factory.go for
credential provider operations.
Change-Id: I6d10b6350e3b149470660de3e7855614314e8b29
* test: fix gofmt in cmdutil factory tests
Change-Id: I4a87d5a815b959f14cc4371b73dee4aae106932f
* fix: remove file/stdin input support from im send/reply and drive comment
The Input (file/stdin) feature is not yet ready for these flags:
- im send/reply: --content, --text, --markdown
- drive add-comment: --content
Retained only in doc create/update where markdown from file is essential.
Change-Id: I582b6349528fccb639ad9edc84650cca3b68535c
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: liushiyao <liushiyao.1206@bytedance.com>
* fix(mail): restore CID validation and stale PartID lookup lost in revert (#199)
The revert of PR #81 (eda2b9c) also removed two independent bugfixes:
1. CID character validation in newInlinePart — reject spaces, tabs,
angle brackets, and parentheses to prevent malformed MIME output.
2. Stale PartID lookup in validateInlineCIDAfterApply and
validateOrphanedInlineCIDAfterApply — use findPrimaryBodyPart by
media type instead of findPart by PrimaryHTMLPartID, which can go
stale when ops restructure the MIME tree.
* test(mail): add tests for CID character validation and stale PartID lookup
- TestAddInlineRejectsInvalidCharactersInCID: verify spaces, tabs,
embedded angle brackets, and parentheses in CID are rejected.
- TestValidateInlineCIDAfterSetBody: verify inline CID validation
works correctly after set_body restructures the MIME tree (covers
the findPrimaryBodyPart fix for stale PartID).
* fix(mail): add CID character validation to replaceInline and strengthen test assertions
Address CR feedback:
1. Add the same CID character validation (spaces, tabs, angle brackets,
parentheses) to replaceInline, matching the check in newInlinePart.
Previously replace_inline could bypass the restriction.
2. Strengthen orphaned CID test assertion to check for specific
"orphaned cids" error message, not just non-nil error.
3. Add TestReplaceInlineRejectsInvalidCharactersInCID to cover the
new validation in replace_inline.
* ci: add issue labeler workflow
Add a manual GitHub Actions workflow and script to poll issues and apply type/domain labels.
* feat(issue-labels): refine heuristics and add docs
Improve domain detection and add safeguards to avoid overriding manual type triage by default. Refresh regression samples from real issues and document usage.
* ci(issue-labels): enable hourly scheduled labeling
Run hourly on schedule with write mode by default while keeping manual dispatch dry-run by default.
* ci(issue-labels): shorten lookback window to 6h
Reduce scheduled scan window while keeping overlap for missed runs.
* ci(issue-labels): opt into Node 24 actions runtime
Set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 and use Node 24 for the script runtime to avoid upcoming Node 20 deprecation warnings.
* ci(issue-labels): restore lookback input for manual runs
Allow workflow_dispatch to override lookback_hours while keeping hourly schedule fixed.
* ci(issue-labels): upgrade checkout/setup-node to v6
Use actions/checkout@v6 and actions/setup-node@v6 to align with Node 24 runtime and avoid Node 20 deprecation warnings.
* fix(ci): label only unlabeled issues via search api
* fix(ci): refine issue labeling heuristics from live issues
* fix(ci): address remaining issue label review comments
* fix(ci): fix issue label arg parsing regression
* docs(issue-labels): clarify one-shot unlabeled triage scope
* feat(drive): support multipart upload for files larger than 20MB
Previously, `drive +upload` rejected files exceeding 20MB with a
validation error. Now files > 20MB automatically use the three-step
chunked upload API (upload_prepare → upload_part × N → upload_finish),
removing the size ceiling for Drive uploads.
Tested with a 189MB file (48 blocks × 4MB) against a live Feishu tenant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(drive): add upload error-path tests to improve coverage
Cover small-file upload (upload_all) success + error paths and
multipart upload error paths (invalid prepare, part API error,
part invalid JSON, finish missing token, custom name flag).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: cli e2e test framework and demo
* feat: add cli-e2e-testcase-writer skill and task case
* feat: add cli e2e config and fix test resource prefix
All HTTP clients previously used http.DefaultTransport which silently respects
HTTP_PROXY/HTTPS_PROXY env vars, allowing credentials to transit through
untrusted proxies. This adds a proxy detection warning and an opt-out switch
(LARK_CLI_NO_PROXY=1) so security-sensitive users can disable proxy entirely.
- Redact proxy credentials in warning output (handles both scheme-prefixed and bare URL formats)
- Suppress warning when LARK_CLI_NO_PROXY is already set
- Use FallbackTransport singleton for nil-Base fallback paths to preserve connection pooling
- Emit proxy warning on both HTTP client and Lark SDK client paths
Change-Id: Ibed7d0470409c73fbd42bccac6673f9fc5e87a83
- Add --as user support to +chat-create
- Add UserScopes (im:chat:create_by_user) / BotScopes (im:chat:create)
- Update skill docs and reference files to reflect user/bot support
- Default identity remains bot (first element of AuthTypes)
Change-Id: I6be0a160567a0d87a92f176ae12297a11d06dcb1
* feat(auth): add response logging and centralize path constants
* refactor(auth): improve response logging and error handling
* fix(auth): ensure log cleanup runs only once per process
Add flag to track if cleanup has run and prevent duplicate executions
Add test to verify cleanup only runs once
* refactor(auth): simplify log writer and cleanup logic
* docs(auth): add comments to auth paths and logging functions
* style(auth): fix indentation in path constants
* docs(auth): add missing function comments across auth package
* docs(tests): add descriptive comments to auth test functions
* test(auth): rename test case and cleanup unused params
* fix(auth): handle file close error in auth response logging
* fix(auth): ensure log cleanup runs only once
* refactor(auth): replace custom log writer with standard logger
* feat(auth): add structured logging for keychain errors
* fix(auth): remove goroutine from auth log cleanup to prevent race condition
* fix(auth): remove goroutine from auth log cleanup to prevent race condition
* refactor(auth): move auth logging logic to keychain package
* docs(mail): add identity guidance to prefer user over bot for mail APIs
Add an identity selection section to the mail skill documentation,
guiding AI agents to default to --as user when operating on mailboxes.
Bot identity requires the app to have tenant-level mail scopes enabled
in the developer console, which most apps do not.
* docs(mail): clarify identity selection wording and bot scope limits
- Replace ambiguous "默认使用" with "策略上应优先显式使用" to
distinguish policy recommendation from CLI default (auto)
- Note that bot identity only supports read operations; all write
operations (send, reply, forward, draft edit) require user identity
- Rewrite decision rules by read/write classification