GenericTemplateGenerator and FreeDocumentGenerator now, when CaseFilesCore is
installed (soft-detected), set the generated Document's folderId to the case
"מסמכים" folder via CaseFolderService so it shows in the CaseDocs panel.
Existing Case link + NetworkStorage upload unchanged.
Verified on dev (P7_OK): php -l clean on both generators; relate/find junction
consistency confirmed; folder placement works.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stop self-naming in the document generators and defer to NetworkStorageIntegration's
single namer (rule N1):
- FreeDocumentGenerator and GenericTemplateGenerator no longer bake in a date prefix,
em-dash or contact name. They set the clean subject, upload explicitly to the case
folder (the AfterSave hook fires before the Case link exists), then realign the
Document + Attachment names to the canonical stored name returned by the upload.
- CaseFolderManager sanitizes every model-chosen name server-side: writeFile splits
dir/basename and de-duplicates via buildStoredFileName; createFolder, renameItem and
moveItem sanitize their final segment. The LLM can no longer pick a raw filename.
Refs Task Master #7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without Controllers/AssistantRule.php, EspoCRM responds 404 "Controller
'AssistantRule' does not exist" on every POST/GET /api/v1/AssistantRule,
even though scopes/AssistantRule.json declares entity=true, object=true,
acl=true. The save_rule tool in shira-hermes calls the Record endpoint
directly, gets the 404, falls back to nothing (because the user_id branch
of the fallback rarely matches), and the LLM tells the user "✅ כלל נשמר"
without verification — yet another instance of the hallucination pattern.
A one-line Controller class that extends Espo\Core\Controllers\Record
exposes the standard CRUD verbs and unlocks the storage path that the
rest of the stack already expected. Verified in prod: GET, POST, DELETE
all return 200 after this is in place.
Paired with shira-hermes 06b27e9 which teaches the LLM to actually read
the tool result and never report success on a ❌ response.
Refs Task Master #6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of the Shira hallucination incident in production case 46 Friedman:
DocumentAnalyzer::extractFromDocx relied on PhpOffice\PhpWord, but on the
EspoCRM prod image the PHPWord files are present in vendor/ yet are NOT
registered in the composer PSR-4 autoload map — class_exists() silently
returned false, the method returned null, and Shira's read_document fell
back to OCR. OCR then only saw the signature image, the AI got "[signature]"
as document content and fabricated the entire CTS appeal as a knee injury.
This change rewrites extractFromDocx to use ZipArchive + a small regex
parser of word/document.xml. Independent of PHPWord, more robust on
tables/footnotes/hyperlinks (which PHPWord's element walker missed at depth
>1), and verified on prod against the same Friedman appeal: 80 paragraphs /
16633 clean chars extracted (vs 0 before).
The paired Python fix in shira-hermes (commit 7b517e1) makes the OCR
fallback also read document.xml, so even if this PHP fix regresses again,
the AI will not be fed "[signature]" as document content.
Refs Task Master #5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shira can now create documents from scratch (not just from templates) and has
full sandboxed read/write access to the case's network folder, including a
soft-delete protocol so she cannot physically remove files.
New services:
- FreeDocumentGenerator: builds RTL Hebrew DOCX from lightweight markdown,
saves as Attachment + Document, links to Case, copies to network folder.
- CaseFolderManager: writeFile/createFolder/renameItem/moveItem operations
constrained to the case's own root via path normalization; never exposes
a physical delete. markForDeletion moves the file into
"<caseRoot>/מסמכים למחיקה/" with a "למחיקה - " prefix so the lawyer can
audit and delete manually.
New controller actions: createDocument, writeToFolder, createFolder,
renameItem, moveItem, markForDeletion.
Bug fix (root cause of the empty TEMPLATES section in Shira's case prompt):
CaseContextBuilder was querying DocumentTemplate with `entityType=Case` but
the real column is `targetEntityType`. Also added `isActive=true` filter and
surfaced category/description for better LLM selection.
Alert refinement: AlertCalculator no longer raises "X days without activity"
for cases that are legitimately waiting on the court. PendingDecision is
always silenced; PendingHearing is silenced when cNextHearing is in the
future (unscheduled PendingHearing cases still alert).
Document entity: new `markedForDeletionAt` + `markedForDeletionBy` audit
fields with Hebrew labels.
Refs Task Master #4
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The floating chat called Espo.Ajax.postRequest with timeout:180000, which is
shorter than the agentic loop's worst-case latency. On 2026-05-13 a prod
request to generate a direct-access report sat 227s in the first ai-gateway
iteration (large skill+context payload), so the browser fired the .catch()
handler at 180s and rendered "שגיאה בתקשורת" — even though the backend
completed the work 47s later (report attached to case 42).
Changes:
- floating-chat.js: timeout 180000 → 600000 (matches the PHP curl timeout
already set to 600s in SmartAssistantService.php:521).
- After 60s the spinner label swaps to a "still working" hint so the user
knows long requests are normal, not stuck.
- i18n: new SlowHint label in en_US + fa_IR (the project uses fa_IR as the
Hebrew slot).
Refs Task Master #3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New POST /api/v1/SmartAssistant/action/getDocumentBytes returns
{success, fileName, mimeType, sizeBytes, base64} for use by
shira-hermes Claude Vision OCR fallback.
- DocumentAnalyzer: new getDocumentBytes() method + guessMimeType helper.
- CaseContextBuilder: expose signatureRequests in case context (for
DigitalSignature integration — gracefully empty if entity absent).
- README: sync version badge.
- Version bump 2.7.3 → 2.8.0 (new public endpoint).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tasks with parentType='Contact' were invisible to AlertCalculator,
CaseContextBuilder, and OfficeContextBuilder — all queries only checked
parentType='Case'. This caused false "no preparation task" alerts in
the daily standup even when preparation tasks existed.
Broadens getSummary, findOverdueTasks, findUpcomingTasks, getWorkloadByUser
to query parentType=['Case','Contact']. Adds caseHasPreparation() helper
that checks Case tasks, Contact tasks, and NhActivity-linked tasks.
Adds getCaseContactIds() to CaseContextBuilder and OfficeContextBuilder.
Refs Task Master #1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>