Commit graph

3 commits

Author SHA1 Message Date
jmcqueen
034e657fcb feat(close): programmatic SS-ticket closure for the CC agent
Extends the app so a Contact Center AI agent can close SS tickets
end-to-end (customer-confirmed fix, cancellation, duplicate) without a
human touch — the natural next step after ticket creation landed.

The SS Resolved (161) workflow validator requires four fields on the
ticket at execution time (components + 3 CMDB customfields for Business
Service / System / Cause). The transition screen itself only exposes
resolution, so the closer has to PUT these into the ticket first and
*then* fire the transition. This change wires up the whole flow with
sensible per-subType defaults, so the AI can close a typical ticket
with just { subType, comment }.

Design (see Forgejo #10 for the full write-up):

- Generalized services/jira/storesCache into a reusable factory
  createAssetsObjectCache. Same in-memory Map + on-disk JSON snapshot +
  startAt/maxResults pagination + personal-PAT auth. Store cache becomes
  one instance; three new caches join it for the CMDB validator fields
  (businessServices=100, systems=103, causes=107 — all in schema 68, so
  the same permission workaround from Forgejo #8 keeps working).

- Boot-time init warms all four caches in parallel; a new
  services/jira/caches.js barrel exposes them for lifecycle (init) and
  by-name lookup (admin routes). Full sync takes ~5s in the tenant
  (2661 stores dominate — the three CMDB caches together hold ~653
  entries and finish in ~1s).

- Admin surface rebuilt around a plural /admin/caches path:
    GET  /admin/caches                    — status of all four
    GET  /admin/caches/:name/status       — one cache
    POST /admin/caches/:name/refresh      — force resync of one
    POST /admin/caches/refreshAll         — parallel refresh
  Old /admin/storesCache/{status,refresh} are kept as aliases.

- Config renamed: config.storesCache -> config.caches (dir instead of
  path; filenames auto-derived per cache). Env vars renamed to
  CACHES_* (dir/refresh-hours/stale-hours/page-size/max-pages).
  .env.example updated.

- ssCloseDefaults.js maps every supported SS subType to a
  {component, businessService, system, cause} tuple. Values verified
  against the live caches so they resolve at runtime. Falls back to a
  safe __default__ tuple (Help Desk / Store Technology / "I can't find
  my option - Misc" / Unknown) for un-mapped subTypes — those catch-all
  values are the tenant's designed "I don't know" escape valves.

- issues.js closeTicket completely reworked:
    * setSSValidatorFields helper: reads current ticket state, resolves
      each of the four fields via caller > existing-on-ticket > default,
      PUTs them using the Cloud CMDB shape [{id: "<ws>:<objectId>"}]
      (same fix as Forgejo #8 for Store Number).
    * Only writes fields that need writing; preserves human triage by
      default (preserveExistingClassification=true).
    * Fixes Forgejo #9: only sends the resolution field when the
      chosen transition targets statusCategory=done. Non-done
      transitions (e.g. "Waiting for customer") no longer 400 on
      "Field 'resolution' cannot be set."

- closeHelpers.js adds three CC-agent intent wrappers:
    confirmFixed(key, {subType, ...})       → resolution=Done
    customerCancelled(key, {subType, ...})  → resolution=Won't Do
    markDuplicate(key, {primaryKey, ...})   → resolution=Duplicate +
                                              formal Duplicate issueLink
                                              to the primary
  Each posts a standardized internal audit-trail comment ("Closed via
  WxCC AI agent: …") that documents the automated action for the
  humans who inherit the ticket.

- New routes: POST /ticket/:key/{confirmFixed,customerCancelled,
  duplicate} plus an expanded body on the existing /close route.

Verified end-to-end: POST /ticket/SS-20948/confirmFixed with just
subType=Report a Technology issue transitioned the ticket to Resolved
with all four validator fields populated from defaults (Help Desk /
Store Technology / I can't find my option - Misc / Unknown), plus
resolution=Done. Direct Jira REST GET confirms every field persisted.

Closes Forgejo #9. Refs Forgejo #10.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 11:20:41 -04:00
jmcqueen
fa06538aa4 Workaround #1: cache Assets store lookups via personal PAT sync
The service account is silently filtered out of Object Type 109 (Store
Address / Hierarchy) despite having schema-level read on schema 68, so
every AQL against the store type returns total=0. Until that permission
is granted, resolve store numbers from a local cache populated by a
personal PAT (different auth path, different account, has the role).

- new: src/services/jira/assetsSyncClient.js — Basic-auth axios against
  api.atlassian.com/jsm/assets/workspace/{ws}/v1, credentials sourced
  from ASSETS_SYNC_EMAIL / ASSETS_SYNC_TOKEN (loaded from Keychain by
  bin/load-assets-sync-secret.sh so the PAT never touches .env)
- new: src/services/jira/storesCache.js — in-memory Map + on-disk JSON
  at data/stores.json (gitignored), atomic write, paginated full sync
  via AQL (objectTypeId=N), boot-time load + background refresh if
  stale, periodic setInterval every STORES_CACHE_REFRESH_HOURS
- new: bin/load-assets-sync-secret.sh — Keychain -> env var wrapper
  (security find-generic-password -s jira-assets-sync -a <email>)
- change: resolveStoreAssetReference now tries cache -> live PAT -> the
  existing service-account AQL, in that order; the fallback path is
  preserved so this cleanly deactivates once the permission on #1 is
  fixed. Error message names all three routes and points at the refresh
  endpoint.
- new admin routes: GET /api/wxccai/admin/storesCache/status,
  POST /api/wxccai/admin/storesCache/refresh
- app.js kicks off storesCache.init() after listen()
- config: STORES_CACHE_ENABLED / _PATH / _REFRESH_HOURS /
  _STALE_AFTER_HOURS / _PAGE_SIZE / _MAX_PAGES, plus intFromEnv /
  boolFromEnv helpers
- .gitignore adds data/; .env.example documents the new vars; README
  adds an "Admin" endpoints section and a "Stores cache" setup guide

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 10:18:28 -04:00
jmcqueen
1070967870 Initial commit: Webex CC + Jira + xAI service
Core capabilities:
- Jira ticket lifecycle: status, update, comment, transitions, close
- JSM Store Support request creation with Assets object resolution
- Assets AQL diagnostic probe endpoint with schema/type introspection
- Webex transcript ingestion (audio + JSON + human-readable) with
  restricted-visibility summary comments
- Grok-powered single-ticket and open-tickets-by-reporter summaries

Repo hygiene:
- .gitignore covering .env, node_modules, logs, IDE dirs
- .env.example documenting every env var
- discover-ss-*.js scripts refactored to read credentials from .env
- README covering setup, endpoints, and the Assets scope-vs-role gotcha

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 15:23:20 -04:00