# pasteToPrint document assistant

The assistant uses a server-only OpenAI Responses API adapter. Browser code never receives the OpenAI API key and never calls OpenAI directly.

The public, plain-language service page is `ai-asistent.html`. Its calls to action open `/?assistant=open`, optional example links prefill `assistantPrompt` without sending it, and the page reads `api/assistant/status.php` so account-level administration is reflected before launch.

## Runtime configuration

1. Revoke any key that was pasted into a chat, issue tracker, source file, or client-side configuration.
2. Create a new project key.
3. Sign in to `ads-manager.html`, open **Asistent**, and save the key in the **OpenAI API kľúč** section.
4. The backend encrypts the key with Sodium and stores only the encrypted value in MySQL table `ptp_assistant_credentials`.

The key is never returned to the browser after it is saved. Do not put it in `index.html`, JavaScript, `.htaccess`, a committed PHP file, a URL, or an assistant request body.

## Provider contract

- Model: `gpt-5.5`
- Endpoint: `POST https://api.openai.com/v1/responses`
- Storage: `store: false`
- Reasoning effort: `low`
- Output: strict Structured Outputs using `api/assistant/provider/v1/schema.json`
- Generated media: Responses API `image_generation` tool, one reviewed WebP result per request
- Server validation: provider schema, content-template registry, PTDF validator, then assistant response contract
- Client application: after one versioned direct-edit consent, validated non-media PTDF responses replace the current editor state automatically; the previous review/import path remains available

The model can create a full draft for a new document or propose bounded operations for an existing PTDF document. Media binary data is removed from model context, and full AI replacement of an existing document containing media is rejected.

The public contract has 8 universal actions. `generate_image` covers both ordinary images and printable coloring pages without introducing a document-specific billing or drawing core.

## Direct editing and proposal workflow

Assistant responses containing a document remain in the in-memory proposal history. Normal document responses can also use a direct application path:

- up to 8 recent proposals are kept only in page memory; reload and **New conversation** clear them;
- a proposal can be reviewed, downloaded as PTDF, rejected, and restored while the page remains open;
- the first eligible result asks for a versioned direct-edit consent and links to the terms; the consent record contains only its version and grant time, never messages or document content;
- after consent, eligible non-media responses are strictly validated and replace the current editor state without opening the PTDF import dialog;
- every automatic application creates an editor-history step and exposes **Undo AI change**; users can revoke direct editing from the assistant at any time;
- generated images and coloring pages remain in the explicit review workflow;
- manually opening a proposal still uses the PTDF dialog with explicit append or replace actions;
- proposals derived from an existing document keep a SHA-256 fingerprint of the request snapshot when Web Crypto is available, with a deterministic local-browser fallback;
- the fingerprint is checked again immediately before automatic or manual application; if the editor changed, the proposal is marked outdated and application is blocked until the assistant creates a fresh proposal.

No proposal content is written to `localStorage`, `sessionStorage`, or IndexedDB.

## Quality and observability

Stage 6 adds explicit feedback, local diagnostics, and offline regression evals:

- successful assistant replies offer **Helpful** and **Not helpful** actions;
- feedback is sent only after a user click to `api/assistant/feedback.php`;
- the strict feedback contract has no message, reply, history, or document fields;
- the server stores monthly aggregate counters, not individual feedback records or request IDs;
- aggregate files use the server temporary directory and aggregates older than 12 months are removed;
- feedback is limited to 60 submissions per source IP per 24 hours;
- up to 40 diagnostic events stay in page memory and disappear on reload or **New conversation**;
- diagnostic export contains event types, result metadata, latency bands, and counters, but no user or model content;
- multilingual document-flow evals run with an injected mock transport and never call OpenAI.

## Production runtime controls

Stage 7 places an authoritative runtime gate in front of every provider request. `api/assistant/status.php` exposes only the public state and bounded limits; it never returns environment values or any part of the API key.

Environment controls:

- `PTP_ASSISTANT_ENABLED` — master feature flag, default `true`;
- `PTP_ASSISTANT_ROLLOUT_PERCENT` — deterministic source-IP cohort from 0 to 100, default `100`;
- `PTP_ASSISTANT_DAILY_REQUEST_LIMIT` — global UTC-day provider request budget, default `500`;
- `PTP_ASSISTANT_MAX_CONCURRENT` — active provider leases, default `4`;
- `PTP_ASSISTANT_CIRCUIT_FAILURES` — failures required to open the circuit, default `5`;
- `PTP_ASSISTANT_CIRCUIT_WINDOW_SECONDS` — rolling failure window, default `300`;
- `PTP_ASSISTANT_CIRCUIT_COOLDOWN_SECONDS` — open-circuit pause, default `120`;
- `PTP_ASSISTANT_RUNTIME_DIR` — writable runtime-state directory. For production, point it outside the web root to persistent server storage.

