Audit trail for notes posted to PSA systems. Tracks session ID,
ticket ID, note type, content, status (success/failed), and any
status changes made alongside the note post.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PSACache class with get/set/invalidate/clear operations and TTL expiry.
Board statuses are cached for 1 hour in the provider.
Cache is cleared when a PSA connection is re-tested.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three new endpoints under /integrations/psa:
- GET /tickets/search: search CW tickets with query, board_id, status_id filters
- GET /tickets/{ticket_id}: fetch a single ticket by ID
- GET /tickets/{ticket_id}/statuses: get available statuses for a ticket's board
Add PSATicketSearchResult and PSATicketStatusItem schemas.
All endpoints require engineer_or_admin auth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement all remaining NotImplementedError stubs in ConnectWiseProvider:
- search_tickets: query by summary with board_id, status_id, include_closed filters
- get_ticket_statuses: fetch statuses for a service board
- list_companies: list companies with optional status filter
- get_company: fetch a single company by ID
- get_ticket_configurations: fetch configs attached to a ticket
- Extract shared _map_ticket helper to reduce duplication
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PATCH /sessions/{id}/ticket-link validates ticket exists in ConnectWise
before linking, supports unlinking by sending null, and returns ticket
details on success.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add columns to link sessions to PSA tickets and connections. Includes
migration 059, model relationship, and response schema fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace NotImplementedError stub with real implementation that fetches
a ticket by ID via CW REST API and maps it to PSATicket.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Five endpoints under /integrations/psa/connections:
- GET (any auth user), POST/PUT/DELETE/test (owner+ only)
- Create tests CW connection before saving; update re-tests on cred change
- Credentials decrypted only for masked hints in responses
- Three CI-safe tests: empty GET, engineer 403, delete 404
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PsaConnectionCreate, PsaConnectionUpdate, PsaConnectionResponse,
and PsaConnectionTestResponse — registered in schemas __init__.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ConnectWiseProvider implements PSAProvider with test_connection() that
calls GET /system/info to verify credentials and connectivity. All other
methods raise NotImplementedError with slice references for future work.
Provider registry (get_provider_for_account) looks up the account's
PsaConnection, decrypts stored credentials, and instantiates the
correct provider. Currently supports connectwise only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements ConnectWiseClient in services/psa/connectwise/client.py with:
- API key auth (Base64 companyId+publicKey:privateKey) + clientId header
- Accept header pinned to CW API version 2025.16
- Dynamic base URL resolution via /login/companyinfo (cloud vs on-premise)
- SSRF prevention: validates against known CW domains, rejects private IPs
- Retry with exponential backoff for timeouts and 5xx errors
- 429 rate limit handling with Retry-After header respect
- Error mapping: 401/403/404/429/5xx to typed PSA exceptions
- Paginated GET with while-loop pattern (max 1000 per page)
- JSON Patch array format for PATCH requests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5-slice plan covering foundation (abstraction layer, encryption,
connection CRUD, frontend), ticket linking, ticket search, update
ticket modal, and member mapping. Slice 1 has full task-level detail;
slices 2-5 are outlined for iterative planning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive design for ConnectWise PSA integration covering
credential management, service ticket integration, session-to-ticket
notes, and callback webhooks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete Script Generator feature including:
Backend:
- ScriptCategory, ScriptTemplate, ScriptGeneration models
- ScriptTemplateEngine with substitution, filters, sanitization
- CRUD + share API endpoints with permission checks
- Integration tests for permissions and sharing
- Migration 057 with AD User Management seed templates
Frontend — Script Library:
- Browse templates with category tabs and search
- Configure pane with parameter form and script generation
- Script preview with live substitution and copy/download
- scriptGeneratorStore Zustand store
Frontend — Template Editor:
- Full CRUD form with metadata, script body (Monaco Editor), parameters
- ParameterSchemaBuilder with visual builder + JSON toggle
- ScriptManagePage with routing and nav link
Frontend — Parameter Detector:
- Client-side PowerShell parameter detection engine
- Detects script-level param() blocks and variable assignments
- Type inference from PS type annotations and value patterns
- ParameterDetectorStepper one-by-one review UI with accept/skip
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Landing page at /landing with full marketing content: hero, features,
pricing, testimonials, and beta email signup form. Beta signups email
beta@resolutionflow.com via new public endpoint. Unauthenticated users
redirect to landing instead of login. Also raises KB Accelerator node
limit from 50 to 100 to accommodate dense troubleshooting articles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The commit endpoint returns {message, validation_errors} when
validation fails, but the error handler was passing the whole object
to toast.error(), crashing React with "Objects are not valid as a
React child".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AI-generated trees can have circular next_node_id references (e.g.,
node A → B → A). The parent mapping now checks for cycles before
assigning parent_node_id, preventing FK deadlocks during insert.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nodes with parent_node_id references were inserted in a single batch,
causing FK violations when children were inserted before their parents.
Now inserts roots first, flushes, then children in subsequent passes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moved md from Phase 2 extensions to allowed formats, added extraction
handler (reuses txt handler), and updated plan_limits defaults to
include md for all plans.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The uploader should know their content type — auto-detection adds
complexity and currently just defaults to troubleshooting anyway.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
_build_flow_context() was reading node.get('content') which was only
present on old KB-imported steps. Now falls back through title →
question → description → content → label so all node types (decision,
action, solution, procedural step) show correctly in the copilot context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Steps built by _build_procedural_tree() were stored under the "content"
key but StepDetail.tsx reads "description". Renamed the key so step
text and commands display correctly during session execution.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _build_procedural_tree now maps AI node types (step, action, warning) to
valid procedural types (procedure_step, section_header, procedure_end)
- Generates 'title' field from content text — fixes "Unnamed step" in editor
- Auto-appends procedure_end step if AI didn't generate one
- Adds procedural validation gate at commit endpoint (same as troubleshooting)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RouteError already handled stale chunk errors, but the Sentry ErrorBoundary
didn't — so errors from React lazy() imports hit the generic error UI.
Now auto-reloads with loop prevention (10s cooldown via sessionStorage).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The StaggerList wrapper creates per-item stacking contexts via CSS animation,
causing the absolute-positioned close popover to render behind sibling cards.
Replace StaggerList with inline stagger-item rendering so we can apply z-50
directly to the stagger-item wrapper when its popover is open.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase max_tokens from 8192 to 16384 to prevent truncation on long articles
- Add _try_repair_json() that fixes trailing commas and attempts to close
unclosed brackets/braces from truncated AI responses
- Log full raw response (first 2000 chars) on parse failure for debugging
- Set status to 'failed' with user-friendly error message instead of leaving
imports stuck in 'processing' state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TreeNavigationPage: initialize currentNodeId from tree_structure.id
instead of hardcoded 'root' — fixes "Invalid tree structure" for trees
with non-'root' root IDs (KB Accelerator, AI Flow Builder)
- SessionHistoryPage: add relative z-10 to session card when close
popover is open so it renders above sibling cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _repair_tree() post-build pass that fixes structural issues caused by
placed-set race conditions: demotes decisions with <2 children to actions,
hoists orphaned children as siblings, converts dead-end actions to solutions
- Add validation gate at commit endpoint — rejects trees that fail validation
with a 422 and descriptive error messages instead of saving invalid trees
- Update test fixtures to create valid multi-node tree structures
- Frontend: initialize currentNodeId from tree's actual root ID instead of
hardcoded 'root', fixing "Invalid tree structure" for KB-generated trees
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Decision nodes with fewer than 2 buildable child targets now get
demoted to action nodes instead of creating invalid tree structures.
Also adds id fields to option objects (required by tree editor).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ability to close active sessions directly from the Session History
page via an inline popover with outcome selection and optional notes.
Adds two new outcomes: cancelled and resolved_externally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix _build_troubleshooting_tree() to handle deep nesting, warning nodes,
and DAG deduplication (placed set prevents duplicate IDs)
- Fix step_sync VARCHAR(255) overflow on publish by truncating title
- Add "Approve All" button to KB review screen
- Add delete button (hover-reveal) to flow canvas nodes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test error was firing on every production page load — no longer needed
now that Sentry is confirmed working.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: flexible intake — deferred variables + prepared sessions
Remove blocking intake form modal. Variables are now filled inline during
flow execution or pre-filled via prepared sessions. Adds PATCH /sessions/{id}/variables
endpoint, POST /sessions/prepare for session pre-staging, inline variable prompts
in StepDetail, editable Session Variables panel, and "Prepared for You" dashboard section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pass treeData directly to startSession to avoid stale state
setTree(treeData) hasn't committed when startSession runs immediately
after, so tree is still null and getStepsFromTree returns []. This
caused the step detail area to render empty on new session start.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: wire PrepareSessionModal entry point in Flow Library
Add "Prepare session" button (clipboard icon) to grid, list, and table
views for procedural/maintenance flows. Clicking fetches tree intake
fields and account members, then opens PrepareSessionModal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent race conditions in token operations and auth flows
Backend:
- Refresh token rotation: use atomic UPDATE...WHERE revoked_at IS NULL
to prevent concurrent refresh requests from both succeeding
- Account invite codes: SELECT FOR UPDATE to prevent double-spend
- Platform invite codes: SELECT FOR UPDATE to prevent double-spend
- Password reset tokens: SELECT FOR UPDATE to prevent double-use
- Email verification tokens: SELECT FOR UPDATE to prevent double-use
Frontend:
- Token refresh subscriber arrays: swap before iterating so a throwing
callback doesn't leave the queue in a dirty state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: atomic counters, plan limit re-check, and double-submit guard
Backend:
- Tree usage_count: use SQL-level UPDATE (Tree.usage_count + 1) instead
of Python-level increment to prevent lost updates under concurrency
- Tag usage_count: same SQL-level atomic increment/decrement in both
create_tree and update_tree (delete_tree already used this pattern)
- Plan tree limit: re-check count after db.flush() to close the TOCTOU
window where two concurrent creates could both pass the pre-check
Frontend:
- TreeEditorPage: add isSaving early-return guard inside handleSaveDraft
and handlePublish callbacks so Ctrl+S can't bypass the button disabled
prop and fire duplicate save requests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent stale API responses from overwriting newer data
- SessionHistoryPage: move loadSessions into effect with cancelled flag
so rapid filter/tab changes discard outdated responses
- TreeLibraryPage: add request ID ref to loadTrees so stale responses
from previous filter selections are discarded
- QuickStartPage: add request ID ref to debounced search so out-of-order
responses don't overwrite newer search results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add flexible intake design — deferred variables + prepared sessions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>