Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]¶
Added¶
Asset.aliases— curated per-plant synonyms. An optional list of alternative names an asset is known by on site (plant jargon, the local-language term, a shop-floor nickname), searched during entity resolution at the same authority asAsset.namerather than on the weak keyword tail. Language-neutral strings, not per-language fields. Declarable wherever assets come from: a;-delimitedaliasescolumn on the Excel / SQL / Generic-CMMS substrates (viadict_to_asset), analiaseskey in a JSON/YAML asset file (Plant.load_assets_from), or the constructor. List members are trimmed, de-duplicated case-insensitively, and blanks dropped —model_config'sstr_strip_whitespacedoes not reach into list fields, and a blank alias would substring-match every message.-
Answerable disambiguation. When a turn's resolution is ambiguous the runtime records the offered candidates for the conversation, so the next message can select one by ID, by name, by an IT/EN ordinal ("la seconda", "the second one"), or by a bare index. The store is keyed on
chat_idalone — recorded candidates are conversational memory, not a write authorization, so unlike_pending_actionsit does not require an identified sender and works on anonymous channels andAgent.ask(). A reply naming none, or more than one, of the candidates restates the question once and then abandons the entry rather than livelocking on a changed subject. -
READ_FAILURE_MODESconnector capability. Failure-mode catalogs are now a declared, typed capability instead of an undocumented private-attribute convention. The agent runtime harvests catalogs by discovering providers viafind_by_capability(Capability.READ_FAILURE_MODES)and awaiting each connector's publicread_failure_modes()— concurrently, deduped by code (first registration wins), with a provider that raisesConnectorErrorcontributing nothing instead of aborting the harvest. Both the workflowFailureAnalyzerand thediagnose_failuretool read the same single source. - Failure-mode sources on all three substrate connectors. Generic CMMS declares the capability when
failure_modes.jsonexists in the data dir (local mode only — REST mode never declares it until a REST fetch exists); the SQL connector maps a table/view viaentity: FailureMode; the Excel/CSV connector loads an optionalfailure_modessheet. Each declares the capability only when its source is actually configured. - Asset↔failure-code linkage across substrates. A
failure_modescolumn/cell on the asset source (semicolon-delimited codes, e.g."BEAR-WEAR-01;SEAL-LEAK-01") resolves intoAsset.failure_modesfor per-asset diagnosis precision. The encoding and the shareddict_to_failure_mode/split_list_cellbuilders live inconnectors/_entity_builders.py. Sample catalog atexamples/sample_data/failure_modes.csvmirrors the demo JSON.
Changed¶
- BREAKING (public API):
EntityResolver.resolve_bestis removed andResolvedEntity.confidenceis now required.resolve_bestwas dead code with no production callers. The confidence default was1.0, which made an entity constructed without a stated score maximally confident — every internal construction site already passes it explicitly, which is precisely why the fail-open default would have shipped unnoticed. External callers constructingResolvedEntity(asset)must now passconfidence=. - Resolution authority gate — ambiguity, anchoring, and a graduated posture. Confidence gating did not imply cardinality gating: several candidates tying at the top (canonically at 1.0) were resolved by taking
resolved[0]arbitrarily, and no confidence floor could catch it. The verdict is now derived in one fail-closed place (resolution_verdict) read by both the runtime gate and the prompt renderer, over a closed band partition (high ≥ 0.7,0.4 ≤ mid < 0.7,low < 0.4) where an indeterminable confidence classifies as not confident. An ambiguous or low-band turn withholds the committed asset and asks; a mid-band read states which asset was assumed; a tie atexact_idstays multiplicity ("compare P-201 and P-202") and still proceeds. - Writes are gated on the resolution verdict, not just the prefetch. Withholding
context["asset"]gated the prefetch and nothing else — write tools readasset_idfrom the model's own arguments.create_work_ordernow refuses, above the sandbox early-return and unconditionally, unless the target asset exists in the registry, is one the turn actually resolved (registry existence alone authorises nothing), and the band permits a write (ambiguous,low, and nothing-resolved all refuse). The verdict is stored alongside a deferred confirmation so a two-turn "sì" re-checks the verdict that authorised the proposal instead of finding none. - Asset IDs match only as whole tokens.
P-2no longer resolves insideP-201at confidence 1.0. IDs stay free-form and are matched literally with non-word lookarounds rather than against any ID grammar. - Docs corrected where they promised unimplemented behaviour. The starter kit's typo/abbreviation tolerance claim is withdrawn:
templates/odl-generator-from-text/prompts/entity_resolver_{it,en}.txtexist but no code loads them, and the resolver has no edit-distance matching at any stage. The synonym half is now genuinely true, viaAsset.aliases. The template README also flags that its workflow'sentity_resolver.resolvestep names an action the workflow engine does not dispatch. - BREAKING (connector authors): the runtime no longer probes the private
_failure_modesattribute. A third-party connector that exposed_failure_modeswithout declaringCapability.READ_FAILURE_MODESand implementing the public asyncread_failure_modes()silently stops contributing to diagnosis after this release — declare the capability and implement the method. dict_to_assetnow resolves afailure_modeskey (list or semicolon-delimited string) intoAsset.failure_modes; previously the key was silently dropped (it never reachedAsset.metadataeither).- SQL read errors are wrapped in
ConnectorError. An un-connectedGenericSqlConnectorread raisesConnectorError(was: rawAttributeError), non-transient driver errors are wrapped asConnectorError, and an invalid failure-mode row raisesConnectorSchemaErrorwith row context instead of leaking a pydanticValidationError. -
Excel sheet loads validate
coercenames at connect. An unknown named coercer (e.g.coerce: floatinstead oftype: float) now fails loudly withConnectorConfigErrorinstead of silently falling back to string coercion. Excel watcherrefresh()is now all-or-nothing: a mid-refresh load failure restores every cache to its pre-refresh snapshot. -
Human-in-the-loop write confirmation.
Agent.handle_messageandAgent.handle_message_fullaccept two new keyword params:confirmer(an optional async callable that renders a confirmation prompt and returns the user's yes/no decision) anduser_id(forwarded for cross-user confirmation scoping). A synchronous channel (e.g. CLI) supplies aconfirmerso a write is confirmed in-turn; async channels degrade to a two-turn propose→confirm flow. - Two-turn confirmation degrade for async channels. When no synchronous
confirmeris available andconfirmationsis on, a proposed write is NOT executed: the agent stores the pending action keyed(chat_id, user_id)and returns the confirmation question. The next inbound message for the same key either confirms (a bare affirmation executes the write and the agent narrates the outcome) or cancels (a decline OR any unrelated message). The affirmation/decline parse is deterministic — never delegated to the LLM. - Public
SupportsConfirmationprotocol plus the channel-author-facing helperssupports_sync_confirmation,is_affirmation,is_decline, and the token setsAFFIRMATION_TOKENS/DECLINE_TOKENS, exported frommachina.connectors.comms. A channel that implementsrequest_confirmation(chat_id, prompt) -> bool(now onCliChannel) advertises synchronous in-turn confirmation; channels that omit it use the two-turn degrade. - Index-based RAG citations. The agent surfaces a stable
[n]citation index for retrieved document chunks (consistent between pre-fetch context and thesearch_documentstool result), with a source/page fallback when the index cannot be resolved. - Output-authority gates in the agent runtime. The runtime now gates the completeness/validity/confidence of model and heuristic output instead of presenting it verbatim: a
list_assetsenumeration tool (read-only, registered underREAD_ASSETS); acompletenessflag onAgentResponse("complete"|"partial", default"complete") with a user-facing hedge when a turn is force-finalized; detection of leaked tool-call JSON in assistant content (raw JSON never shown; a leaked write is never auto-executed; a leaked read is re-entered, bounded); bounded self-correction on malformed tool arguments; withholding of low-confidence entity resolution (RESOLUTION_MIN_CONFIDENCE); andDiagnosisResult.failure_mode_for_write, a confidence-gated write-path accessor that fails closed (onlymedium/highyield a code). - Write-path hardening. SAP PM asset-scoped BOM reads are bounded (configurable equipment field or refuse the unbounded fetch; a
_MAX_ODATA_ROWScap on OData pagination); CSRF-token desync recovery is idempotency-safe (a non-idempotent write is never replayed); a workflow write step is never retried after a timeout or a post-apply exception; the sandbox write-detection heuristic is biased to over-gate and closes thepublish_messagegap; andauto_work_order_idaccepts an optionalsession_idscope (opt-in; the empty default reproduces the prior content-only digest byte-for-byte).
Changed¶
- BREAKING (install):
[docs-rag]vector stack migrated off deprecatedlangchain-communityclasses. The Chroma vector store now comes fromlangchain-chromaand the optional HuggingFace embedder wrapper fromlangchain-huggingface; the unused top-levellangchaindependency was dropped (document loaders stay onlangchain-community). Existing[docs-rag]installs must re-runpip install -U "machina-ai[docs-rag]"after upgrading — pip does not record extras, so a barepip install -U machina-aileaveslangchain-chromamissing and the connector silently degrades to keyword search (now with a WARNING naming the missing package and the exact remedy). - BREAKING (behaviour):
confirmationsnow defaults toTrueonAgent, and a newconfirmations:YAML config key mirrors it. Writes (mutating tool calls such ascreate_work_order/execute_workflow) now require human confirmation by default. A programmatic caller that wants autonomous writes must opt out withAgent(confirmations=False)(orconfirmations: falsein YAML) or pass aconfirmer. With confirmations on and no confirmer available, a mutating tool call is fail-safe: it is NOT executed (the two-turn degrade stores it for the next message).trigger_workflowis a deliberate direct-execution path guarded bysandboxonly — it is not gated byconfirmations. - Citation markers renormalized at egress.
AgentResponse.textnow carries numeric[n]markers renumbered to1..Nby first appearance in the prose (unresolvable markers are stripped), with a channel footer of the form[n] source:page.AgentResponse.citationsis now display-ordered:citations[n-1]corresponds to the inline[n]marker; citations referenced only in the<citations>block are appended after the inline ones. diagnose_failureresult shape widened. Eachprobable_failuresentry now carriesconfidence(numeric indicator-match ratio 0–1, distinct fromFailureAnalyzer's categorical confidence),matching_indicators, andrecommended_actions; token-overlap matching against the live failure-mode catalog is ranked by matched-indicator count, then ratio, and capped at the top 5. Empty results always carry an explanatorynote(unknown asset, no catalog, declared-modes/catalog mismatch, or nothing matched).DiagnosisResult.primary_codeis now display/read-only. It still returns the top-ranked code ungated, but is no longer the recommended write-path accessor — usefailure_mode_for_write(confidence-gated) when settingWorkOrder.failure_mode. The builtinalarm_to_workorderworkflow switched itsgenerate_work_orderstep from{analyze_alarm.primary_code}to{analyze_alarm.failure_mode_for_write}, so a low-confidence diagnosis now producesWorkOrder.failure_mode=Noneinstead of stamping an uncertain code. Custom workflows that copied the old binding should migrate to get the same gate.
[0.3.1] - 2026-06-05¶
Added¶
- Deterministic content-hash work-order IDs via a shared
auto_work_order_id(asset_id, wo_type, priority, description)helper, used by the agent runtime tool,WorkOrderFactory, and the MCP create tool. Re-creating the same logical work order (an alarm fired twice, a re-run workflow, or a model re-requesting the create tool) collapses to one ID the CMMS can dedup, instead of minting a fresh ID each call. Replaces the oldid(args)/uuid4/"NEW"schemes. - Per-turn memoisation of side-effecting tools in the agent LLM loop, sourced from a single
MUTATING_TOOLSregistry (create_work_order,execute_workflow). A model that re-requests the same write inside one turn reuses the first result; error results are not memoised. - Method-aware HTTP retry (
retry_on_network_error): POST/PATCH are no longer retried on network/timeout errors or 503 (timeout-after-success duplicate risk); GET/HEAD/OPTIONS/PUT/DELETE still are, and 429 is always retried. - Durable, atomic local persistence: local-mode
work_orders.jsonand Excel/CSV updates write to a temp sibling then atomically replace, so a crash mid-write can't truncate the file. Native local files reload losslessly viaWorkOrder.model_validate. Anasyncio.Lockserialises the local create/update + persist sequence. prompts.safe_textscrubs identity-/infra-revealing absolute paths (user-home dirs, UNC shares) from LLM-visible chunk content and workflow error/output strings, while preserving instructional system paths. Complements the existingsafe_sourcefor the metadata field.- Minimal golden-set RAG retrieval eval (
tests/integration/test_document_store_golden.py): a frozen(query, filter, expected)set over a fixed 3-manual corpus, turning the five observed retrieval failure modes into a repeatable regression tripwire. Gated on the[docs-rag-hybrid]stack. WorkflowContext.resolve_input_valuefor step-input resolution that preserves raw object types. The existingresolve()always returns astrviare.sub, which silently coerces complex outputs (dicts,WorkOrderinstances) to theirstrrepr. The new method returns the raw referenced value when the entire template is a single{key}placeholder, and falls back toresolve()for templates with surrounding text. This lets workflow steps pass complex objects between steps — e.g. theWorkOrderproduced bywork_order_factory.createnow flows intocmms.create_work_order(work_order=…)without coercion. Backward compatible: text-with-placeholder templates still produce strings.- Section-aware chunking + parent-document retrieval (
SectionAwareSplitter,ParentSection,MatchChunk). The splitter detects Markdown headings (fence-aware), numbered headings, and ALL-CAPS headings (the last two require blank-line context). Small match chunks feed embedding / BM25 / rerank; the LLM receives the full surrounding section so a multi-step procedure stays together. Oversized sections are windowed around the match using char offsets. - Layout-aware PDF/DOCX parsing via Docling (
LayoutAwareParser,ParsedDocument,Section,TableBlock) behind[docs-rag-parsing]extra. Tables are emitted as atomic chunks (DocumentChunk.is_table=True) that retrieval never splits mid-row. Per-file failures fall back toPyPDFLoader/Docx2txtLoader. Surfaces a[TABLE]tag informat_document_resultsso the LLM treats table results as structured rows. - Swappable embedder via
embedder=constructor param onDocumentStoreConnector(e.g."BAAI/bge-m3"for multilingual technical content). Falls back to Chroma's default on any failure. [docs-rag-pro]aggregator extra pullingdocs-rag + docs-rag-hybrid + docs-rag-rerank + docs-rag-parsing. Now referenced by[all].- Connector documentation at
docs/connectors/document-store.mdcovering metadata schema, sidecar / frontmatter, extras, embedder configuration, citation contract, and failure-mode table. examplesextra inpyproject.toml, pulling inpython-dotenv. The example preflight now loadsexamples/.envautomatically so users can keep API keys in a local file instead of exporting shell variables every session. The import is wrapped intry/exceptso users onmachina-ai[litellm]without the new extra are unaffected.- Shared CLI helper
examples/_mode.pyexposingadd_mode_flags()andresolve_sandbox(). Every example agent and theodl-generator-from-texttemplate now accept mutually exclusive--sandboxand--liveflags.--helpadvertises which mode is the default (LIVE forquickstart, SANDBOX everywhere else). - CLI consistency tests (
tests/unit/test_examples_mode_helper.py,tests/unit/test_template_mode_parity.py,tests/e2e/test_examples_cli_consistency.py). The e2e test auto-discovers everyagent.pyunderexamples/andtemplates/, so new examples are covered without manual registration.
Changed¶
- Excel/CSV
update_work_ordernow persists durably. Withwrite_modeconfigured, both.xlsxand.csvare rewritten from cache on update (previously CSV updates were cache-only and lost on restart). The log eventupdate_cache_onlywas renamedupdate_not_persisted. request_with_retrydefault behaviour for POST/PATCH changed: network/timeout errors and 503 are no longer retried for non-idempotent methods unless the caller opts in viaretry_on_network_error=True. This prevents timeout-after-success duplicate writes.DocumentStoreConnector.search()result semantics:DocumentChunk.contentnow carries the full parent section (after dedup-by-parent), not the small match passage. The match passage is still what was embedded and ranked; only the surface returned to the caller (and the LLM) changed. Callers that previously assumedcontentwas a short passage may need to adjust slicing logic. The chunk metadata still carries the deterministicchunk_idfor citation purposes. NewDocumentChunkfields:parent_id,start_offset,is_table(appended to preserve positional construction).predictive_pipelinedefault mode flipped from LIVE to SANDBOX for safety. Pass--liveto execute writes.quickstartkeeps LIVE as the default (Q&A is read-mostly), but the CLI now accepts--liveand--sandboxsymmetrically and the help text annotates which is the default.- Documentation:
examples/quickstart/README.mdnow shows the.envworkflow alongsideexport/$env:/setsyntax for bash, PowerShell, and CMD. Install command updated topip install "machina-ai[litellm,docs-rag,examples]".
Fixed¶
- Uniform sandbox enforcement across every external-mutation path.
@sandbox_awarenow guards commssend_message(Telegram/Slack/Email), MQTTpublish, calendarcreate_event/delete_event, and SQLupdate_work_order(CMMS already had it);CliChannelstays exempt (prints only). The MCP_runtime()helper — both the domain (mcp/tools.py) and vendor (mcp/tools_vendor.py) variants — re-establishes the per-request sandbox contextvar, which per-request MCP tasks do not inherit. This closed a vendor-tool bypass where a raw MaximohttpxPATCH (no@sandbox_awarebackstop) executed live in sandbox mode. The MCPsend_message"blocked" path is now live (was dead code). - No more duplicate writes. Beyond the deterministic IDs and per-turn memo (see Added), the generic CMMS connector is now idempotent on work-order ID in local mode (re-creating an existing ID returns the existing record).
- Persist-failure rollback. A failed local-mode persist now rolls back the in-memory create/update so the list never diverges from disk; orphan
.tmpfiles are cleaned up if an atomic replace fails. The Excel cache mutation moved inside the write lock. - CSV/Excel formula-injection neutralised on write (leading
= + - @→ apostrophe) and stripped on read for a lossless round-trip; the guard/strip pair is a true inverse so a literal'=valueis no longer corrupted. - Path leaks closed beyond the source field. Absolute paths embedded in chunk content text and in workflow error/output strings — both LLM-visible — are now scrubbed via
safe_text(user-home / UNC paths reduced to basename), not justDocumentChunk.source. MUTATING_TOOLS↔ runtime guard consistency is asserted by a test so a new write tool can't silently bypass the loop-level dedup.- Document source paths no longer leak into LLM responses.
DocumentChunk.sourceflowed verbatim into both the context-gathering payload and thesearch_documentstool result, so absolute paths likeC:\Users\foo\bar\manual.mdreached the LLM and surfaced in citations. The redaction inActionTracerprotected logs but not the LLM-visible payload. Sanitisation now happens at both runtime boundaries via a small_safe_sourcehelper that strips directory components from path-like strings while passing through opaque IDs and URLs.format_document_resultsalso calls it as defence in depth. A new Guideline 8 in the system prompt forbids disclosure of absolute paths, directory structures, database schemas, or system architecture as backstop. The rawchunk.sourceis preserved for non-LLM consumers (logs, traces). Agent.sandboxmutations now propagate to theWorkflowEngine. The engine was constructed insideAgent.__init__with a snapshot of the sandbox flag; mutatingagent.sandboxafterward (the pattern every example uses when--liveis passed) left the engine's copy stuck on the construction-time value. The CLI banner readMode: LIVEbut workflow logs showedsandbox=Trueand every write went throughsandbox_service/sandbox_connectorinterception.Agent.sandboxis now a@propertywith a setter that writes through toself._engine.sandbox— single mutation point, no behaviour change for the three existingif self.sandboxread sites.alarm_to_workorderbuilt-in workflow steps now consume upstream outputs.generate_work_orderandsubmit_work_orderhad noinputs={...}declaration, so the engine dispatched them with empty kwargs — sandbox logs showedinputs={}and live runs would have failed on missing arguments. The two steps now declare explicitinputsmappingasset_id,failure_mode(fromanalyze_alarmoutput via the new raw-passthrough behaviour described above),description(templated with alarm and asset IDs), andwork_order(the factory output flowing into the CMMS connector).- Unified
Agent.channelswith the connector registry (#31). Channels passed viaAgent(channels=[...])are now registered into theConnectorRegistry, so workflow steps dispatched viachannels.send_message(e.g.alarm_to_workorder.notify_technician) correctly route through them. Previously onlyconnectors=[...]was discoverable by capability-based dispatch, and channel-only agent configurations silently returned{"sent": False, "error": "No communication connector available"}. Channels passed to bothconnectors=andchannels=as the same instance are deduplicated by identity. - Sandbox now gates channel lifecycle (#31). With
sandbox=True,Agent.start()andAgent.stop()skipchannel.connect()/channel.disconnect(), soEmailConnectorno longer performs real SMTP logins and other channels (Slack, Telegram) no longer open outbound sockets in sandbox mode. - Example CLI conventions unified. Previously three examples accepted only
--sandbox(default LIVE), two accepted only--live(default SANDBOX), and--liveon the quickstart raised an argparse error. Every example agent and the template now accept both flags consistently. Preflight error messages route to stderr.
[0.3.0] - 2026-04-20¶
Added¶
- MCP Server — expose Machina connectors via Model Context Protocol. Supports
stdio(IDE integration) andstreamable-http(multi-client deployment) transports. Tools are auto-registered from connector capabilities. Includes resources (asset details, work orders, failure taxonomy) and pre-built prompts (diagnosis, preventive planning, history summary). - MCP Authentication — static bearer token auth with per-token client identity tracking (
MACHINA_MCP_TOKENS_JSON). PluggableTokenVerifierprotocol for Vault/AKV integration. - Typed Capability enum —
Capabilityenum replaceslist[str]for connector capabilities. Dual-accept registry preserves backward compatibility through v0.3.x. - Excel/CSV Connector (
ExcelCsvConnector) — read/write maintenance data from.xlsxand.csvfiles with YAML schema mapping and file watcher support. - SQL Connector (
GenericSqlConnector) — read from PostgreSQL, SQL Server, SQLite, DB2 with YAML table-to-entity mapping. - GenericCmms YAML Mapper — zero-Python entity mapping for any REST CMMS. Declarative field specs with coercers (
enum_map,regex_extract,datetime), reverse mapping for writes, and pluggable coercer registry. - ActionTracer v2 —
conversation_idfield groups traces by conversation. LLM cost tracking (prompt_tokens,completion_tokens,usd_cost,model). JSONL export with automatic secret redaction and summary truncation. - Docker deployment — multi-stage Dockerfile, docker-compose with Machina + ChromaDB + mock CMMS,
.env.examplewith all configuration variables documented. - systemd deployment — production-ready
machina.serviceunit with security hardening (ProtectSystem=strict,NoNewPrivileges, dedicated user/group). - Starter-kit template (
templates/odl-generator-from-text/) — clone-configure-deploy package: Italian free-text message → asset resolution → Work Order creation. Dual substrate (Excel / REST CMMS). Email + Telegram channels. 20 PMI-Italia sample assets. Ships an Italian entity-resolver prompt file describing typo/abbreviation/synonym handling — documentation only: no code loads it, and the shipped resolver has no typo tolerance (corrected retroactively; see Unreleased). - Deployment documentation — on-premise guide, Docker guide, uptime/resilience doc (16-combination behavior matrix), security doc (threat model for stdio/HTTP/DocumentStore/traces), scaling doc (why CPU autoscaling is wrong for LLM workloads), secrets management decision matrix.
- MCP documentation — setup, tools reference, resources, prompts, auth configuration.
- Observability documentation — action traces format, JSONL export, cost tracking and analysis.
- Connector documentation — Excel/CSV, SQL, GenericCmms YAML mapper guides.
- Migration guide — v0.2 → v0.3 checklist (5-minute upgrade path for custom connector authors).
Changed¶
- Connector
capabilitiesproperty type:list[str]→frozenset[Capability]. The old format is still accepted (dual-accept registry) but will be removed in v0.4. machina.mcp.MCPServerstub replaced by a real MCP server implementation (FastMCP-based).mkdocs.ymlnavigation expanded with MCP, Templates, Deployment, and Observability sections.- Top-level README updated with Starter Kit section.
Deprecated¶
list[str]capability format on connectors — migrate tofrozenset[Capability]before v0.4.MACHINA_MCP_TOKENS(comma-separated) — useMACHINA_MCP_TOKENS_JSONfor per-token client identity.
Removed¶
MCPServerNotImplementedErrorstub — replaced by real implementation.
Deferred to v0.3.1¶
- Kubernetes manifests, Helm charts, HPA configuration
- Conversation replay API (
ActionTracer.for_conversation()) - Alerting hooks (
ActionTracer.on_alert) - Templates: technician-chatbot, predictive-workflow
- MCP resource URI scheme promotion from pre-stable to stable
- OAuth 2.1 authorization server for MCP
- WhatsApp connector (pending Meta approval)
- MaintainX dedicated connector (GenericCmms YAML covers the use case)
[0.2.1] - 2026-04-15¶
A focused consolidation release between v0.2.0 and v0.3. No new features; the goal was an honest, stable base ahead of the MCP server layer work in v0.3. No public API removed.
Added¶
docs/roadmap.md— what ships in v0.2.1 and what's planned for v0.3 (MCP server,#31channels/registry unification, MaintainX/Limble/Fiix,AgentTeam, anomaly detection, plugin system, WhatsApp/Teams).docs/troubleshooting.md— short entries for the issues adopters hit most: LLM provider model strings, sandbox vs live mode, connector capability discovery, config-loader errors.- Loud stub for
machina.mcp.MCPServer— instantiation raisesNotImplementedErrorwith a pointer to the roadmap.import machina.mcpcontinues to work, reserving the import path across the v0.2 → v0.3 jump. EmailConnector— available as a communication connector for workflow notification. Seedocs/connectors/email.mdfor setup.- LiteLLM contract tests (
tests/unit/test_llm_provider.py::TestLiteLLMModelStringContract) — exercise the reallitellm.get_llm_providerparser, pinning theprovider:model → provider/modelnormalization introduced inb48f649and anchoring that the colon form keeps being rejected by LiteLLM. tests/validate_examples.pyconstruct check — now imports every runnableexamples/*/agent.pyso module-levelAgent(...)construction actually runs. Catches the "imports fine but blows up at first call" class of regression that produced the post-v0.2.0 reactive-fix cadence.- Per-module coverage floors in CI (agent 88%, config 95%, llm 95%, observability 85%, workflows 90%). Floors sit ~5% below the measured baseline; any silent regression in a core module now trips CI.
Changed¶
docs/mcp-server.mdwarning admonition — describes the new import-OK / instantiate-raises behaviour and links the newdocs/roadmap.md.- Test layout — contract tests live alongside fake-based tests in
tests/unit/test_llm_provider.py(one test file per source file, perCLAUDE.mdconvention).
Fixed¶
- No code fixes beyond the honesty cleanup above; v0.2.0 shipped stably and this release is scaffolding.
Deprecated¶
- None.
Removed¶
- None.
machina.mcpimport path is preserved; it was empty before and is a loud stub now, but still importable.
Notes¶
- A framework gap surfaced during consolidation: workflow notification steps resolve channels via the connector registry while
Agent(channels=[...])lives on a separate list, andsandbox=Truedoes not gatechannel.connect(). Tracked in #31 for v0.3.
[0.2.0] - 2026-04-11¶
Added¶
- Workflow Engine with trigger-step-action model, sequential execution,
template variable resolution (
{trigger.field},{step_name.field}), per-step error policies (RETRY, SKIP, STOP, NOTIFY), guard conditions, configurable timeouts, and action tracing - Sandbox mode for safe experimentation: write actions (create, update, delete, send) are logged but not executed; read-only actions still run normally. Sandbox enforced in both WorkflowEngine and Agent tool dispatch
alarm_to_workorderbuilt-in workflow — 7-step template from sensor alarm through diagnosis, spare part check, work order creation, technician notification, confirmation, and CMMS submissionSlackConnector— Slack integration via the Bolt SDK in Socket Mode (WebSocket-based, no public endpoint required). Supports channel whitelisting, bot-message filtering, and bidirectional messagingEmailConnector— Email integration with two backends:- Standard SMTP/IMAP (zero external dependencies, TLS/SSL support)
- Gmail API backend via OAuth2 (
pip install machina-ai[gmail]) - Polling-based inbox monitoring with persistent IMAP connections
CalendarConnectorwith three pluggable backends:- Google Calendar API v3 (OAuth2 + service account auth)
- Microsoft 365 / Outlook (MSAL client-credentials + Graph API)
- iCal
.icsfiles and URLs (read-only, with RRULE expansion) - Facade pattern with dynamic capabilities (read-only for iCal, full CRUD for Google/Outlook)
- Convenience methods:
get_production_schedule(),get_planned_downtime(),get_technician_availability() CalendarEvent,PlannedDowntime,ShiftPatterndomain entities withEventTypeenumOpcUaConnector— OPC-UA client for real-time sensor data with subscription-based monitoring, value-to-alarm conversion, and security policy support (None, Sign, SignAndEncrypt)MqttConnector— MQTT pub-sub with JSON, Sparkplug B, and raw payload support. Topic wildcards, TLS, and fan-out architecture for concurrent subscriptionsStep.is_writefield for explicit write-action marking, overriding the keyword-based heuristic in sandbox mode- Workflow
depends_onvalidation — the engine validates all step dependency references at execution start, raisingWorkflowErrorfor invalid references IncomingMessageandMessageHandlerextracted tomachina.connectors.comms.typesfor clean cross-connector imports- Phase 2 CMMS connectors:
SapPmConnector(SAP PM OData),MaximoConnector(IBM Maximo OSLC/JSON),UpKeepConnector(UpKeep REST v2) OAuth2ClientCredentialsauth strategy for SAP S/4HANA and other enterprise systems requiring OAuth2 machine-to-machine authSparePart.metadatafield to preserve connector-specific fields verbatim, consistent withAsset.metadataandWorkOrder.metadata- Shared HTTP retry helper (
machina.connectors.cmms.retry.request_with_retry) with exponential backoff on 429 / 503 responses (honouring numericRetry-Afterheaders) and transient network errors (httpx.TimeoutException,httpx.ConnectError,httpx.ReadError). All HTTP calls inSapPmConnector,MaximoConnector, andUpKeepConnectornow route through it. SapPmConnector.__init__acceptsbom_service,bom_entity_set,bom_material_field,bom_equipment_fieldto configure the BOM OData endpoint per SAP version. Defaults targetAPI_BILL_OF_MATERIAL_SRV/BillOfMaterialItem(standard S/4HANA Cloud).MaximoConnector.__init__acceptsasset_type_map: dict[str, AssetType]that maps Maximoclassstructureid(orassettype) values to MachinaAssetType. Without the map the connector falls back to the historical default ofROTATING_EQUIPMENT.get_work_order(id)single-record fetch for all three CMMS connectors (follows the existingget_asset()pattern).update_work_order(id, *, status, assigned_to, description)via PATCH for all three CMMS connectors, with keyword-only args for partial updates.close_work_order(id)andcancel_work_order(id)convenience wrappers on all three connectors — delegate toupdate_work_orderwith the appropriateWorkOrderStatustransition.read_work_orders(status=WorkOrderStatus)now accepts a MachinaWorkOrderStatusenum (automatically reverse-mapped to native CMMS code) in addition to raw status strings, for all three connectors.- Failure-mode mapping:
SapPmConnectornow extractsMaintenanceActivityType→WorkOrder.failure_modeandMaintenanceCause→WorkOrder.failure_cause;MaximoConnectorextractsfailurecode→failure_modeandfailureremark→failure_cause.
Security¶
- Secret redaction in structured logs — fields matching
token,password,secret,api_key,client_secret,authorizationare automatically replaced with***REDACTED*** - Input length limit —
Agent.handle_message()truncates messages exceeding 10,000 characters with a warning log - Prompt hardening — system prompt now includes guideline rejecting instruction override attempts and role changes
- Sandbox enforcement —
Agent._tool_create_work_order()now respects sandbox mode (previously bypassed) - Insecure connection warnings — OPC-UA and MQTT connectors log warnings when security/TLS is disabled
- Dependabot configured for weekly pip and GitHub Actions vulnerability scanning
.gitignoreexpanded to block*.pem,*.key,credentials.json, and client secret files- Auth docstring examples updated to use
os.environ[]instead of hardcoded secrets
Fixed¶
- OPC-UA task reference leak —
_DataChangeHandlernow tracks background tasks in asetwithadd_done_callbackto prevent garbage collection of in-flight tasks under rapid data changes - MQTT shared iterator bug — replaced per-subscription
_message_loopwith a single_reader_loopfan-out architecture, preventing competing consumers on theaiomqtt.Client.messagesasync generator - Guard condition exceptions now logged with
exc_info=Trueinstead of being silently swallowed - Outlook Calendar token refresh — MSAL app instance is now stored
and tokens are refreshed via
acquire_token_silent()before each API call, preventing failures after the initial 1-hour token expiry - IMAP connection reuse —
EmailConnectornow maintains a persistent IMAP connection across poll cycles with automatic reconnection on failure, reducing TCP/TLS handshake overhead SapPmConnectorCSRF token flow:_fetch_csrf_tokenreplaced by_write_with_csrfwhich performs the CSRF fetch and the write (POST/PATCH) within a singlehttpx.AsyncClientcontext, sharing session cookies. The previous implementation used separate HTTP sessions, which caused SAP to reject the CSRF token with 403 on most configurations.MaximoConnector._parse_spare_partandUpKeepConnector._parse_spare_partnow preserve unknown fields inSparePart.metadata(previously dropped)UpKeepConnector._parse_spare_partnow preferspartNumber/barcodeas the SKU, falling back to the UpKeep recordidonly when neither is available — the previous implementation conflated internal record IDs with physical part identifiersUpKeepConnectorpriority mapping corrected to match the UpKeep REST API v2 0-indexed scale (0 = lowest, 3 = highest). Previously used an off-by-one 1-4 scale which mislabelled every work order's priority.SapPmConnector.read_spare_partspreviously pointed at the non-existentAPI_EQUIPMENT/EquipmentBOMentity set. The default now targetsAPI_BILL_OF_MATERIAL_SRV/BillOfMaterialItem; users on legacy SAP versions can override via constructor parameters.
Changed¶
- BREAKING:
MaximoConnector.read_spare_partsandUpKeepConnector.read_spare_partsno longer accept theasset_idparameter. The previous implementation filtered onSparePart.compatible_assets, which was never populated by either parser — the feature was silently returning empty lists.SapPmConnector.read_spare_partsretainsasset_idbut the filter is now routed through the configurablebom_equipment_fieldand is silently dropped (with a warning log) when no equivalent field exists on the configured BOM entity set. - BREAKING:
UpKeepConnector_reverse_priority/_UPKEEP_PRIORITY_MAPswitched from a 1-4 scale to the correct 0-3 scale. Callers that were constructing raw UpKeep payloads assuming the old scale must update their code; callers going throughPriorityenums are unaffected.
[0.1.1] - 2026-04-07¶
Added¶
.zenodo.jsonmetadata for automatic Zenodo DOI generationCITATION.cfffor academic citation
[0.1.0] - 2026-04-07¶
Added¶
- Project scaffolding: pyproject.toml, CI, linting, testing setup
- Core domain model:
Asset,WorkOrder,FailureMode,SparePart,Alarm,MaintenancePlan,Plant BaseConnectorprotocol andConnectorRegistry- Exception hierarchy (
MachinaErrorand subclasses) - Configuration system with YAML and environment variable support
- LLM abstraction layer (LiteLLM wrapper) with function-calling tool definitions
- Structured logging with structlog and action tracing (
ActionTrace) GenericCmmsConnectorfor JSON/CSV-based CMMS integrationTelegramConnectorfor maintenance notifications and commandsDocumentStoreconnector with RAG (ChromaDB + LangChain document loaders)Agentruntime with domain-aware prompting, tool dispatch, and conversation loopEntityResolverfor natural language → Asset/WorkOrder resolutionFailureAnalyzer,WorkOrderFactory,MaintenanceSchedulerdomain servicesknowledge_agentquickstart example- 306 unit tests, 98% coverage