Conversation
Users expect done to halt everything, but it's a loop break — execution continues after the loop block. Add examples showing var capture pattern and bail as the full-halt alternative. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate from @mlld/claude-poll (positional args) to @mlld/claude 3.0
(config object pattern):
- import { @claudePoll } from @mlld/claude-poll → @mlld/claude
- @claudePoll(@p, "sonnet", "@root", @tools, @out) → @claudePoll(@p, { model: "sonnet", tools: [...], poll: @out })
- Tool permission strings → arrays
- Wrapper exes simplified to pass-through config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds HandleEvent dataclass and next_event() method to _BaseHandle, enabling consumers to receive state:write events one at a time during execution rather than buffering until completion. This is the foundation for the AgentDojo tool-call protocol where mlld writes tool requests to state:// and Python injects results via update_state(). Also includes the _decode_state_write_value fix for structured values and three new integration tests covering the roundtrip, simple completion, and structured value preservation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Callers can now pass labels (e.g., ["untrusted"]) through state:update requests. Labels propagate into mlld's taint tracking via stateLabels on the root environment. Python SDK exposes the labels parameter on update_state() and surfaces the security dict on StateWrite events. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…preserve field access metadata Hard policy denials inside when arms were silently caught as soft guard denials. Per-field payloadLabels option added across TS/Python SDK and live transport. Field access on object variables now carries security descriptors through nested traversal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract resolveObjectMethod helper that properly handles AST entry-based objects, legacy properties-based objects, and plain objects without false-matching user data. Unwrap Variable/StructuredValue after accessFields in namespace method resolution path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When resolving methods on namespace objects, resolveObjectMethod could return native JS functions (e.g., Array.prototype.includes) from the prototype chain. These were incorrectly treated as namespace-exported executables, bypassing the builtin handler. Now defers to the builtin handler when the resolved value is a native function and the method name collides with a builtin, while still allowing namespace-exported mlld executables with the same names to take priority. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update guard, policy, and security docs to cover two new features: - @mx.args for named operation input access in guards and denied handlers - Privileged guard override of managed label-flow policy denials with locked: true opt-out Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GuardOpIdentifier only supported dot-separated segments, so `op:tool:w` failed to parse. Added GuardOpSegment rule to accept optional colon suffixes (matching DataLabelIdentifier convention). Also added a validator warning when a guard uses a bare data filter (e.g., `guard before tool:w`) that matches a known operation label, suggesting the `op:` prefix to avoid the silent scope mismatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Guards now support function filters: `guard before @send_email` targets a specific exe by name, paralleling hook `@fnName` syntax. Hooks now match `op:` filters against operation labels (not just built-in types like exe/var/run), so `hook before op:tool:w` works the same way as `guard before op:tool:w`. Removed the "unknown operation type" warning since custom labels are valid targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lazy-load the TypeScript AST extractor so npm install -g mlld works without a global typescript package. Defer Python package manager detection to first @py/ resolve. Show actionable ENOENT messages in py/bash/node executors instead of raw spawn errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds mcpServers (map of logical name → shell command) to execute() and process() across TypeScript SDK, Python SDK, and live stdio transport. import tools from mcp "name" resolves against the map before treating the spec as a command, enabling parallel executions with independent MCP server instances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Coerce MCP tool arguments to match inputSchema types at the call boundary: string→array wrapping, string→number/boolean/null parsing, JSON string→object/array. Uses schema type info already available from tools/list. When calling MCP tools with variable-reference arguments whose names match schema property names, match by name instead of position. Exe wrappers can declare params in any order and mlld routes them to the correct MCP properties. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends fake-server.cjs with typed tools (create_event, type_mirror) that echo back arg types and values. Tests verify coercion (string→ array/integer/boolean) and named-arg matching through the full stack: McpImportManager → McpImportService → coerceMcpArgs → MCP server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exe wrappers that delegate to their MCP counterpart (e.g. @sendEmail → @mcp.sendEmail) were rejected as self-recursive because the recursion guard compared unqualified method names, ignoring the namespace prefix. Skip recursion tracking when the call resolves through a namespace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Namespace variables (`@mcp` with `isNamespace: true`) lost their flag
when serialized into a module's capturedModuleEnv. Imported exe wrappers
that delegate to `@mcp.toolName()` then hit false circular-reference
detection because the recursion guard couldn't identify the namespace-
qualified call. Add `__namespace` serialization marker (paralleling
`__executable`/`__template`) so the flag survives round-trip.
Propagate action errors from matched when-conditions instead of
silently collecting them and falling through to `*`. Previously a
matched branch whose action threw (e.g. CircularReferenceError) was
caught, pushed to an errors array, and the next condition was tried —
making real failures look like "no case matched."
Harden MCP arg coercion:
- Handle `anyOf`/`oneOf` nullable schemas (`anyOf: [{type: array},
{type: null}]`) by extracting the non-null type
- Strip null, undefined, and string "null" from MCP payloads so
servers use schema defaults for optional params
- Coerce empty strings to `[]` for array-typed params
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expose @mx.llm.native so exe llm modules can suppress built-in CLI tools when config.tools contains only exe refs. Previously only the inBox path passed --tools "", leaving 25+ native tools visible to the model alongside the intended MCP-bridged tools. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
llmxml's getSection() parsed markdown into an AST and re-serialized it, which dropped inline code spans. Replaced all four call sites with a text-based extractor that preserves content verbatim. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
formatBlockingAnalysisErrors only checked result.errors but hasBlockingAnalysisErrors also flags reserved-conflict redefinitions as blocking. The redefinition details were silently dropped, producing "Pre-flight validation failed:" with no explanation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract coercion logic to shared core/mcp/coerce.ts and apply it in all three MCP dispatch paths: imported tools, config-spawned tools, and the function bridge (LLM native tool calls). Adds two new coercions: empty string → omit for nullable/optional params, and comma-separated string → array split. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidates syntax highlighting, EXAMPLES.md, and llms docs into a single workflow that rebuilds all generated files on push to main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove docs/llm/*.txt, llms-combined.txt, and tests/cases/EXAMPLES.md from tracking. These are rebuilt by the build-generated workflow on push to main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enables @arr[*].field to extract a field from every array element, producing a flat array. Combines with .includes() for position-independent membership checks like @mx.tools.history[*].name.includes("verify"). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates mlld to v2.0.5 by expanding security provenance (tool lineage + audit IDs), adding filesystem integrity (sign-on-write / verify-on-read) with new mlld sign / mlld status commands, and improving guard/policy ergonomics (named guard args, new built-in policy rules, richer validation/analyze surfaces).
Changes:
- Add filesystem integrity policy fields (
signers,filesystem_integrity), file signing helpers, and CLI/RPC surfaces (sign,status,fs:status). - Add tool provenance tracking (
@mx.tools.callsvs@mx.tools.history), toolCall audit events with stable IDs, and propagate provenance via variable metadata. - Improve authoring/validation: named guard args (
@mx.args.*), tolerant comparison (~=), wildcard projection ([*]), and context-awaremlld_validate/mlld_analyze.
Reviewed changes
Copilot reviewed 134 out of 502 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/user/security.md | Document filesystem integrity behavior, policy fields, and @mx.sig inspection. |
| docs/user/sdk.md | Document Python fs_status() for integrity inspection. |
| docs/user/reference.md | Clarify done loop semantics in reference docs. |
| docs/user/flow-control.md | Clarify done vs bail semantics with examples. |
| docs/user/cli.md | Add mlld status docs and list fs:status RPC method. |
| docs/src/atoms/security/13-patterns--airlock.md | Clarify execution-level vs value-level tool history in enforcement guard. |
| docs/src/atoms/security/12-patterns--audit-guard.md | Update dates and document calls vs history rationale. |
| docs/src/atoms/security/11-audit-log--tool-call-tracking.md | Document @mx.tools.calls vs @mx.tools.history and wildcard projection usage. |
| docs/src/atoms/security/10-audit-log--basics.md | Document stable audit IDs and toolCall ledger fields and provenance pointers. |
| docs/src/atoms/security/07-mcp-security--guards.md | Document @mx.args, @mx.tools.history, and after-guard output taint behavior. |
| docs/src/atoms/security/06-mcp-security--policy.md | Clarify hard capability denials vs managed label-flow denials + locked. |
| docs/src/atoms/security/05-mcp-security--basics.md | Document .mx.tools provenance on MCP outputs and request/response taint semantics. |
| docs/src/atoms/security/04-signing--autosign-autoverify.md | Explain calls vs history distinction. |
| docs/src/atoms/security/01-security-getting-started.md | Add task-scoped authorization docs and clarify denial handling + influenced sources. |
| docs/src/atoms/sdk/07-sdk--language-sdks.md | Document boundary labels across SDKs. |
| docs/src/atoms/sdk/06-sdk--payload.md | Document per-field payload labels and Python helpers. |
| docs/src/atoms/sdk/03-sdk--state.md | Document labeled state updates across SDKs. |
| docs/src/atoms/sdk/02-sdk--execute.md | Add payload label + MCP server injection docs. |
| docs/src/atoms/modules/22-llm-modules.md | Add @mx.llm.native and guidance for suppressing built-in tools. |
| docs/src/atoms/mcp/05-mcp--import.md | Document arg coercion, name-based arg matching, and SDK MCP server injection. |
| docs/src/atoms/mcp/03-mcp--tool-collections.md | Document var tools @t = mcp @expr runtime discovery. |
| docs/src/atoms/mcp/02-mcp--export.md | Clarify src:mcp applies to returned values, not inputs. |
| docs/src/atoms/flow-control/18-loop.md | Clarify done vs bail semantics with examples. |
| docs/src/atoms/flow-control/07-when--operators.md | Document tolerant comparison operators ~= / !~=. |
| docs/src/atoms/effects/17-guards--denied-handlers.md | Document denied handlers catching managed label-flow denials and @mx.args usage. |
| docs/src/atoms/effects/15-guards--privileged.md | Document privileged allow overriding managed label-flow denials and locked. |
| docs/src/atoms/effects/14-guards--composition.md | Clarify precedence/override rules for privileged allow vs managed denials. |
| docs/src/atoms/effects/13-guards--basics.md | Document @mx.args semantics and per-arg label inspection examples. |
| docs/src/atoms/effects/11-labels--tracking.md | Clarify union labels on arrays/objects with examples. |
| docs/src/atoms/effects/08-labels--influenced.md | Expand influenced label semantics beyond prompt text to config/messages/system. |
| docs/src/atoms/effects/07-labels--trust.md | Document override pattern + locked interaction. |
| docs/src/atoms/core/26-builtins--methods.md | Document wildcard projection syntax [*]. |
| docs/src/atoms/config/13-box--blocks.md | Document @mx.llm.native. |
| docs/src/atoms/config/08-policy--composition.md | Document merge semantics for authorizations. |
| docs/src/atoms/config/07-policy--label-flow.md | Document privileged overrides for managed denials and link to authorizations. |
| docs/src/atoms/config/06-policy--operations.md | Document hierarchical risk labels and link to authorizations. |
| docs/src/atoms/config/04-policy--basics.md | Document built-in positional rules, locked, and authorizations. |
| docs/src/atoms/cli/06-mcp-dev.md | Expand MCP dev tools schemas/outputs and add context-aware validation/analyze. |
| docs/src/atoms/cli/05-live-stdio.md | Document mcpServers injection for live stdio. |
| docs/src/atoms/cli/03-validate.md | Document --deep, --context, richer JSON output, and new warnings. |
| docs/llm/llms-reference.txt | Remove older per-topic LLM reference file (generated docs workflow change). |
| docs/llm/llms-overview.txt | Remove older overview LLM reference file (generated docs workflow change). |
| docs/llm/llms-output.txt | Remove older output LLM reference file (generated docs workflow change). |
| docs/llm/llms-mcp.txt | Remove older MCP LLM reference file (generated docs workflow change). |
| docs/llm/llms-core-rules.txt | Remove older “core rules” LLM reference file (generated docs workflow change). |
| docs/dev/SECURITY.md | Document signers / filesystem_integrity and policy merge semantics. |
| docs/dev/GUARD-ARGS.md | Add developer spec for @mx.args guard arg access and reserved behavior. |
| core/types/when.ts | Add normalizeWhenCondition() helper for evaluator consistency. |
| core/types/variable/VariableTypes.ts | Add tools provenance to variable context type. |
| core/types/variable/VariableMetadata.ts | Preserve .tools when generating metadata from descriptors. |
| core/types/variable/VarMxHelpers.ts | Round-trip .tools between mx and security descriptors + serialization. |
| core/types/var.ts | Add McpToolSourceValue to AST value unions. |
| core/types/tools.ts | Add controlArgs metadata to tool definitions. |
| core/types/security.ts | Add ToolProvenance and descriptor merge/serialize/normalize support. |
| core/types/primitives.ts | Add wildcard field access node and expand binary operators set. |
| core/types/output.ts | Add OutputSourceData variant to output sources union. |
| core/types/guard.ts | Extend guard filter kind union (adds 'function'). |
| core/security/sig-service.test.ts | Add tests for signing/verifying and caching behavior with injected FS. |
| core/security/sig-adapter.ts | Add createSigContextWithFS() for non-Environment callers. |
| core/security/index.ts | Re-export sig service, identity resolution helpers, and new context creator. |
| core/security/identity.ts | Add signer identity resolution (user/agent/system) with config/git/env fallbacks. |
| core/security/identity.test.ts | Add tests for identity resolution precedence and derivation. |
| core/security/AuditLogger.ts | Add stable audit IDs + toolCall fields; return generated ID from append. |
| core/security/AuditLogger.test.ts | Test audit ID generation and toolCall event payload fields. |
| core/resolvers/ResolverManager.ts | Improve lockfile/cache behavior for versioned module refs. |
| core/resolvers/ResolverManager.cache.test.ts | Add tests for versioned cache bypass/refresh behavior. |
| core/resolvers/PythonPackageResolver.ts | Lazy-init package manager to avoid eager default selection. |
| core/resolvers/DynamicModuleResolver.ts | Add per-field boundary labels for object dynamic modules + setter API. |
| core/resolvers/DynamicModuleResolver.test.ts | Add tests for per-field labels serialization and updates. |
| core/policy/union.test.ts | Add tests for sticky locked and normalization/merge of integrity fields. |
| core/policy/label-flow.ts | Add positional “known” rules (no-send-*, no-destroy-*) + helpers. |
| core/policy/label-flow.test.ts | Add tests for new positional “known” rules. |
| core/policy/guards-defaults.test.ts | Expand defaults-rule guard generation tests for new built-ins. |
| core/policy/builtin-rules.ts | Add new built-in rule names for positional checks + influenced advice. |
| core/policy/authorizations.test.ts | Add tests for authorization normalization/merge/validation/runtime decisions. |
| core/mcp/coerce.ts | Add MCP inputSchema-based arg coercion helper. |
| core/mcp/coerce.test.ts | Add tests covering coercion and schema parsing. |
| core/highlighting/rules.ts | Update syntax highlighting tokens for operators/directives/keywords. |
| core/errors/enhanced-error-display.test.ts | Add test verifying heavy internal state is sanitized in JSON errors. |
| core/errors/MlldParseError.ts | Sanitize error details in parse error JSON. |
| core/errors/MlldFileSystemError.ts | Sanitize error details in filesystem error JSON. |
| core/errors/MlldError.ts | Sanitize error details in base error JSON. |
| core/errors/MlldError.test.ts | Add tests for error detail sanitization. |
| core/errors/GuardError.ts | Distinguish capability vs label-flow policy denial codes. |
| cli/utils/state-parser.ts | Add --state parsing to build @state object from file/JSON/KEY=VALUE. |
| cli/utils/state-parser.test.ts | Add tests for file + merge behavior for state parsing. |
| cli/parsers/ArgumentParser.ts | Add sign/status commands and parse --state flag. |
| cli/parsers/ArgumentParser.test.ts | Add tests for --state parsing variants. |
| cli/mcp/FunctionRouter.ts | Propagate conversation security descriptor and tool provenance into tool calls. |
| cli/mcp/FunctionRouter.test.ts | Add tests for provenance propagation and toolCall audit logging. |
| cli/mcp/BuiltinTools.ts | Add context-aware mlld_validate / mlld_analyze support for inline code. |
| cli/mcp/BuiltinTools.test.ts | Add tests for new schemas and context-aware validation/analyze output. |
| cli/interaction/HelpSystem.ts | Update help text for validate and add --state documentation. |
| cli/index.ts | Set default signing context tier to user for CLI executions. |
| cli/execution/FileProcessor.ts | Merge --state into @state, add signing context, and surface denials in JSON. |
| cli/execution/FileProcessor.integration.test.ts | Validate CLI interpret passes signing context. |
| cli/execution/CommandDispatcher.ts | Register new sign and status commands. |
| cli/execution/CommandDispatcher.test.ts | Add test verifying status command registration. |
| cli/commands/status.ts | Implement mlld status to report signature state and signer-derived labels. |
| cli/commands/status.test.ts | Add tests for status outputs, glob filtering, JSON, and taint printing. |
| cli/commands/sign.ts | Implement mlld sign for files/globs with resolved user identity. |
| cli/commands/sign.test.ts | Add tests for signing and re-sign reporting. |
| cli/commands/run.ts | Improve blocking validation output (include hard redefinitions) and add signing context. |
| cli/commands/live.ts | Document guard_denial events in live stdio help header. |
| cli/commands/live-stdio-security.ts | Add live signing/verification helpers + execution file writer with provenance. |
| cli/commands/live-stdio-security.test.ts | Add tests for live signing, sign-content, and execution file writes/provenance. |
| cli/commands/info.ts | Add docs URL handling for directory modules and lazy-load heavy deps in fetchSection. |
| cli/commands/info.test.ts | Add tests for source/docs URL construction across module source kinds. |
| cli/commands/docs.ts | Prefer docs URL when fetching docs/tldr sections. |
| cli/commands/docs.test.ts | Add test ensuring docsUrl is used for directory modules. |
| .github/workflows/build-syntax.yml | Remove old syntax-only generator workflow. |
| .github/workflows/build-generated.yml | Add unified workflow to rebuild/commit generated files (syntax/fixtures/docs). |
Comments suppressed due to low confidence (1)
cli/index.ts:1
- The new
signingContextoption is being passed viainterpretOptions as any. This weakens type safety and makes it easier for option regressions to slip in unnoticed. It would be better to extend theinterpret()options type (andexecute()where applicable) to includesigningContextso callers don’t need unsafe casts.
import * as path from 'path';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 'no-send-to-external', | ||
| 'no-destroy-unknown', | ||
| 'no-untrusted-destructive', | ||
| 'no-untrusted-privileged' |
There was a problem hiding this comment.
hasManagedPolicyLabelFlow() uses LABEL_FLOW_BUILTIN_RULES to detect whether managed label-flow can occur, but no-influenced-advice (added in core/policy/builtin-rules.ts and implemented in checkBuiltinPolicyRules) is missing from this set. This can cause the system to misclassify a policy as not having managed label-flow when only no-influenced-advice is enabled. Add 'no-influenced-advice' to LABEL_FLOW_BUILTIN_RULES (or derive the set from BUILTIN_POLICY_RULES) to keep detection consistent.
| 'no-untrusted-privileged' | |
| 'no-untrusted-privileged', | |
| 'no-influenced-advice' |
| if (value === null || value === undefined) { | ||
| continue; | ||
| } | ||
| if (typeof value === 'string' && value.trim() === 'null') { | ||
| continue; | ||
| } | ||
| if (typeof value === 'string' && value.trim() === '') { | ||
| const schemaType = paramTypes[key]; | ||
| if (schemaType === 'array') { | ||
| result[key] = []; | ||
| continue; | ||
| } | ||
| if (paramNullable[key] || !requiredSet.has(key)) { | ||
| continue; | ||
| } | ||
| } | ||
| const schemaType = paramTypes[key]; |
There was a problem hiding this comment.
coerceMcpArgs() currently drops the literal string "null" for all params, including those whose schema type is string. This can unintentionally erase valid user/tool input (e.g., a subject/body that is literally "null"). Consider restricting this sentinel handling to non-string schema types and/or to params that are nullable/optional in the schema, while preserving "null" when the declared type is string.
| if (value === null || value === undefined) { | |
| continue; | |
| } | |
| if (typeof value === 'string' && value.trim() === 'null') { | |
| continue; | |
| } | |
| if (typeof value === 'string' && value.trim() === '') { | |
| const schemaType = paramTypes[key]; | |
| if (schemaType === 'array') { | |
| result[key] = []; | |
| continue; | |
| } | |
| if (paramNullable[key] || !requiredSet.has(key)) { | |
| continue; | |
| } | |
| } | |
| const schemaType = paramTypes[key]; | |
| const schemaType = paramTypes[key]; | |
| if (value === null || value === undefined) { | |
| continue; | |
| } | |
| if (typeof value === 'string') { | |
| const trimmed = value.trim(); | |
| // Treat the string "null" as an omitted value only for non-string params | |
| // that are nullable or optional. Preserve "null" for string-typed params. | |
| if ( | |
| trimmed === 'null' && | |
| schemaType && | |
| schemaType !== 'string' && | |
| (paramNullable[key] || !requiredSet.has(key)) | |
| ) { | |
| continue; | |
| } | |
| if (trimmed === '') { | |
| if (schemaType === 'array') { | |
| result[key] = []; | |
| continue; | |
| } | |
| if (paramNullable[key] || !requiredSet.has(key)) { | |
| continue; | |
| } | |
| } | |
| } |
| export async function appendAuditEvent( | ||
| fileSystem: IFileSystemService, | ||
| projectRoot: string, | ||
| event: AuditEvent | ||
| ): Promise<void> { | ||
| ): Promise<string> { | ||
| const id = event.id ?? randomUUID(); |
There was a problem hiding this comment.
appendAuditEvent() now returns Promise<string> (generated ID) instead of Promise<void>, which is a behavioral/typing change for a widely reusable utility. If external/internal callers relied on the previous void return type, this becomes a breaking change. Consider either (1) keeping appendAuditEvent returning Promise<void> and adding a separate helper to generate IDs, or (2) providing an overload / new function name (e.g., appendAuditEventWithId) while keeping the original signature for compatibility.
|
|
||
| if (rawArg.startsWith('@')) { | ||
| const filePath = path.resolve(basePath, rawArg.slice(1)); | ||
| const content = await fileSystem.readFile(filePath, 'utf8'); |
There was a problem hiding this comment.
parseStateArg() calls IFileSystemService.readFile(filePath, 'utf8'). Elsewhere in the repo, IFileSystemService.readFile is used without an encoding argument, and many filesystem abstractions intentionally diverge from Node's signature. If IFileSystemService doesn't accept an encoding parameter, this will be a compile-time error (or a runtime error for non-Node implementations). Prefer calling readFile(filePath) and ensuring it returns a string, or update IFileSystemService to formally support the encoding argument across implementations.
| const content = await fileSystem.readFile(filePath, 'utf8'); | |
| const rawContent = await fileSystem.readFile(filePath); | |
| const content = typeof rawContent === 'string' ? rawContent : String(rawContent); |
| const deduped: ToolProvenance[] = []; | ||
| const seenAuditRefs = new Set<string>(); | ||
|
|
||
| for (const value of values) { | ||
| const normalized = freezeToolProvenanceEntry(value); | ||
| if (!normalized) { | ||
| continue; | ||
| } | ||
| if (normalized.auditRef) { | ||
| if (seenAuditRefs.has(normalized.auditRef)) { | ||
| continue; | ||
| } | ||
| seenAuditRefs.add(normalized.auditRef); | ||
| } | ||
| deduped.push(normalized); | ||
| } |
There was a problem hiding this comment.
Tool provenance deduplication currently happens only when auditRef is present. If some tool provenance entries omit auditRef (e.g., synthetic steps, older data, or partial ingestion), repeated merges can accumulate duplicates and increase descriptor size. Consider also deduping no-auditRef entries by a stable key such as name + JSON.stringify(args) (or ensuring auditRef is always populated at the source).
| private buildToolCallSecurityDescriptor(): SecurityDescriptor | undefined { | ||
| if (!this.conversationDescriptor) { | ||
| return undefined; | ||
| } | ||
| const policyEnforcer = new PolicyEnforcer(this.environment.getPolicySummary()); | ||
| return ( | ||
| policyEnforcer.applyOutputPolicyLabels(this.conversationDescriptor, { | ||
| inputTaint: descriptorToInputTaint(this.conversationDescriptor), | ||
| exeLabels: ['llm'] | ||
| }) ?? this.conversationDescriptor | ||
| ); | ||
| } |
There was a problem hiding this comment.
buildToolCallSecurityDescriptor() constructs a new PolicyEnforcer on every tool call. If MCP routing is hot (many tool calls per execution), this repeated instantiation can become avoidable overhead. Consider caching a PolicyEnforcer instance on the router (and refreshing only if policy summary changes) or passing an already-constructed enforcer via options.
…rg, and null-string coercion for string params Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added
[*]for array element projection:@arr[*].fieldextracts a field from every element, producing a flat array. Works anywhere arrays appear — variables, guard expressions, template interpolation. Combines naturally with.includes()for membership checks like@mx.tools.history[*].name.includes("verify")..mlld/sec/audit.jsonlnow assigns a stableidto every event, exe/native tool invocations emittoolCallrecords with args/timing/result summaries, result values carry.mx.toolslineage with audit references, and guards now expose that lineage as@mx.tools.history.var tools @name = mcp @exprnow builds a first-classToolCollectiondirectly from runtime-discovered MCP server tools, complementing the existingimport tools from mcp "..."flow.mcp_serversoption onexecute()andprocess(): maps logical names to MCP server commands per-execution.import tools from mcp "name"resolves against the map before treating the spec as a shell command. Enables parallel executions with independent MCP server instances. Supported in TypeScript, Python, and live stdio transport.inputSchematypes. String-to-array wrapping ("x"→["x"]), string-to-number/integer, string-to-boolean, string-to-null, and JSON string parsing for object/array types. Runs at the MCP call boundary using schema type info fromtools/list.policy.authorizationsnow enforces task-scoped default-deny envelopes fortool:woperations, supports tolerant literal/eq/oneOfarg pinning, reads trustedcontrolArgsfromvar toolsmetadata, composes across policy layers, and fails closed when invalid fragments are activated throughwith { policy }.mlld validateandmlld analyzenow also report unknown tools/args, missing control-arg coverage, and unconstrained authorizations with context-aware diagnostics.~=and!~=for guard/when/expression conditions. The new matcher handles string vs single-item array coercion, order-independent flat arrays, subset matching for array expectations, null/empty equivalence when the expected side is empty, numeric string coercion, and comma-separated string to array matching. This makes LLM-produced guard args much easier to validate without hand-written normalization logic.mlld validate/mlld analyzenow surface policy declarations (policies), executable labels, richer guard metadata (filter,privileged,arms), and a new--contextoption for validating guards against tool modules. Context-aware validation warns on missing exe filters, missing operation labels, and@mx.args.*references that do not match the guarded executable signature.filesystem_integrityrules add identity-aware write protection on top of normal filesystem capability checks.mlld statusreports verified/modified/unsigned files with signer labels and taint metadata, runtime reads populate@mx.sig(including@mx.sig.files("glob")), and the Python/live SDK surface now exposesfs:statusviaclient.fs_status().sign,verify, andsign_content, andExecuteHandle.write_file()writes execution-scoped files that are auto-signed asagent:{script}with taint/provenance metadata.no-send-to-unknownrequires the first argument toexfil:sendoperations to carryknown, andno-send-to-externalrequiresknown:internalfor internal-only send policies.no-destroy-unknown, which requires the first argument todestructive:targetedoperations to carryknown. This gives delete/cancel/remove flows the same positive-check protection model as the send-specific rules and supports pinned-target privileged overrides when policies are not locked.@stateupdates are now available across the live transport and all maintained language SDKs (Go, Python, Rust, Ruby, Elixir).payloadLabels/payload_labelsattach labels to individual@payloadfields, and SDK state updates can now sendlabelsso injected values participate in normal label flow. Python also keeps thetrusted(),untrusted(), andlabeled()helpers for inline payload construction.@mx.args, including dot access for identifier-safe names (@mx.args.value), bracket access for arbitrary names (@mx.args["repo-name"]), and the reserved discovery list@mx.args.names. The named-arg snapshot is available in guard bodies, after-guards, and denied handlers for both direct exec calls and pipeline-stage bindings.guard before @send_email = when [...]), matching by exe name. This parallels the existing hook@fnNamesyntax and enables guards scoped to specific executables without requiring label-based matching.op:filters against operation labels (e.g.,hook before op:tool:w), not just built-in operation types. This aligns hook and guard trigger semantics — both now treat custom labels as validop:targets.guard_denialevents, structured execute results collectdenials, and the live stdio / Python / Ruby / Go / Rust / Elixir SDK layers now preserve that payload without string parsing.Fixed
toolCallaudit events are now emitted only when an executable/tool body actually runs. Early returns from pre-guards or parameter label-flow checks no longer create false tool-call records, anddurationnow measures body execution only.@mx.taintand@output.mx.taint, so source markers such assrc:mcpandsrc:jsremain visible in after-guard checks.npm install -g mlldno longer requires a globally installedtypescriptpackage. Thetypescriptmodule is only imported when extracting definitions from.tsfiles.@py/or@python/import is actually resolved, so scripts that don't use Python imports work without Python installed.py { },bash { }, andnode { }blocks now show actionable error messages when the required binary is missing (e.g. "Python 3 is not installed. Install it to use py { } blocks") instead of the rawspawn ENOENTerror.||now preserves normal boolean semantics for ordinary exec/method-call expressions such as@model.includes(...) || ...instead of misrouting them through parallel exec handling. Explicit streamed chains (stream @a() || stream @b()) still run in parallel, and longer streamed chains now include every operand.when,&&,||,!, and ternary conditions from accidentally treating errors as truthy values.@fn(...)//run @fn(...)) now preserves per-argument security descriptors through therunExecdispatcher, so callee parameters keep labels for both bare variable arguments and inline object/array literals instead of droppingmx.labelsat the exe boundary.when [...]expressions and/ifbranches. Selectedwhenresults inherit labels from evaluated conditions, including fallback arms, and/ifbranch results plus branch-local updates now retain the condition's security context.mlld analyze/mlld validatenow includesoperationsmappings andlockedstate consistently, privileged-guard validation now catches missing policy operation mappings, and the MCP built-insmlld_validate/mlld_analyzenow route through the same analyzer surface as the CLI so inline validation sees the same semantic/context warnings.when/guard matching, so conditions like@mx.args.recipients == ["john@gmail.com"]work for privileged policy exceptions and other pinned-argument checks. Added regression coverage for nested array coercion and guard-level array arg matching.allowdecisions can now override policy label-flow denials and built-in label-flow rules by default, which enables policy-plus-guard exception envelopes for destructive/exfil/privileged flows. Policies can opt back into absolute denial behavior withlocked: true, and managed label-flow checks now run through the guard pipeline so denied handlers andwhenexpressions preserve the correct policy denial semantics.mxlabels, so nesteduntrustedvalues in config objects still triggeruntrusted-llms-get-influencedandno-untrusted-destructive. Added regression coverage for parsedmessagesconfig objects, nested object field label access, and destructive-policy enforcement through object wrappers.when/denied-handler blocks now reparses split bracket/dot tails after variable references, so expressions like@mx.args["names"]and chained forms such as@mx.args["names"].mx.labels.join(",")resolve correctly instead of rendering the tail as literal text.@mcp.sendEmail()) from an exe with the same unqualified name (@sendEmail) no longer falsely trigger the circular reference guard. The recursion detector was comparing unqualified method names, ignoring the namespace prefix, so every exe wrapper that delegated to its MCP counterpart was rejected as self-recursive.@base/docs/*.txtnow materialize to their string form instead of degrading to"[object Object]", which fixes config-importedfilesystem_integrityglobs inmlld status.whenexpressions no longer misclassify plain object results that happen to include atypekey as internal AST nodes. Inline object literals like{ type: "response" }now return correctly instead of collapsing toundefined.mlld live --stdio):stdouteffects no longer write raw text to stdout when streaming is disabled, which was corrupting the NDJSON protocol. Content is now captured in the document buffer instead. Fixes SDKexecute()/process()calls failing withinvalid live responsewhen scripts produce multiline output (e.g. viaclaude -p)._reader_loopnow buffers incomplete JSON lines instead of failing all pending requests. Provides defense-in-depth against any stdout contamination reaching the transport.registryVersionso subsequent versioned imports reuse the correct cache entry.var @x = @val ? @val | @filter : null) now produces an actionable parse error explaining the limitation and showing two workarounds (exe block wrapper or split into separate steps) instead of a generic "Text content not allowed in strict mode" message.--stateso scripts can populate@statefrom@file.json, inline JSON objects, orKEY=VALUEflags without routing through--inject.mlld infonow builds source URLs correctly for directory-backed registry modules, fixing broken# tldrlookups that previously producedundefinedpaths.state://writes now preserve structured objects and booleans as native values instead of flattening them through the text surface first. This also fixes live@statesnapshots and SDK payloads receiving"[object Object]"or stringified booleans in affected flows, and adds first-class support for inline object/array literals inoutput ... to "state://..."."[object Object]". Objects now prefer JSON serialization, and genuinely unserializable values surface as[unserializable object]instead.state:writeevents and finalstateWritesresults, avoiding manualjson.loads(...)for object values and preventing mixed-type duplicate entries during merge.whenexpressions no longer swallow hard policy denials (MlldDenialError). Previously, a policy-denied action inside awhenarm was silently caught and mishandled as a soft guard denial, allowing execution to continue. Hard denials now propagate correctly.@payloadand other object variables now preserves security metadata (labels, taint) when resolving nested fields via method calls and builtins. Previously, extracting the raw value before field traversal dropped per-field descriptors..mx.labelsaccess consistently, instead of only preserving labels after reassignment or on primitive leaves.@args.data, matching bare-variable behavior and policy label-flow enforcement..mx.text,.mx.html, and.mx.mdcorrectly. Previously, the content-type-derived accessors onLoadContentResultURLImplwere not propagated through the StructuredValue metadata path, so.mx.html.isDefined()and.mx.md.isDefined()returnedfalsefor HTML pages.op:prefix now accepts colon-compound labels (e.g.,op:tool:w,op:net:r). PreviouslyGuardOpIdentifieronly allowed dot-separated segments, soop:tool:wfailed to parse. Guards also now warn when a data filter likeguard before tool:wmatches a known operation label, suggestingop:tool:winstead.@statepath/top-level export instead of collapsing those labels onto the entire reserved@stateobject, andmlld live --stdionow forwardsstate:update.labelsthrough to the runtime.whenscripts now get full token coverage instead of leaving semantic highlight gaps.exe llmconfig.tools, so tool lists exported from helper modules preserve their function references instead of re-resolving in the caller scope.exe llmtool calling viaconfig.toolsnow preserves label-flow provenance across the internal function-tool bridge. Tool-call args inherit the enclosing LLM/input descriptor plus prior tool-result taint, so label-based defenses likeuntrusted-llms-get-influencedandno-untrusted-destructiveapply to subsequent native function tool calls instead of resetting at the JSON bridge boundary.exe llmcalls with only exe-ref tools (no string tools) no longer leak the CLI's native built-in tools to the model. The runtime now exposes@mx.llm.native(the native tool names CSV), and@mlld/claudepasses--tools ""whennativeis empty to suppress built-in tools. Previously, only theinBoxpath suppressed native tools; exe-ref-only calls outside a box left all 25+ built-in tools visible alongside the intended MCP-bridged tools.<file # section>) no longer strips inline backtick content. Thellmxmllibrary'sgetSection()was parsing markdown into an AST and re-serializing it, which dropped inline code spans. Replaced with a text-based extractor that preserves content verbatim.Documentation
~=/!~=), privileged-guard validation guidance, and the expandedmlld validateJSON/context workflow.uv pip install -e ./sdk/python) so SDK changes apply immediately in downstream projects.