Expired concurrency leases are removed after 240 seconds. This keeps the lease valid across the 180-second provider timeout used for image generation. The browser refreshes runtime state when the assistant opens, displays a state indicator beside GPT-5.5, and avoids a provider request when the public state is unavailable. The server remains authoritative if browser state becomes stale.

## Administrative account access

The **Asistent** section in `ads-manager.html` uses the existing protected administration session and provides:

- secure OpenAI API key creation, replacement and removal backed by encrypted MySQL storage;
- one master switch for all assistant access;
- separate switches for anonymous guests, signed-in Free accounts, and signed-in Premium accounts;
- server-side enforcement in both the public runtime status and message endpoint, so a hidden or modified browser control cannot bypass the policy.

Access settings are stored in `storage/private/assistant-access.json` by default. Set `PTP_ASSISTANT_ACCESS_FILE` to a writable path outside the web root in production. A missing configuration file preserves the previous behavior by allowing all account types. `PTP_ASSISTANT_ENABLED=false` remains the emergency environment-level kill switch and always takes precedence.

## Content security and secret handling

Stage 8 adds a server-side security boundary around the model request:

- the user message, conversation history and normalized PTDF text are scanned before rate limiting, runtime admission and the OpenAI request;
- high-confidence API keys, private-key headers, bearer credentials, JWTs and common provider access tokens are rejected with HTTP `422` and are never sent to OpenAI;
- business data such as IBANs, VAT IDs, invoice numbers, postal addresses and ordinary document text remains allowed;
- media `dataUrl` payloads remain omitted from model context and are excluded from text scanning;
- `instructions` contains only application rules, while the user request and reference-only history/document remain in `input` with explicit trust labels;
- instructions embedded in a document or conversation are treated as untrusted reference content, which is covered by prompt-injection regression tests;
- validated model output is scanned again before a proposal can reach the browser;
- security telemetry stores only monthly counters by detection category and source. It never stores matched values, messages, documents, request IDs or IP addresses and retains at most 12 months.

`PTP_ASSISTANT_SECURITY_DIR` can point the aggregate counter directory to writable storage outside the web root. When unset, the server temporary directory is used. A pasted or otherwise exposed credential must still be revoked; the scanner is a defense-in-depth control, not a recovery mechanism.

## Deployment readiness

Stage 9 prepares the assistant for a controlled production handoff without making a live OpenAI request:

- each provider request can include an OpenAI `safety_identifier` derived as a 48-character HMAC digest of the source identifier;
- the HMAC salt is derived server-side from the existing application secret with a separate HKDF context and is never stored with the encrypted API key;
- raw IP addresses, account identifiers, salts and keys are never inserted into model input or deployment reports;
- when the salt is absent, the optional safety identifier is omitted instead of sending a reversible or raw identity;
- `scripts/assistant-deployment-check.php` validates PHP/cURL, all assistant schemas, the template registry, model consistency, writable state storage, provider configuration and production HTTPS;
- preparation mode reports a missing database credential as `pending`, so the codebase can pass its release gate before the new API key is added;
- production mode additionally requires an HTTPS public URL and explicit runtime/security directories outside the web root;

Apply the MySQL credential table migration once per database:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/migrate-assistant-credentials.php --confirm=MIGRATE-ASSISTANT-CREDENTIALS
```

Run the safe preparation check at any time:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/assistant-deployment-check.php
```

After saving a newly issued key in administration and configuring the HTTPS URL and external runtime directories, run the authoritative production check:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/assistant-deployment-check.php --production
```

## Launch acceptance

Stage 10 adds the final deployment identity and smoke-test layer:

- `api/assistant/manifest.php` exposes a secret-free, deterministic build ID, exact component contract versions and universal capability counts;
- the build ID changes when assistant schemas, server orchestration, security/deployment logic, browser controller, direct-edit consent, proposal handling, observability or PTDF application changes;
- `scripts/assistant-launch-check.php` verifies the deployed manifest and runtime over HTTP without calling the model by default;
- the optional live probe goes through the normal application endpoint, runtime budget, secret scanner and response contract;
- a provider request is impossible unless production HTTPS mode and both `--live` and `--confirm-live` are present;
- the synthetic prompt requests an informational, document-free response and does not include a key or server safety identifier in the browser-facing body;
- the assistant dialog now returns keyboard focus to its launcher, exposes processing through `aria-busy`, announces status through `aria-live`, and connects its input to the AI safety note.

Safe launch check with no provider request:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/assistant-launch-check.php --base-url=https://www.pastetoprint.com --production
```

