Skip to content
KAMOUniverse

Changelog

What's new.

Every improvement to the platform, as it ships.

  1. · kamo-asterisk-support

    Voicemail uploads name their message, and Kamo can ask for one it has no audio of

    A voicemail upload now carries the message's own id, <mailbox>/<msg_id> from the msgNNNN.txt Asterisk writes beside it, as its unique id: VOIPService files FreePBX voicemail under exactly that id, so the two pair exactly instead of by time. One upload per message, from its PCM copy (msgNNNN.wav). The wav49 copy (.WAV), the greetings (unavail, busy, greet, temp) and a mailbox's tmp files are no longer uploaded, so they are no longer deleted from the spool either: uploading them used to take a mailbox's greeting away and give each message a second, competing upload. A message whose sidecar names no msg_id is not uploaded and stays where it is. The policy pull now carries voicemailAudio: messages Kamo has no audio of and asks for (KamoAI SP98). VoicemailAudioSender finds each one by msg_id in its mailbox's folders, takes the best copy the spool still has (wav, WAV, gsm, ...), has Asterisk convert it into a WAV in its own tmp directory over the helper's manager user (file convert), uploads it under the message's id and removes the temporary file. The spool is never changed. Ids that are not <mailbox>/<msg_id> are dropped at the pull; each message is attempted once per ten minutes, in the background. Agent version 2.1.0.

  2. · VOIPService

    Shared voicemail boxes, and FreePBX voicemail audio from the message itself

    Shared boxes (SP98-N-2). A mailbox on the PBX that no Kamo extension is (K1M1's 8100, "KamoCRM Main Line") was never synced. The sweep's single AMI session now also asks VoicemailUsersList once the extensions are read, records each such mailbox in FreePBX's user context as a phone server's shared box (PHONE_VOICEMAIL_BOX, DDL in docs/sql; INSERT ... ON CONFLICT so a second sweep cannot poison the transaction), and syncs its messages as rows with no extension. Each box has a notify list (PHONE_VOICEMAIL_BOX_NOTIFY, nobody by default): a new message raises VOICEMAIL_RECEIVED for those members, opening Settings > Phone > Voicemails. GET/PUT **************** lists the boxes and replaces a box's list (MANAGE_VOIP_SETTINGS; members of the org only, at most 25). A box's messages are listed, played, marked read and deleted only by members who manage phone servers, in both voicemail lists. Audio (SP98-N-3). Settings plays the upload that names the message exactly (the helper now sends <mailbox>/<msg_id>), then the softphone's pairing by mailbox and time. With neither, the message itself is requested from the PBX: VoicemailAudioRequests records it in Redis for ten minutes, the policy pull carries it as voicemailAudio, and the helper uploads that message's audio under its own id. The player gets 404 with a reason: AUDIO_REQUESTED, AUDIO_NOT_SENT (asked three minutes ago, nothing came), HELPER_OFFLINE or AUDIO_NOT_UPLOADED. The softphone asks for the messages it cannot pair, so their audio arrives before anyone presses play. The per-server list now gives every row its id (the player asked for /voicemails/<externalId>/audio, which is no id, so it never played) and its mailbox, and reads the extensions once instead of per row.

  3. · kamo-internal

    Phonetic spellings of the company name and short name in Company Info (SP94 T7)

    Two optional fields beside Company Name and Short Name, loaded from and saved to PUT /api/security/org/{id} as **************** A blank box is sent as null so a cleared spelling is actually cleared. They are used only for the text the AI's speech engine receives.

  4. · AIService

    E5 speaks the org's name and short name in the phonetic spellings the org set (SP94)

    The speech model is sent the caller's input with every whole-word mention of the org's name or short name replaced by its phonetic spelling (OrgPhoneticSpeech). The org is read once per request, after the request is validated and before the walk, so every candidate is sent the same text, on both of SP96's paths (a person's or caller's voice, and an AI member's own voice). The 4096-character cap and the CHARACTERS_1K measure stay on the caller's input. An org that is not found, or cannot be read, is spoken as written; a failed read is a WARN.

  5. · MCPGatewayService

    Phone_call says plainly when the org's outbound policy blocks a number, and names text or email instead (SP93 T12)

    K6 refuses a number the phone server's toll-fraud guards block with 409 BLOCKED_BY_OUTBOUND_POLICY (SP93 T11). phone_call now answers it as its own status, with a clear message and fallback [sms_send, mail_send], beside NO_VOICE_CONSENT, and the audit records it as DENIED, not ERROR.

  6. · VOIPService

    The PBX's managed policy carries the toll-fraud block list (SP93 T7)

    /internal/voip/pbx-config answers blockedPatterns, the phone server's guards as Asterisk patterns **************** for kamo-asterisk-support's [kamo-toll-fraud-blocked] context. Every guard off answers an empty list. The version hash covers the list too, so it moves when a guard does (SP93-T7-1).

  7. · SecurityService

    Cap the org's two phonetic name spellings on the settings PUT (SP94 T4)

    titlePhonetic and titleShortPhonetic ride PUT /api/security/org/{id} under the same rights as title/titleShort (ownership or CONFIGURE_SYSTEM, never god-mode only). Each is capped at 255 characters, refused with 400 {error, field} before anything is stored, as the member's namePhonetic is. GET /{id} already returns the entity whole, so both appear there with no change. **************** runs the real OrganizationService over one stored row: owner sets both, a caller without the right gets today's 403, 256 characters on either is 400 naming the field, 255 is stored, a PUT naming one leaves the other, null clears, and the GET carries both (null until set).

  8. · KlusterServices

    The outbound gate refuses a number the toll-fraud guards block (SP93 T9)

    [kamo-outbound-gate] gains a GotoIf on kamo-asterisk-support's managed [kamo-toll-fraud-blocked] list, between the SP13 AI-transfer line and the Goto onward, and a (blocked) branch that tags OBBLOCK, plays the not-available prompt and hangs up with cause 21. An allowed call no guard matches still ends at the same Goto. 911 and 933 never reach the gate. **************** makes the same edit on the running node: one guarded, idempotent edit of the gate's Goto after '; sp13:ai-xfer-outbound', refusing with nothing written when that anchor is missing or [kamo-toll-fraud-blocked] is not yet in the managed policy section; it backs up, reloads the dial plan and verifies what Asterisk loaded. The fixture is the k1m1 file as of 2026-10-04.

  9. · VOIPService

    The phone server's toll-fraud guards for KamoAI's pre-flight check (SP93 T6)

    GET **************** answers {blockInternational, blockPremiumRate, blockHighRiskAreaCodes, blockedPrefixes} for one phone server, read by its id; a server that doesn't exist is 404 INSTANCE_NOT_FOUND. Guarded by InternalAuthFilter like every KamoAI route.

  10. · AIService

    Remove the charter voice catalogue, GET /api/ai/charter/voices (SP96)

    The charter no longer pins a voice: an AI member remembers one per speech model, read and chosen at **************** The charter editor's voice picker was the catalogue's only caller (kamo-internal, SP96 Task 19), so the endpoint and VoiceCatalogService go, with its test. The /api/ai/charter/** access row stays for the preview. CharterPreviewControllerTest scans every controller of the service and fails if any route, a pattern included, answers /api/ai/charter/voices again. AiAccessRulesTest samples /api/ai/charter/preview for the charter row instead.

  11. · kamo-shared-library

    Phonetic spellings of the org's name and short name, for the AI's speech (SP94 T3)

    Organization.titlePhonetic / titleShortPhonetic (TITLE_PHONETIC, TITLE_SHORT_PHONETIC, VARCHAR(255), nullable), on patchOrganization's allow-list beside title/titleShort, and OrgPhoneticSpeech.forSpeech: a pure, single-pass, case-insensitive, whole-word swap of the org's names for their spellings, longest name first, for AIService's speech API only. Needs SP94 T2's DDL applied in production before this is pushed.

  12. · kamo-asterisk-support

    Render the toll-fraud block list as [kamo-toll-fraud-blocked] (SP93 T8)

    The PBX policy pull now carries blockedPatterns, the Asterisk extension patterns of the phone server's optional toll-fraud guards. Fetch keeps only an underscore followed by digits and the N, X, Z and . wildcards (at most 40), so nothing from the response can add a priority, an application or a context to the dial plan. RenderPolicy writes them as a third managed context beside the two allow-lists; with every guard off it still defines the context with a pattern no digits match, so the outbound gate's DIALPLAN_EXISTS is always safe. Tick threads the list through PolicyWriter.Apply and logs it on a change. A Kamo that predates the field sends none, which blocks nothing.

  13. · VOIPService

    New extensions start as the phone server's outbound setting says (SP93 T5)

    A new extension, a person's or an AI member's line, now starts allowed to dial outside only when its phone server's newExtensionsOutboundAllowed setting is on. - AiVoiceAssignmentWriter.assign reads the setting instead of the literal true. Every assign creates a line, so a row already there (sync-inserted, or left denied by a retired line) takes it too; clear still always denies. - **************** and **************** set it on a row they create, read at most once a call; a cached row's flag, an admin's choice, is never touched.

  14. · VOIPService

    The phone server's outbound policy in the AI voice settings (SP93 T4)

    GET and PUT **************** carry SP93's five fields: newExtensionsOutboundAllowed, blockInternational, blockPremiumRate, blockHighRiskAreaCodes and blockedPrefixes (a list). A custom prefix is trimmed, blanks dropped and repeats merged; each must pass **************** (1-15 digits, never 911, 933 or a prefix of either), at most 25, or the PUT is 400 {field:blockedPrefixes} and nothing is stored. Like every other field here the PUT is a whole replacement: a missing or null flag is off. Nothing stored shows the policy off. Still MANAGE_VOIP_SETTINGS alone.

  15. · AIService

    The member voice endpoints, to read and choose an AI member's voice per speech model

    SP96 Task 10 (spec §5.5): - GET **************** the router's TTS candidates for the AI (actor AI with the member; never AiCallRuntime.guard, so a paused or retired AI reads), those a speech adapter serves, preferred model first: E5's walk. Each model has its remembered voice, decide()'s next voice with nothing written, and the compatible voices best tier first; rows on models not offered are listed unavailable. - PUT **************** {voiceId?, preferred?}: a CHOSEN voice (the caller, the sex at pick), then the model used first, in one transaction (MemberVoiceWriter, @Transactional @RetryOnDbConflict, no HTTP). Preferring a model with no row remembers E5's first pick there, unspoken. - GET **************** the same view, canChange false, behind X-Internal-Auth (InternalCaller). - Read: MANAGE_MEMBERS, MANAGE_MEMBER_SECURITY, MANAGE_AI_WORKFORCE, VIEW_AI_WORKFORCE, or SUPERVISE_AI_MEMBERS as its supervisor. Change: MANAGE_AI_WORKFORCE, or its supervisor with SUPERVISE_AI_MEMBERS. Never the AI itself. Refusals MISSING_RIGHT, MEMBER_NOT_FOUND, MODEL_NOT_AVAILABLE, VOICE_NOT_OFFERED, VOICE_NOT_COMPATIBLE, RETIRED. - AiAccessRules: /api/ai/members/** is a member route; the controller decides.

  16. · MCPGatewayService

    A verified request from someone who may approve is the approval: an ON_REQUEST row, the action at once, its outcome on K3b (SP95 T34)

    When the charter asks for approval and the AI cites a verified request (onRequestOf), K3 is asked with mode ON_REQUEST, the requester and the reference. An APPROVED row lets the action run at once; it still passes the limits and the outreach gate, and what it came to is reported on K3b (EXECUTED, or FAILED with its status and message, before the key is freed). An ambiguous outcome is left to the tick, which re-executes under the same key. K3's NOT_AN_APPROVER, REQUEST_USED and SHADOW, a soft refusal and a SHADOW charter ask a person as before, and the answer says why (onRequestOf {accepted:false, reason}). FORBIDDEN still refuses, and an action that needs no approval writes no ON_REQUEST row. A dry run asks K3 under X-Kamo-Dry-Run and writes nothing. A re-executed ON_REQUEST approval keeps the outreach gate (K4 mode).

  17. · kamo-shared-library

    Per-phone-server outbound policy and OutboundCallPolicy (SP93 T3)

    SP93 Task 3 (spec §5.2, D1, D4-D6): - AiVoiceInstanceSettings gains **************** BLOCK_INTERNATIONAL, BLOCK_PREMIUM_RATE, BLOCK_HIGH_RISK_AREA_CODES (boolean, NOT NULL DEFAULT false) and BLOCKED_PREFIXES (VARCHAR(500) NOT NULL DEFAULT ''), plus blockedPrefixList(). - New pure OutboundCallPolicy: the Settings record, the FCC 'One Ring' high-risk NANP area codes, the premium-rate codes 900/976, isValidPrefix (1-15 digits, never 911, 933 or a prefix of either), dialPatterns (the Asterisk patterns for [kamo-toll-fraud-blocked], in a fixed order, each once, invalid stored prefixes dropped) and blocks (does a rendered pattern match the AI's PBX dial digits), so the PBX and an AI's pre-flight check never disagree. The five columns must exist (SP93 Task 2's DDL apply) before this reaches main.

  18. · MCPGatewayService

    An AI may cite the request it acts on (onRequestOf), checked by reading it; a run's taint says where it came from (SP95 T31)

    Every AI write that may need approval takes the optional onRequestOf (chat:<conversation>/<message>, task:<item>, task:<item>/comment:<id>) on tools/list. The pipeline takes it out before the schema check, the planner and the args hash, and RequestVerifier reads the reference as the AI (D20): a forged or outside reference refuses the call (INVALID_ARGUMENTS REQUEST_NOT_VERIFIED + why, key freed); a genuine one that is stale, not this run's or in an externally tainted run is a soft refusal; a verified one goes to the planner on the default plan (ToolPlan.VERIFIED_REQUEST). A dry run reports onRequestOf.accepted. Approvals are unchanged until Task 34. RunTaint gains a source: chat reads of a team CHAT whose authors are all on the org's team taint the run INTERNAL; everything else is EXTERNAL, which alone blocks a cited request (D22).

  19. · AIService

    The brief says which company the AI works for, and words a call to a colleague (SP95)

    The charter profile SecurityService sends now carries orgName, the org's spoken name (short name, else display name, else legal name; SP95 D15). The brief's identity line closes with "You work for <orgName>." when it is non-blank, so no brand is ever written into the brief by hand. orgName is part of the profile fingerprint, so a renamed org recompiles its AIs' briefs. PLACE_INTERNAL_CALL (SP95 D1) reads "Calling colleagues" in the approvals and escalation sections; the default arm from T1 stays for future classes. CharterCompiler.VERSION moves to sp95-1, as its own rule asks when the wording changes, so a job-title persona brief also picks up the new label once. CharterSnapshotTest's approvals count follows AiActionClass instead of pinning 18: shared-lib's PLACE_INTERNAL_CALL makes it 19.

  20. · VOIPService

    The DDL file for the phone server's outbound policy

    SP93 Task 1. Five NOT NULL DEFAULT columns on AI_VOICE_INSTANCE_SETTINGS: **************** BLOCK_INTERNATIONAL, BLOCK_PREMIUM_RATE, BLOCK_HIGH_RISK_AREA_CODES and BLOCKED_PREFIXES, all defaulting to today's behaviour. Sp93DdlContractTest pins the file to spec 5.1 and checks every column is re-runnable, carries its DEFAULT in its own clause, and that there is no CHECK, JSON or DROP.

  21. · EmailService

    A member's new name reaches their mailbox's display name (SP95 T35)

    POST **************** {previous, current} -> {updated}, behind the KamoAI internal secret like provision-mailbox. The mailboxes the member holds as primary whose display name is blank or still the previous one (trimmed, ignoring case) take the new name; a name an admin chose, and a mailbox the member only shares, stay. A blank current name is a 400. One @RetryOnDbConflict transaction keyed by member; no provider call (KamoMail keeps no server-side display name).

  22. · kamo-internal

    Call colleagues in the charter, the phone server's team line, and "Asked for" on an action someone asked for (SP95 T26)

    - Charter: PLACE_INTERNAL_CALL beside PLACE_CALL; APPROVAL holds one item per class (19, ruling SP95-T4-1). - Phone server card: the team line (defaultTransferExtension), sent on every save, blank as none (V10 replaces every field). - Approvals: an "Asked for" chip for mode ON_REQUEST in the list and the detail; the detail says who asked and links to the chat (opened in place) or the task (/notes?task=). Types read through **************** (SP95-T26-1).

  23. · SecurityService

    A rename reaches the member's mailboxes (SP95 T36)

    After a committed change of a person's "First Last", SecurityService posts {previous, current} to EmailService's **************** once per membership of the user, with the KamoAI internal secret (the MAILBOX hire step's client and URL). EmailService renames only the mailboxes that member holds as primary and that still carry the old automatic name. - MemberNameChangeNotifier: after commit only (AfterCommit), off the request thread on kamoaiExecutor, best effort (WARN, never fails the save); a name cleared to blank is not announced. - Called by the member profile PUT and the platform Users tab's account edit, only when "First Last" changed. AccountController's parseName names a customer Account, not a person, so it announces nothing.

  24. · AIService

    An org that ranks OpenAI transcription first streams STT through it, and every ticket names its session dialect (SP95)

    transcription model was dropped as ADAPTER_UNSUPPORTED and every streaming STT ticket went to Kamo Speech on CPU. Each catalogue row now names the session (the model rides in the session update), and the ticket answers sessionDialect.

  25. · APIService

    Route website contact forms, on the session-creation budget

    POST /api/public-chat/contact proxies to MediaService's new contact endpoint through the same site-key, origin and scope checks as every other public-chat call, so a lender's contact form files a ticket in that lender's own support queue. It creates a session and a ticket, so it guards the *** like its siblings and draws on the strict creation budget (3/min per IP). It is the one creating path without "sessions" in it, so it is matched by name; left to the message budget a script could file 30 tickets a minute from one address.

  26. · MediaService

    A lender's website contact form files a ticket in their own queue

    POST /api/media/public-chat/contact, reached through the gateway's site-key gate like every other public-chat call, so the key names the org and its allowed origins name the website. The platform's own contact form could not do this: it files into the platform org, whatever site posted it. A contact form is a chat opened and typed into once, so it is built from exactly those two steps: createSupportSession (the integration's topic, attribution, geo, the topic's assignment strategy after commit) and sendSupportMessage (the visitor's first message). Only the title and description differ, and both lead with a card of name, email and phone, because the visitor has left the page and email is the way back to them. The queue is told once the message is in the thread, as with the AI handoff. A filled honeypot answers success and files nothing.

  27. · MCPGatewayService

    Rag_search takes audience PUBLIC, the search the public may read (SP95 T12)

    An AI on a phone call answers an anonymous caller, so it searches only the published articles the public may read, never notes (spec D12). The optional audience enum is ["PUBLIC"] and is forwarded to RAGService (Task 11); any other value is INVALID_ARGUMENTS by schema and never reaches RAG.

  28. · KlusterServices

    Rspamd keeps its data in its own Redis

    local.d/redis.conf is now the redis-own.conf key: rspamd-redis in mail, database 0, the same mail_rspamd login. The subPath change rolls the pod. The old key stays in the ConfigMap until the switch is confirmed, so `kubectl -n mail rollout undo deployment/rspamd` is the whole rollback (SP98-M-1, stage 2 of 3). Ship it only after rspamd's data has been copied into rspamd-redis.

  29. · KlusterServices

    A Redis of rspamd's own

    rspamd parses untrusted mail and logs in to the platform Redis, beside every member's session, and no ACL can keep it from them there: a user cannot be scoped to one database; rspamd's scripts pass class labels, flags and msgpack blobs as KEYS, so a key-pattern ACL refuses every Bayes learn; and its Bayes expiry SCANs, which lists every key name (*** and OTK names are bearer credentials) whatever the patterns. This adds rspamd-redis in mail: the platform Redis's build, pinned to k1m1 beside rspamd with its files on that node's disk, one database, PING only without a login, mail_rspamd with rspamd's existing password, kamo_ops for operators, and a NetworkPolicy that admits rspamd's pods alone. CI applies it and waits for it before rspamd. rspamd itself is unchanged until the next commit (SP98-M-1, stage 1 of 3).

  30. · SecurityService

    A charter save reaches the AI runtime at once, and the charter profile names the company (SP95 T6)

    **************** now carries a cause. The rights path sends RIGHTS; a committed charter save sends CHARTER for each affected AI member, with the member's current rightsStamp (read after the commit, in one query) and empty added/removed, so every kamoai pod drops that AI's charter and tool lists at once instead of serving a stale tool list. - A member-level save (S6) announces the member when it is an AI. - A role, department or job-title save, charter-only (S7-S9) or a full update carrying charterItems, announces every non-retired AI team member of the org, from one org-keyed query. - CharterProfile gains orgName, the spoken company name (OrgSpokenName: short name, else display name, else legal name), for the brief's identity line.

Your universe is waiting.

Start free in minutes. Add products whenever you are ready — no migration, no new logins.

Free forever — no credit card required