fix(cursor): resolve model from model_id and transcript fallbacks - #55
Conversation
Read Cursor's model_id when model is missing, extract model from agent transcripts when hooks only send Auto/default placeholders, and hide tool-scoped unknown-model labels in the VS Code hover. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔴 Autter review in progress — running security, correctness & dependency checks on this PR. Follow live step-by-step progress on the autter/review-gate check in the merge box. Merge is blocked until the gate completes; Autter approves automatically when the review comes back clean, and releases this hold with a neutral review when it finds non-blocking issues.
| } | ||
| } | ||
|
|
||
| hook_model.unwrap_or("unknown").to_string() |
There was a problem hiding this comment.
🟠 [ai] Placeholder hook model is retained when transcript fallback is unusable — Risk: 73/100
If the hook provides a placeholder model such as auto or unknown and transcript extraction fails or yields another placeholder, the final fallback returns the original hook value. Return "unknown" whenever the selected hook model is a placeholder and no usable transcript model was found.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Functions/symbols:
matching_workspace_root,cursor_file_path_from_tool_input,resolve_repo_cwd,parse,normalize_cursor_path,CursorPreset,BlameLensManager - Dependent files:
agent-support/vscode/src/extension.ts,crate::authorship::authorship_log_serialization::generate_session_id,crate::authorship::working_log::AgentId,crate::commands::checkpoint_agent::bash_tool::{self, Agent, ToolClass},crate::error::AutterError,std::collections::HashMap,std::path::PathBuf,super::parse
🛠 AI fix prompt (copy & paste into your coding agent)
Change the final fallback to return `unknown` when the resolved hook model is missing or a placeholder. Preserve real model names from the hook and transcript, but do not return placeholder values as the final model string.
Flagged by Autter security & observability checks.
🚦 Pre-merge checks ·
|
| Check | Status | Explanation |
|---|---|---|
| Removed observability | 1 potential issue(s) detected (max risk 42/100): src/streams/model_extraction.rs:15. | |
| Rate limiting not detected | 1 potential issue(s) detected (max risk 53/100): src/commands/checkpoint_agent/presets/cursor.rs:49. | |
| Batch size limit not detected | 1 potential issue(s) detected (max risk 56/100): src/commands/checkpoint_agent/presets/cursor.rs:559. | |
| Security group open to the internet | 1 potential issue(s) detected (max risk 64/100): src/commands/checkpoint_agent/presets/cursor.rs:188. | |
| Missing linked tracker issue | 1 potential issue(s) detected (max risk 61/100): agent-support/vscode/src/blame-lens-manager.ts:1297. | |
| Missing CODEOWNERS reviewer approval | 3 potential issue(s) detected (max risk 76/100): agent-support/vscode/src/blame-lens-manager.ts:1297, src/commands/checkpoint_agent/presets/cursor.rs:49, src/streams/model_extraction.rs:15. | |
| Source changes without matching tests | 5 potential issue(s) detected (max risk 73/100): src/commands/checkpoint_agent/presets/cursor.rs:11, src/streams/model_extraction.rs:15, agent-support/vscode/src/blame-lens-manager.ts:1297, src/commands/checkpoint_agent/presets/cursor.rs:49, src/streams/model_extraction.rs:374. | |
| Generic placeholder identifier in production logic | 1 potential issue(s) detected (max risk 43/100): src/streams/model_extraction.rs:387. | |
| Repetitive boilerplate (duplicated block) | 1 potential issue(s) detected (max risk 22/100): src/commands/checkpoint_agent/presets/cursor.rs:553. | |
| Overbroad try/catch swallowing all exceptions | 2 potential issue(s) detected (max risk 63/100): src/commands/checkpoint_agent/presets/cursor.rs:564, src/streams/model_extraction.rs:379. | |
| Public route touches private/PII data | 1 potential issue(s) detected (max risk 52/100): agent-support/vscode/src/blame-lens-manager.ts:1130. | |
| Code correctness issue | 2 finding(s) on changed lines. | |
| Simplifiable code | 1 finding(s) on changed lines. | |
| Complexity Guard | 4 finding(s) on changed lines. | |
| Bundle Size Monitor | 3 finding(s) on changed lines. | |
| Release Notes Curator | 1 finding(s) on changed lines. |
✅ Passed checks (154)
| Check | Status | Explanation |
|---|---|---|
| Too many files changed | ✅ Passed | Changed 3 file(s), within the limit of 50. |
| Too many lines changed | ✅ Passed | Changed 209 line(s), within the limit of 1000. |
| Too many unrelated chapters | ✅ Passed | 3 chapter(s) detected, within the limit of 6. |
| Generated files hiding real changes | ✅ Passed | Generated-file volume (0 lines) does not obscure the 209 hand-written line(s). |
| Missing PR context | ✅ Passed | PR context looks sufficient. |
| Mixed concerns (refactor + behavior change) | ✅ Passed | This PR is a behavior fix focused on model-resolution logic plus a small UI display adjustment; it does not show a pure refactor/no-op cleanup mixed with a separate behavior change. |
| Migration + app logic + UI combined in one PR | ✅ Passed | There are no database migration files in this PR, so it cannot combine migrations with application logic and UI changes. |
| Sensitive data in logs | ✅ Passed | No sensitive data in logs issues detected. |
| Log injection | ✅ Passed | No log injection issues detected. |
| Missing audit logging | ✅ Passed | No missing audit logging issues detected. |
| Silent exception swallowing | ✅ Passed | No silent exception swallowing issues detected. |
| Unhandled promise rejection | ✅ Passed | No unhandled promise rejection issues detected. |
| Circuit breaker not detected | ✅ Passed | No circuit breaker not detected issues detected. |
| Stack trace leakage | ✅ Passed | No stack trace leakage issues detected. |
| Multi-write without detected transaction | ✅ Passed | No multi-write without detected transaction issues detected. |
| Possible TOCTOU in critical path | ✅ Passed | No possible toctou in critical path issues detected. |
| Idempotency key not detected | ✅ Passed | No idempotency key not detected issues detected. |
| Possible non-atomic read-modify-write | ✅ Passed | No possible non-atomic read-modify-write issues detected. |
| Optimistic locking not detected | ✅ Passed | No optimistic locking not detected issues detected. |
| Rate limiting removed | ✅ Passed | No rate limiting removed issues detected. |
| Pagination not detected | ✅ Passed | No pagination not detected issues detected. |
| Publicly exposed storage | ✅ Passed | No publicly exposed storage issues detected. |
| Over-permissive IAM policy | ✅ Passed | No over-permissive iam policy issues detected. |
| Unencrypted storage at rest | ✅ Passed | No unencrypted storage at rest issues detected. |
| Infrastructure missing access logging | ✅ Passed | No infrastructure missing access logging issues detected. |
| Hardcoded secret in IaC | ✅ Passed | No hardcoded secret in iac issues detected. |
| Infrastructure misconfiguration | ✅ Passed | No infrastructure misconfiguration issues detected. |
| Deprecated Kubernetes API version | ✅ Passed | No deprecated kubernetes api version issues detected. |
| Compound IaC attack chain | ✅ Passed | No compound iac attack chain issues detected. |
| Prompt injection risk | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| LLM output used in a dangerous sink | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Sensitive data in prompt or system-prompt leakage | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Over-privileged LLM tool / excessive agency | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Missing validation on an LLM-driven decision | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Unbounded LLM usage (denial-of-wallet) | ✅ Passed | No LLM/AI-integration code touched by this diff. |
| Table exposed without row-level security | ✅ Passed | No row-level-security-related code touched by this diff. |
| Over-broad row-level security policy | ✅ Passed | No row-level-security-related code touched by this diff. |
| Code path that bypasses row-level security | ✅ Passed | No row-level-security-related code touched by this diff. |
| Privileged database credential reachable from the client | ✅ Passed | No row-level-security-related code touched by this diff. |
| Privileged query without row-level scoping | ✅ Passed | No row-level-security-related code touched by this diff. |
| Template-default gradient styling | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Interchangeable AI marketing copy | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Placeholder content shipped to users | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Emoji standing in for an icon system | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Call-to-action that goes nowhere | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Templated page composition | ✅ Passed | No added frontend pages or design-slop markers in this diff. |
| Merge-blocking marker left in the change | ✅ Passed | No pending-work markers added by this diff. |
| Known-defect marker shipped in code | ✅ Passed | No pending-work markers added by this diff. |
| Untracked TODO without an issue reference | ✅ Passed | No pending-work markers added by this diff. |
| Test disabled or left pending | ✅ Passed | No pending-work markers added by this diff. |
| PII in logs | ✅ Passed | No pii in logs issues detected. |
| PII or internals leaked in error response | ✅ Passed | No pii or internals leaked in error response issues detected. |
| PII stored without application-level encryption | ✅ Passed | No pii stored without application-level encryption issues detected. |
| User data stored without retention controls | ✅ Passed | No user data stored without retention controls issues detected. |
| PII sent to external / cross-border destination | ✅ Passed | No pii sent to external / cross-border destination issues detected. |
| Lockfile resolution / integrity tampered | ✅ Passed | No lockfile resolution / integrity tampered issues detected. |
| Dependency runs install-time lifecycle script | ✅ Passed | No dependency runs install-time lifecycle script issues detected. |
| Possible dependency-confusion attack | ✅ Passed | No possible dependency-confusion attack issues detected. |
| Lockfile resolves a dependency the manifest does not declare | ✅ Passed | No lockfile resolves a dependency the manifest does not declare issues detected. |
| Checked-in build artefact modified without source change | ✅ Passed | No checked-in build artefact modified without source change issues detected. |
| Dockerfile build-step is insecure | ✅ Passed | No dockerfile build-step is insecure issues detected. |
| External artefact pulled in without integrity pinning | ✅ Passed | No external artefact pulled in without integrity pinning issues detected. |
| Changed export, importer not updated | ✅ Passed | No changed export with an un-updated importer detected. |
| Missing security-team review on sensitive path | ✅ Passed | No missing security-team review on sensitive path issues detected. |
| Migration missing rollback / down step | ✅ Passed | No migration missing rollback / down step issues detected. |
| Frontend importing database client directly | ✅ Passed | No frontend importing database client directly issues detected. |
| Route handler bypassing service layer | ✅ Passed | No route handler bypassing service layer issues detected. |
| Backend service importing UI module | ✅ Passed | No backend service importing ui module issues detected. |
| Cross-context internals import | ✅ Passed | No cross-context internals import issues detected. |
| Workspace package rule violation | ✅ Passed | No workspace package rule violation issues detected. |
| Inconsistent logging pattern | ✅ Passed | No inconsistent logging pattern issues detected. |
| Inconsistent error handling | ✅ Passed | No inconsistent error handling issues detected. |
| Endpoint missing input validation | ✅ Passed | No endpoint missing input validation issues detected. |
| Multi-write without transaction wrapper | ✅ Passed | No multi-write without transaction wrapper issues detected. |
| New feature shipped without feature flag | ✅ Passed | No new feature shipped without feature flag issues detected. |
| Module placed in the wrong workspace package | ✅ Passed | No module placed in the wrong workspace package issues detected. |
| Direct env-var access bypasses config module | ✅ Passed | No direct env-var access bypasses config module issues detected. |
| Hallucinated import (package not installed) | ✅ Passed | No hallucinated import (package not installed) issues detected. |
| Nonexistent package (not found in registry) | ✅ Passed | No nonexistent package (not found in registry) issues detected. |
| Call to function that does not exist | ✅ Passed | No call to function that does not exist issues detected. |
| TODO / FIXME on critical path | ✅ Passed | No todo / fixme on critical path issues detected. |
| Comment contradicts or fabricates code behaviour | ✅ Passed | No comment contradicts or fabricates code behaviour issues detected. |
| Abstraction defined but never used | ✅ Passed | No abstraction defined but never used issues detected. |
| Code style differs from rest of codebase | ✅ Passed | No code style differs from rest of codebase issues detected. |
| Established pattern ignored | ✅ Passed | No established pattern ignored issues detected. |
| Unhandled edge case (null / empty / zero / boundary) | ✅ Passed | No unhandled edge case (null / empty / zero / boundary) issues detected. |
| Doc-copy code with insecure defaults | ✅ Passed | No doc-copy code with insecure defaults issues detected. |
| Dead code (defined but never referenced) | ✅ Passed | No dead code (defined but never referenced) issues detected. |
| Deprecated API call | ✅ Passed | No deprecated api call issues detected. |
| API pattern from wrong library version | ✅ Passed | No api pattern from wrong library version issues detected. |
| API endpoint removed | ✅ Passed | No api endpoint removed issues detected. |
| HTTP method changed (GET ↔ POST etc.) | ✅ Passed | No http method changed (get ↔ post etc.) issues detected. |
| New required field added to request | ✅ Passed | No new required field added to request issues detected. |
| Field removed from response schema | ✅ Passed | No field removed from response schema issues detected. |
| Response field type changed | ✅ Passed | No response field type changed issues detected. |
| HTTP status code changed | ✅ Passed | No http status code changed issues detected. |
| Auth requirement added / removed / changed | ✅ Passed | No auth requirement added / removed / changed issues detected. |
| Error response shape changed | ✅ Passed | No error response shape changed issues detected. |
| Pagination behaviour changed | ✅ Passed | No pagination behaviour changed issues detected. |
| Outbound webhook payload schema changed | ✅ Passed | No outbound webhook payload schema changed issues detected. |
| GraphQL field removed without deprecation | ✅ Passed | No graphql field removed without deprecation issues detected. |
| GraphQL enum value removed | ✅ Passed | No graphql enum value removed issues detected. |
| SQL injection | ✅ Passed | No sql injection issues detected. |
| Cross-site scripting (XSS) | ✅ Passed | No cross-site scripting (xss) issues detected. |
| Path traversal | ✅ Passed | No path traversal issues detected. |
| Command injection | ✅ Passed | No command injection issues detected. |
| Insecure deserialization | ✅ Passed | No insecure deserialization issues detected. |
| Weak cryptography | ✅ Passed | No weak cryptography issues detected. |
| Hardcoded secret | ✅ Passed | No hardcoded secret issues detected. |
| Insecure randomness for security material | ✅ Passed | No insecure randomness for security material issues detected. |
| Unsafe file upload | ✅ Passed | No unsafe file upload issues detected. |
| Missing input validation | ✅ Passed | No missing input validation issues detected. |
| Unsafe CORS configuration | ✅ Passed | No unsafe cors configuration issues detected. |
| Unsafe / open redirect | ✅ Passed | No unsafe / open redirect issues detected. |
| Missing CSRF protection | ✅ Passed | No missing csrf protection issues detected. |
| Unsafe cookie / session settings | ✅ Passed | No unsafe cookie / session settings issues detected. |
| Sensitive data exposure | ✅ Passed | No sensitive data exposure issues detected. |
| API key in source | ✅ Passed | No api key in source detected. |
| Access token in source | ✅ Passed | No access token in source detected. |
| Private key in source | ✅ Passed | No private key in source detected. |
| Database connection URL with embedded credentials | ✅ Passed | No database connection url with embedded credentials detected. |
| Cloud credential in source | ✅ Passed | No cloud credential in source detected. |
| Webhook signing secret in source | ✅ Passed | No webhook signing secret in source detected. |
| OAuth client secret in source | ✅ Passed | No oauth client secret in source detected. |
| JWT signing secret in source | ✅ Passed | No jwt signing secret in source detected. |
| Hardcoded password | ✅ Passed | No hardcoded password detected. |
| Auth middleware removed from route | ✅ Passed | No auth middleware removed from route issues detected. |
| Route protection changed (protected → public) | ✅ Passed | No route protection changed (protected → public) issues detected. |
| Permission / RBAC check removed | ✅ Passed | No permission / rbac check removed issues detected. |
| Required role weakened | ✅ Passed | No required role weakened issues detected. |
| Admin-only route exposed to lower privilege | ✅ Passed | No admin-only route exposed to lower privilege issues detected. |
| Token validation skipped in middleware chain | ✅ Passed | No token validation skipped in middleware chain issues detected. |
| JWT verification weakened or changed | ✅ Passed | No jwt verification weakened or changed issues detected. |
| Session expiration / TTL changed | ✅ Passed | No session expiration / ttl changed issues detected. |
| Password reset flow changed | ✅ Passed | No password reset flow changed issues detected. |
| OAuth callback / redirect handling changed | ✅ Passed | No oauth callback / redirect handling changed issues detected. |
| Webhook endpoint missing signature verification | ✅ Passed | No webhook endpoint missing signature verification issues detected. |
| Frontend performance issue | ✅ Passed | No additional explanation was reported. |
| Frontend security issue | ✅ Passed | No additional explanation was reported. |
| Frontend correctness issue | ✅ Passed | No additional explanation was reported. |
| Accessibility issue | ✅ Passed | No additional explanation was reported. |
| Frontend maintainability issue | ✅ Passed | No additional explanation was reported. |
| Runtime error risk | ✅ Passed | No additional explanation was reported. |
| Resource leak risk | ✅ Passed | No additional explanation was reported. |
| Data integrity risk | ✅ Passed | No additional explanation was reported. |
| Maintainability issue | ✅ Passed | No additional explanation was reported. |
| Co-change coupling | ✅ Passed | No additional explanation was reported. |
| Redundant alias / duplicate import | ✅ Passed | No additional explanation was reported. |
| Redundant type construct | ✅ Passed | No additional explanation was reported. |
| Unnecessary type assertion | ✅ Passed | No additional explanation was reported. |
| Module smell | ✅ Passed | No additional explanation was reported. |
| Excessive complexity | ✅ Passed | No additional explanation was reported. |
| Dead export (no callers) | ✅ Passed | No additional explanation was reported. |
| Code duplication / DRY violation | ✅ Passed | No additional explanation was reported. |
This comment is updated automatically whenever Autter reviews a new PR revision.
🧭 PR hygiene & process suggestionsAutter has 1 suggestion(s) about the shape of this PR (size, scope, reviewability). These are process guidance — not code defects — so they are consolidated here instead of posted as inline comments on individual files. 🟠 Missing test coverage for top-level Cursor
|
There was a problem hiding this comment.
Autter completed PR review for #55: 5 finding(s) remain below the merge-blocking bar, so this review stays neutral rather than approving. (Also detected: 22 finding(s) dismissed as likely false positives by verification.) See the findings below; the task checklist follows as the review's final comment.
🤖 Release Notes CuratorImpact: patch — fixes Cursor model attribution by resolving documented model identifiers and usable transcript data. Changelog: Fixed Cursor attribution to display the actual model more reliably, including when hooks provide a model ID or transcript-derived model. Custom agent · runs after review · configured in Autter |
|
|
||
| /// Resolve the model for a Cursor hook: prefer hook `model`/`model_id`, then transcript. | ||
| fn resolve_cursor_model(data: &serde_json::Value, transcript_path: Option<&str>) -> String { | ||
| let hook_model = parse::optional_str_multi(data, &["model", "model_id"]); |
There was a problem hiding this comment.
🔴 [ai] Fall back to model_id when model is only a placeholder — Risk: 82/100
resolve_cursor_model obtains hook_model with optional_str_multi(["model", "model_id"]), which always returns the first present string. For a documented hook payload containing model: "auto" and a concrete model_id, the placeholder causes the first branch to be skipped, but the helper never considers model_id; if the transcript is unavailable or has not yet been written (normal for preToolUse), it returns auto. CursorPreset::parse then copies that value into AgentId.model for every generated checkpoint event, so the production orchestrator persists the placeholder despite receiving the actual model identifier. This breaks the new fallback contract precisely on the pre-tool call path that needs it.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/commands/checkpoint_agent/orchestrator.rs,src/commands/checkpoint_agent/presets/cursor.rs
🛠 AI fix prompt (copy & paste into your coding agent)
Read `model` and `model_id` independently. Return a non-placeholder `model` first, then a non-placeholder `model_id`, then try the transcript, and only then return the available placeholder or `unknown`. Add coverage for `{model: "auto", model_id: "claude-opus-4-7"}` without a transcript.
Flagged by Autter security & observability checks.
|
|
||
| /// Resolve the model for a Cursor hook: prefer hook `model`/`model_id`, then transcript. | ||
| fn resolve_cursor_model(data: &serde_json::Value, transcript_path: Option<&str>) -> String { | ||
| let hook_model = parse::optional_str_multi(data, &["model", "model_id"]); |
There was a problem hiding this comment.
🟠 [ai] Fall back to model_id when model is a placeholder — Risk: 78/100
The resolver uses optional_str_multi with model before model_id, so a payload containing model: "auto" and a concrete model_id selects only auto. Because it is a placeholder, the code then tries the transcript; if the transcript is unavailable, incomplete at preToolUse time, or has no model field, line 208 returns auto rather than the supplied concrete model_id. This is reachable for the documented hook shape that carries both fields, and it defeats the PR's stated model_id fallback. The resulting AgentId is passed into both edit checkpoints and normalization does not treat auto as a placeholder, so it is persisted as the model instead of the known actual model.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/commands/checkpoint_agent/presets/cursor.rs,src/commands/checkpoint_agent/orchestrator.rs,src/authorship/working_log.rs
🛠 AI fix prompt (copy & paste into your coding agent)
Resolve `model` and `model_id` independently. Use a non-placeholder `model`; otherwise use a non-placeholder `model_id` before attempting transcript extraction, and only retain a placeholder when neither source produces a real model. Add coverage for `{ model: "auto", model_id: "claude-opus-4-7" }` with no usable transcript.
Flagged by Autter security & observability checks.
|
|
||
| /// Resolve the model for a Cursor hook: prefer hook `model`/`model_id`, then transcript. | ||
| fn resolve_cursor_model(data: &serde_json::Value, transcript_path: Option<&str>) -> String { | ||
| let hook_model = parse::optional_str_multi(data, &["model", "model_id"]); |
There was a problem hiding this comment.
🟠 [ai] Do not let a placeholder model hide a usable model_id — Risk: 68/100
When Cursor emits both model: "auto" (or another placeholder) and a concrete model_id, optional_str_multi returns the first present field at this line. The placeholder is rejected at lines 191-195, but model_id is never considered. If the transcript cannot yet be opened or is incomplete, the extraction failure is deliberately ignored at lines 197-205 and the function returns auto; checkpoint normalization then replaces it with cursor/unknown-model before the request is persisted. This loses the valid model identity precisely on the transient transcript-read failure path, producing a permanently incorrect attribution/analytics record despite the hook having supplied the needed value.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/commands/checkpoint_agent/presets/cursor.rs,src/commands/checkpoint_agent/orchestrator.rs,src/authorship/working_log.rs
🛠 AI fix prompt (copy & paste into your coding agent)
Resolve `model` and `model_id` independently. Prefer a non-placeholder `model`, then a non-placeholder `model_id`, then a non-placeholder transcript value, and only then return a placeholder/unknown fallback. Add coverage for `model: auto` plus a concrete `model_id` when the transcript is missing or unreadable.
Flagged by Autter security & observability checks.
Read model and model_id independently so preToolUse hooks with model: auto and a concrete model_id keep the real model when no transcript is available. Return unknown instead of persisting placeholder values when no usable model source is found. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔴 Autter review in progress — running security, correctness & dependency checks on this PR. Follow live step-by-step progress on the autter/review-gate check in the merge box. Merge is blocked until the gate completes; Autter approves automatically when the review comes back clean, and releases this hold with a neutral review when it finds non-blocking issues.
There was a problem hiding this comment.
Autter completed PR review for #55: 4 finding(s) remain below the merge-blocking bar, so this review stays neutral rather than approving. (Also detected: 25 finding(s) dismissed as likely false positives by verification.) See the findings below; the task checklist follows as the review's final comment.
🤖 Release Notes Curator
Custom agent · runs after review · configured in Autter |
|
looks good sorted |
| .and_then(|v| v.as_str()) | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())); | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())) | ||
| .or_else(|| json.get("model_id").and_then(|v| v.as_str())); |
There was a problem hiding this comment.
🟠 [ai] Cursor transcript fallback lets a placeholder model hide a concrete model_id — Risk: 78/100
resolve_cursor_model delegates placeholder hook inputs to extract_model and only accepts a non-placeholder return. The shared JSONL extractor selects message.model first, then top-level model, and only then model_id. Consequently a valid Cursor transcript record containing model: "auto" (or message.model: "auto") alongside model_id: "claude-opus-4-7" returns auto; the resolver rejects it and persists unknown, never considering the concrete ID. This breaks the checkpoint orchestrator's new Cursor model-resolution contract for pre/post hook events that must rely on transcript fallback, losing the model identity in the AgentId sent to downstream checkpoint normalization and authorship logging.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/commands/checkpoint_agent/orchestrator.rs,src/commands/checkpoint_agent/presets/cursor.rs,src/streams/model_extraction.rs
🛠 AI fix prompt (copy & paste into your coding agent)
When extracting a CursorJsonl model, evaluate `model` and `model_id` independently and prefer a non-placeholder value. Either make `extract_model` format-aware or have `resolve_cursor_model` inspect Cursor transcript records with Cursor placeholder rules, so `auto`/`default`/`unknown` cannot mask a concrete `model_id`. Add coverage for a transcript-only fallback record with `model: auto` plus `model_id: claude-opus-4-7`.
Flagged by Autter security & observability checks.
| .and_then(|m| m.get("model")) | ||
| .and_then(|v| v.as_str()) | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())); | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())) |
There was a problem hiding this comment.
🟠 [ai] Do not let a placeholder transcript model hide its concrete model_id — Risk: 78/100
The Cursor resolver invokes this generic extractor only after both hook fields are unusable. For a Cursor transcript record containing model: "auto" and model_id: "claude-opus-4-7", this or_else chain selects the present placeholder at line 107, never evaluates model_id at line 108, and returns auto. resolve_cursor_model then rejects that placeholder (cursor.rs:209) and returns unknown (cursor.rs:215), despite the transcript carrying a concrete model identity. This is reachable on postToolUse when Cursor's hook reports Auto/default and the transcript is the fallback source, so persisted attribution is incorrectly normalized to cursor/unknown-model rather than the actual model.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/commands/checkpoint_agent/presets/cursor.rs,src/streams/model_extraction.rs,src/authorship/working_log.rs
🛠 AI fix prompt (copy & paste into your coding agent)
When extracting a Cursor JSONL record, resolve `model` and `model_id` independently: use a non-placeholder `model`; otherwise use a non-placeholder `model_id`. Keep the existing precedence for non-Cursor formats or make placeholder filtering format-aware. Add coverage for a Cursor transcript line with `model: "auto"` and a concrete `model_id`.
Flagged by Autter security & observability checks.
| .and_then(|m| m.get("model")) | ||
| .and_then(|v| v.as_str()) | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())); | ||
| .or_else(|| json.get("model").and_then(|v| v.as_str())) |
There was a problem hiding this comment.
🟠 [ai] Do not let a placeholder transcript model mask model_id — Risk: 74/100
When the hook only supplies a placeholder, resolve_cursor_model intentionally falls back to extract_model; however, JSONL extraction chooses a present top-level model before model_id without applying the placeholder check. A Cursor record containing model: "auto" and model_id: "claude-opus-4-7" therefore returns auto; the resolver rejects it, silently treats transcript extraction as unsuccessful, and returns unknown. The orchestrator then normalizes that value to cursor/unknown-model before the checkpoint is persisted. Thus a transient/pre-tool hook with no usable hook model loses the real transcript model rather than producing an error or retaining the available identity.
⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:
- Dependent files:
src/streams/model_extraction.rs,src/commands/checkpoint_agent/presets/cursor.rs,src/commands/checkpoint_agent/orchestrator.rs,src/authorship/working_log.rs
🛠 AI fix prompt (copy & paste into your coding agent)
Make Cursor JSONL extraction resolve `model` and `model_id` independently, preferring a non-placeholder `model`, then a non-placeholder `model_id`. Ensure the resolver can still fall back to `unknown` only when neither transcript field is usable, and add coverage for a Cursor JSONL record with `model: auto` plus a concrete `model_id`.
Flagged by Autter security & observability checks.
Autter task list
Generated from PR diff, blast radius, and context. Issues found
Also detected but not listed above: 25 finding(s) dismissed as likely false positives by verification — see the Autter review dashboard for their verdicts. 🛠 Fix optionsCheck one option and Autter will start a fix run for the unresolved issues above.
Checking a box triggers the fix run immediately — Autter comments back with the issues being fixed and the branch created for each. |
Summary
model_idwhenmodelis missing or a placeholder (auto,default,unknown)transcript_pathJSONL when the hook payload doesn't carry a real model namemodel_extractionsupportsCursorJsonland checksmodel_idon transcript linescursor/unknown-model(and other*/unknown-modelfallbacks) instead of showing them literallyContext
Fixes
cursor/unknown-modelshowing in attribution UI when Cursor hooks omitmodelbut includemodel_id, or when Auto routing sends placeholder values.Test plan
cargo test presets::cursor::tests --lib(18 passed)cargo test model_extraction --lib(20 passed)claude-opus-4-7) instead ofcursor/unknown-modelMade with Cursor
Summary
Summary generated by Autter.
Improve Cursor model attribution by resolving concrete model identifiers from hook payloads and Cursor transcripts, rather than persisting placeholder values such as
autoor<synthetic>. The VS Code blame UI also normalizes model display so attributed lines show a useful model name when available.Changes
CursorPresetmodel resolution to:modelvalue from Cursor hook data.model_idwhenmodelis absent or a placeholder.unknownrather than retaining an unusable placeholder when no concrete model can be resolved.modelandmodel_idfields in addition to existing nested message-model and session model-change formats.model_idfallback, transcript fallback behavior, placeholder handling, and workspace/path normalization cases.Acceptance Criteria
preToolUseandpostToolUsecheckpoints persist a concreteAgentId.modelwhen the hook suppliesmodel_id, including whenmodelisauto.modelormodel_idin a Cursor JSONL transcript is detected by the shared stream model extractor.auto,unknown, and<synthetic>) do not override a concrete hook or transcript model.unknownrather than persisting a placeholder.autter blame --jsoncontract.Test Plan
task fmt.task lint.task test.model: "auto"andmodel_id: "claude-opus-4-7"with no readable transcript; verify generated checkpoint events useclaude-opus-4-7.modelormodel_id; verify stream extraction returns that concrete model.Rollback Plan
src/commands/checkpoint_agent/presets/cursor.rsandsrc/streams/model_extraction.rsto restore the prior Cursor hook and JSONL model-resolution behavior.agent-support/vscode/src/blame-lens-manager.tschange if the extension renders unexpected model labels or hover content.Related Issues
No linked issue was identified.
Written for commit cb4d056. Summary will update on new commits.