Only after the safe production check succeeds, run one explicitly confirmed paid provider probe if required:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/assistant-launch-check.php --base-url=https://www.pastetoprint.com --production --live --confirm-live
```

## Generated images and coloring pages

Stage 11 adds reviewed image generation to the same GPT-5.5 conversation:

- ordinary text/document requests do not expose the built-in `image_generation` tool at all;
- only the explicit Image or Coloring page quick action adds a bounded server capability (`image_generation` or `coloring_page_generation`), and the returned `mediaKind` must match it;
- while that tool is authorized, conversation history and document content are omitted from its model context; only the current document's non-content shell is retained so untrusted reference text cannot steer the tool;
- a vague request such as “create an image” returns `needs_input` and asks what the image should depict instead of spending an image-generation call;
- each accepted request may return exactly one generated image;
- the tool is configured for a portrait, opaque, medium-quality WebP with bounded compression so the result fits PTDF 1.0;
- the server strictly decodes Base64, checks the real WebP/PNG/JPEG signature, enforces a 1.1 MB binary ceiling and rejects unauthorized, unexpected or missing tool output;
- the model supplies only localized title, alternative text and optional caption metadata; it never supplies a `dataUrl`;
- an ordinary image preserves the requested subject and style, while a coloring page receives additional print rules for a white background, clean closed outlines, generous margins and no gray, gradients or shadows;
- a new image becomes a one-block PTDF preview; for an existing document the server appends an image block while preserving the current content and reports `append_block` in the audit trail;
- the public contract carries `mediaKind` so the reviewed result has an explicit, language-neutral workflow: ordinary `image` proposals open the PTDF review dialog, while `coloring_page` proposals enter the existing Coloring Book import process;
- the Coloring Book path normalizes the raster through the shared drawing importer, activates and locks it as `coloring-reference`, and renders it into the composite boundary map used by Fill; this prevents the generated page from bypassing the line-aware coloring workflow;
- both paths remain human-controlled: a normal image changes the editor only after PTDF confirmation, and a coloring page starts its editable Coloring Book session only after the proposal is approved;
- the quick actions reuse the existing drawing translation catalog, providing localized Image and Coloring Book labels across all 48 registered locales without duplicating locale data.

Image generation can have materially higher latency and separate image-generation cost. The HTTP timeout is 180 seconds, while runtime budgets, rate limits, encrypted credentials, proposal review and feedback controls remain unchanged. A project may also need OpenAI organization verification before GPT Image access is available.

## Fullscreen assistant

Stage 13 keeps the same stateful assistant available over the document preview in both native and fallback fullscreen modes:

- before `requestFullscreen()` the existing `#ptpAiAssistant` DOM node is mounted inside the fullscreen preview container, so native fullscreen includes it;
- no assistant clone is created, preserving the open/closed state, textarea value, conversation, proposals, consent layer and any in-progress response;
- leaving fullscreen restores the same node to its original parent and original DOM position;
- the same mount/restore lifecycle covers browsers that use the CSS fullscreen fallback;
- fullscreen-specific stacking, viewport height and mobile safe-area rules keep the bottom sheet usable without obscuring the whole document.

The fullscreen integration is entirely client-side and does not create an OpenAI request by itself.

## Limits

- User message: 3,000 characters
- Conversation history: 12 messages, kept only in the open browser page
- Current PTDF request snapshot: 500,000 serialized characters
- Whole request: 750,000 bytes
- Provider operations: 50
- Generated draft blocks: 100
- Generated images per request: 1
- Generated image binary: 1,100,000 bytes maximum before Base64 wrapping
- Public rate limit: 30 requests per 10 minutes per source IP

## Verification

The provider tests use an injected mock transport and never make an OpenAI API call:

```powershell
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-provider.php
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-credentials.php
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-credentials-database.php
node scripts/test-ai-assistant-stage4.js
node scripts/test-assistant-proposals.js
node scripts/test-assistant-direct-edit.js
node scripts/test-assistant-observability.js
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-feedback.php
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/run-assistant-evals.php
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-security.php
& 'C:\laragon\bin\php\php-8.3.30-Win32-vs16-x64\php.exe' scripts/test-assistant-access.php
& scripts/test-assistant-stage10-release.ps1
& scripts/test-assistant-stage11-media.ps1
& scripts/test-assistant-stage13-fullscreen.ps1
```

Official references:

- https://developers.openai.com/api/docs/models/gpt-5.5
- https://developers.openai.com/api/docs/guides/structured-outputs
- https://developers.openai.com/api/docs/guides/image-generation
- https://developers.openai.com/api/reference/cli/resources/responses/methods/create
