Repository navigation
Conversation
…, F3) The planner and worker user messages now carry worker output, finding details, recent_facts, finding_list, cold_digests, related_tasks and hints not written by the operator inside a code-owned data envelope whose nonce comes from crypto/rand (16 bytes, hex) once per message. Embedded marker stems are neutralised with a fixed-string replacement. The task goal, goal nodes, constraints and the operator's own hints stay outside, labelled operator. Overview fact summaries are capped at factSummaryMax (160 runes), the cap list_facts already uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Agent- and target-derived fields reach the planner and worker user messages inside a code-owned data envelope. Each message gets its own random nonce.
renderTriggers): the worker output conclusion, the finding detail, and hints not written by the operator are framed. An operator hint (originhuman) stays outside the frame, prefixed[operator].renderOverviewSections, used by the planner situational block and the worker launch overview): the snapshot is split by origin.task(description and goal),goals(goal nodes), andhintswritten by the operator.recent_facts,finding_list,cold_digestsandrelated_tasksis framed. The hints of every other origin go in anagent_hintsframe.graphOverviewDatanow addsoriginto each hint entry, so the split can tell operator hints from the others. Thegraph_overviewtool output gains this field too.recent_factsnow usecompactFact, so their summaries are capped.Why
Audit F3 for #124 (memory poisoning and plan hijacking rows), target 0.7.0. Raw worker output and fact summaries reached the planner's user message without framing, and so did every worker overview. That includes a payload a worker quoted verbatim in a fact, as the #49 rule asks it to. The Norma boundary frames tool results only, never these messages.
Envelope format
<N>is 16 bytes fromcrypto/rand, hex-encoded (32 characters). A new one is drawn for each planner round message and each worker launch message. All the frames of one message share it.agent/data_envelope.go), never in an editable prompt body. The intro describes the markers without spelling a complete one.<,>and&, sojson.Marshalnever escapes them. A marker copied into a fact therefore stays literal in the JSON, and the neutraliser can find it.strings.Replacerrewrites every occurrence of the stemARTEX_DATA(both markers contain it) toartex-data-marker-removed.A. Rewritten text therefore cannot form a new stem with its neighbours.Fact summary cap
The cap is
factSummaryMax = 160runes, plus an ellipsis andsummary_truncated: true. This is the existing named constant thatlist_factsalready applies.list_factsand in the overview.node_detail(id)call away.Operator versus derived
recent_facts,finding_list,cold_digests,related_taskshuman, labelled operatororchestrator,worker-N,planner, unrecorded)Goal nodes stay outside because they are the operator's goal, either set by the operator or decomposed from it. They are not target content.
Tests
The tests are pure and need no database (
agent/data_envelope_test.go):TestDataEnvelopeNeutralisesMarkers: content that tries to close the frame with the right nonce, then open a new one, is neutralised. Only the two code-owned markers survive, and the payload stays inside the frame. The same holds after JSON encoding in a fact summary.TestDataEnvelopeNoncePerMessage: two messages get different nonces. Each nonce is at least 16 hex-decoded bytes, and every frame in one message carries that message's nonce.TestPlannerInputEnvelopeSplit: the full planner round input has a frame for each derived field kind (worker_output,finding,hints,recent_facts,finding_list,cold_digests,related_tasks,agent_hints). Derived text appears only inside the frames. The goal, the constraints and the operator hint appear only outside, and the operator hint is labelled[operator]and sits in the operator block.TestWorkerOverviewEnvelopeSplit: the same split holds for the worker launch overview, and coverage is still dropped.TestFactSummaryCapped:compactFactcaps atfactSummaryMax.TestRenderTriggersHintOriginis updated for the new signature and framing.Results in WSL Kali (go1.26.10 toolchain):
go build ./...: okgo vet ./...: okgo test -count=1 ./agent:ok github.com/Autumn-27/artex/agent. 91 pass, 36 skip, 0 fail. The skips are the PG-unit tests (no database configured locally); CI runs them.Mutation check
Each mutation was applied, the tests were run, and the code was restored.
renderTriggers,env.wrap("finding", ev.Detail)was replaced withev.Detail.TestPlannerInputEnvelopeSplitfailed with "no frame for derived field finding" and reported the derived text outside the frames.renderOverviewSections, the derived fields were written withoutenv.wrap.TestPlannerInputEnvelopeSplitandTestWorkerOverviewEnvelopeSplitfailed on every derived field.After the restore, all tests pass.
Limits
open_intents,running_intents,recent_done_intents) are written by the planner and can relay derived text. They are outside the F3 list and stay plain.task_credentialsand the stagnation warning also stay plain.graph_overviewtool result and the other tool results are framed only by the Norma boundary (F9) when it is on.humanis treated as the operator's. The main-agent chat writes that origin, so a main agent that transcribes target text into a hint would still be labelled operator (see F4 and F6).artex_dataor a homoglyph is not rewritten. The per-message nonce is what such text cannot forge.