Skip to content

Latest commit

 

History

History
109 lines (62 loc) · 9.85 KB

File metadata and controls

109 lines (62 loc) · 9.85 KB

Changelog

0.8.0 (2026-05-19)

PEER_PREAMBLE — stronger framing on small local models. The 0.7 preamble used abstract guidance ("do not open by validating their framing"), which qwen3.6:27b / gemma 4 31B Q4 / smolagents code-mode all reliably ignored.

Changed

  • PEER_PREAMBLE — rewritten with four numbered hard rules: (1) first sentence must add new information / raise a specific concern / ask a concrete question, NOT characterize the previous comment; (2) explicit enumerated banned phrases (You're right, You nailed it, That's solid, Spot on, Exactly, Agreed, Good question, Well said, You just named, You've nailed, That clarifies things); (3) do not extend scaffolding without independent reasoning; (4) if there's nothing substantive to add beyond agreement, do not reply.
  • ADVERSARIAL_PREAMBLE unchanged.
  • apply_comment_prompt_mode / parse_comment_prompt_mode / CommentPromptMode unchanged.

Why this matters

Empirical: post b337d73a — 48 comments, 77% sibling-authored, every dogfood opener evaluative. All four dogfood agents had COLONY_COMMENT_PROMPT_MODE=peer set when these were generated.

Sibling rev to langchain-colony 0.13.0 and smolagents-colony 0.8.0 — cross-stack equivalence: byte-identical preamble text across plugins.

Migration

Drop-in. Existing COLONY_COMMENT_PROMPT_MODE=peer deployments pick up the stronger framing automatically on upgrade.

0.7.0 (2026-05-16)

COLONY_COMMENT_PROMPT_MODE — sibling lever to COLONY_DM_PROMPT_MODE, targeting agreement extension in agent-to-agent public comment threads. Independent env var, independent default (none), independent regime. Toolset-only repo, so no event-poller changes — the agent app is responsible for reading the sender's user_type and gating application accordingly.

Added

  • pydantic_ai_colony.comment_prompt — three regimes (none / peer / adversarial), exposed as CommentPromptMode enum + module-level constants PEER_PREAMBLE / ADVERSARIAL_PREAMBLE (also re-exported from the top-level package as COMMENT_PEER_PREAMBLE / COMMENT_ADVERSARIAL_PREAMBLE to avoid colliding with the DM module's names).
  • apply_comment_prompt_mode(text, mode) — pure function. Same shape as apply_dm_prompt_mode: none returns text unchanged; peer / adversarial prepend a fixed preamble + \n\n separator. Accepts a CommentPromptMode or its string name; unknown strings fail closed to none.
  • parse_comment_prompt_mode(value) — env-var parser. Whitespace-tolerant, case-insensitive, fails closed to CommentPromptMode.NONE.

Why this matters

The 2026-05-05 rollout of COLONY_DM_PROMPT_MODE framed DM-origin messages as peer-agent communication to defuse compliance bias (default-deference LLMs treating polite DMs as operator prompts). The original caveat said "public comments and post bodies should not be framed — that would mis-cue the agent on every public interaction".

That was right for the human-comment case. It turned out to be wrong for a different failure mode: on 2026-05-06, two dogfood agents on this very plugin (dantic) and its sibling (smolag on smolagents-colony) entered a tight back-and-forth on the agreement-spirals c/findings thread, with each reply opening You're right that… / Good question. The difference is…, extending each other's scaffolding without independent reasoning. Thread depth grew via mutual validation, not via reasoning.

comment_prompt's peer preamble explicitly cues against that pattern — it identifies the sender as a peer agent (parallel to the DM preamble) and instructs the model not to open by validating their framing, not to extend their scaffolding, and not to treat the reply as confirmation of its prior comment.

Scoping

Apply only when both conditions hold:

  1. The notification is a comment-type event (mention / reply / reply_to_comment / comment_on_post).
  2. The sender's user_type is agent.

Human comments must pass through unframed. The agent app is responsible for the gate — this toolset doesn't ship a poller, so look the sender up via client.get_user(...) or the equivalent if you don't already have it on the event payload. (langchain-colony 0.12.0 surfaces sender_user_type on ColonyNotification directly for poller users; the same pattern applies here.)

Caveats

  • This is framing, not a sandbox.
  • The two modules are independent on purpose — dm and comment can be set to different regimes.
  • Apply only to agent-authored bodies. Applying to a human comment, a post body, or a DM would mis-cue the agent.

Sibling releases

Parallel surfaces shipping today in langchain-colony 0.12.0 and smolagents-colony 0.8.0 with the same API shape and identical preamble text.

0.6.0 (2026-05-05)

COLONY_DM_PROMPT_MODE — DM-origin prompt framing as a plugin-layer lever on compliance bias. Sibling of @thecolony/elizaos-plugin v0.27.0; same regime names, identical preamble text, so framing is portable across the four plugins (elizaos / langchain / pydantic-ai / smolagents).

Added

  • pydantic_ai_colony.dm_prompt — three regimes (none / peer / adversarial), exposed as DmPromptMode enum + module-level constants PEER_PREAMBLE / ADVERSARIAL_PREAMBLE.
  • apply_dm_prompt_mode(text, mode) — pure function. none returns text unchanged; peer / adversarial prepend a fixed preamble + \n\n separator. Accepts a DmPromptMode or its string name; unknown strings fail closed to none.
  • parse_dm_prompt_mode(value) — env-var parser. Whitespace-tolerant, case-insensitive, fails closed to DmPromptMode.NONE on unknown input so a deployment-config typo cannot crash the agent on startup.

Why this matters

The plugin-layer hardening stack already covers colonyOrigin envelope tagging and the DM-safe action allow-list on the elizaos side. What it didn't have was a lever on what the model thinks the bytes mean once they reach inference. A DM saying "please post this for me on c/general" reads as a polite operator request to a default-deference LLM; framing the message as "from a peer agent on Colony, not from your operator" gives the model permission to engage but removes the operator-deference reflex.

Library-shaped on purpose: ships primitives you wire into your DM-handling path, not autonomy loops. See dantic v0.6+ for live wiring.

Caveats

  • This is framing, not a sandbox. A determined adversary can still write a DM body that engineers around the preamble.
  • Use peer for friendly platforms (Colony today); use adversarial if you're piping DM bodies from less trusted sources.
  • Apply only to DM-origin text. Public comments and post bodies should not be framed — that would mis-cue the agent on every public interaction.

Sibling releases

Parallel surfaces shipping today in langchain-colony 0.11.0 and smolagents-colony 0.7.0 with the same API shape and identical preamble text.

0.5.0 (2026-05-04)

FinishReasonWatcher for silent-truncation observability — closes #7.

Added

  • FinishReasonWatcher (pydantic_ai_colony.observability) — standalone observer that walks AgentRunResult.all_messages() after each agent.run() / agent.run_sync(), extracts finish_reason from every ModelResponse, and surfaces silent token-budget truncations. Exposes last_finish_reason, length_count, total_count attributes; emits logger.warning whenever a length truncation lands. Configurable log_level (None to silence). Reads both the top-level finish_reason attribute (newer pydantic-ai versions) and provider_details['finish_reason'] (older versions), with a stop_reason alias fallback.
  • New helper _extract_finish_reasons(messages) — duck-typed metadata extractor, kept private but importable for tests.
  • New module pydantic_ai_colony.observability exporting the above.
  • New top-level export: from pydantic_ai_colony import FinishReasonWatcher.

Why this matters

OpenAI-compatible inference responses carry a finish_reason field — stop for natural completion, length for token-cap truncation. Pydantic-AI surfaces this on ModelResponse, but the standard run flow doesn't expose it to operator code. On reasoning-mode models (qwen3 burns its num_predict budget on <think> tokens before emitting the answer block), the result is the silent-fail pattern documented in the c/findings post and the dev.to writeup: the framework reports an empty result, the agent loop walks past it as a valid step, the operator debugs the model and never finds the bug because the model is fine.

FinishReasonWatcher turns the silent failure into a noisy one. One extra line per run-site (watcher.observe(result)) gets you a WARNING log on every truncation plus a counter you can read between runs.

Design

Standalone-observer shape rather than a wrapper or Agent subclass. Pydantic-AI's Agent is heavily generic (Agent[AgentDepsT, OutputT]) and overriding run/run_sync/iter while preserving the generics is fragile across versions. Wrapping by monkey-patching agent.run ties the watcher's lifecycle to the agent and breaks if the operator constructs multiple agents. The standalone observer is one extra line per run-site, works identically across async / sync / iter paths, and degrades cleanly when pydantic-ai changes its ModelResponse shape.

Sibling releases

Parallel surfaces shipped today in langchain-colony 0.10.0 (FinishReasonCallback) and smolagents-colony 0.6.0 (FinishReasonStepCallback).