Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions loopx/capabilities/content_ops/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,88 @@ compatibility facade and delegates those packets without changing their output.
The provider does not open a browser, read a timeline, or publish. A real
browser session remains owner-controlled, and every external write still needs
the exact account, body, media/link plan, source references, and stop condition.

### Bundled experience before X preparation / 发 X 前的经验种子

Every new Agent can read the versioned, public-safe
[`x-composer-preflight-v1.json`](experiences/x-composer-preflight-v1.json)
from either existing provider entry point, without a memory account or network:

```bash
loopx value-connectors source-map --connector social_browser_x --format json
loopx value-connectors install-check --connector social_browser_x --format json
```

The JSON `operating_experience` field contains the full seed, version, package
content digest, applicability, observations, procedure and memory initialization
recipe. Markdown output exposes the compact lesson and points to JSON. The
seed ships in the wheel, not only in the source tree. Read it before editing a
composer, and revalidate the final body/card after every subsequent edit.

新的 Agent 不需要继承旧会话,也不需要先有 memory provider,就能从以上入口读到
完整经验种子。它属于 `content-ops` 的内置 `social_browser_x` provider;不新增
capability、扩展、内核规则或第二套记忆存储。

The seed includes a negative publication result: entering the main URL last
restored its composer preview, but a published long post bound `card.rest_id`
to the first supporting URL in its `note_tweet` URL entities instead. Input
order, body order and published-card selection are distinct. Retain every
required link and try the intended URL first in the actual body; this remains
a mitigation **without a verified successful published readback**, not an X
contract. Unknown target binding stays unknown. A rich-text `fill` can leave old paragraphs behind;
scoped native select-all/clear/insert plus exact readback is the recovery path.
Composer readiness and published-post verification are separate evidence. Do
not remove requested links or publish a test merely to obtain a card.

这里保存的是包含失败反例的操作经验:最后输入主链接只能改变编辑器预览,曾经
在发布后仍绑定到第一个合作者链接。保留多个链接,把目标链接放到正文第一个
URL 位置,是待发布后验证的缓解方案,不是已证实的修复。输入先后、正文顺序和
发布端选卡必须分开。不得为验证卡片擅自发帖,也不得用删链接代替解决问题。

### Initialize an Agent's own memory / 初始化自己的记忆

Bundled discovery is implemented; automatic seed import is intentionally **not**
performed. A host can initialize its own provider through the existing
Reward Memory `scoped_feedback` adapter, after corpus-owner authorization and
current-artifact review. Repository publication is not permission to write a
user's provider, and another Agent's private corpus is not a shared seed store.

1. Read `operating_experience` and deduplicate by the configured Goal/Agent scope
plus seed id, version and `content_digest`. Review newer revisions rather
than overwriting local corrections or reviving retired memories.
2. Resolve the Agent's existing
[Reward Memory configuration](../reward_memory/README.md). Keep provider
credentials and scope references in its owner-local configuration.
3. Prepare one `scoped_feedback_reward_memory_event_v0` using the seed's
`content_summary`, `target_class=procedural_experience`, and source kind
`reviewed_learning_card`. Use the seed id/version/digest as a stable source
reference; fill workspace/project/user/peer and surface from the configured
corpus. Set `requested_action_scopes=[]` and `raw_content_captured=false`.
The configured standing policy still reviews the event. Set
`current_artifact_verified=true` only after actual verification; package
presence alone does not satisfy it. Never copy a ready-made verification flag.
4. Use the existing scoped import and require its exact provider readback:

```bash
# event.json contains adapter, event and observed_at; preview first.
loopx reward-memory ingest-event --goal-id <goal> --agent-id <agent> \
--input event.json --format json
# Only under the corpus owner's existing write authorization:
loopx reward-memory ingest-event --goal-id <goal> --agent-id <agent> \
--input event.json --execute --format json
```

5. For subsequent automatic retrieval, use the existing
[`agent_workflow.turn_admission`](../agent_turn_recall/README.md) surface,
configured to the compatible corpus with automatic recall enabled. Name
actual X preparation in the selected Todo so its bounded query can retrieve
the relevant experience. Memory availability is not proof of recall, and
recall is not proof that the Agent applied or verified the lesson.

经验种子读取、写入个人 memory、下一轮召回、当前操作验证,是四个不同结果。
未配置或不可用时,直接参考仓库种子并如实报告,不冒称“已入库/已召回”。后续
平台行为变化应由新的验证经验修订或淘汰旧记录;不要提升为全局强制偏好。

No frontend/Lark configuration changes are introduced: these commands only
read packaged public guidance and retain existing memory settings and gates.
There are no new background imports, browser operations or external writes.
5 changes: 5 additions & 0 deletions loopx/capabilities/content_ops/catalog_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
),
"entry_command": "loopx content-ops aggregate-packets --format json",
"commands": [
{
"command": "loopx value-connectors source-map --connector social_browser_x --format json",
"purpose": "Before X preparation, read the provider's bundled operating experience and optional memory initialization recipe.",
"write_boundary": "packaged seed read only; no browser or memory-provider calls",
},
{
"command": "loopx content-ops exploration-plan --format json",
"purpose": "Plan source lanes before reading connector material.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"schema_version": "content_ops_provider_experience_seed_v0",
"seed_id": "social_browser_x.composer_preflight",
"version": 1,
"observed_on": "2026-09-13",
"provider_id": "social_browser_x",
"target_class": "procedural_experience",
"authority": "advisory",
"title": "Preserve multiple X links while verifying the intended preview card",
"applicability": [
"Preparing or revising an X browser composer with multiple URLs",
"Handing an editable draft back for manual publication"
],
"content_summary": "X composer previews can disagree with published cards. A long post bound its card to the first supporting URL despite a correct main-link preview. Preserve all links; try the intended URL first in the actual body, not merely last in input order. This mitigation is unproven without published readback. Native clear/insert and exact readback avoid rich-text duplication. Never publish to test without authorization.",
"observations": [
"A supporting PR or repository URL can replace the intended main-link card.",
"Entering the intended URL last restored a composer preview but did not preserve that selection after publication. This is a failed publication mitigation, not a success recipe.",
"In a published long post observed on 2026-09-10, card.rest_id matched the first supporting URL in note_tweet entities rather than the intended fourth URL; the card lacked renderable content.",
"Moving the main URL first later retained a supporting preview in the editor; no corresponding successful published readback was recorded. Body order and input order are distinct, and neither is a universal guarantee.",
"A rich-text fill operation can replace only part of an existing editor, leaving duplicated paragraphs."
],
"procedure": [
"Read the current composer and preserve the owner's latest wording, links and account selection.",
"Record the intended card URL separately from supporting URLs; do not silently delete links.",
"Try placing the intended URL before all supporting URLs in the actual body, preferably near the opening. Record this as a mitigation pending published readback, not a proven fix. Merely typing it last is insufficient. Use scoped native editing and verify the editor is empty before inserting a replacement body.",
"Read back the complete final body. Wait for the card to resolve, then inspect its title, loaded image and target where exposed. Missing target evidence remains unknown; do not infer it from the body URL alone.",
"After any text or link edit, repeat card verification. If ambiguous, leave the draft unsent and report the gap.",
"Hand off at the requested manual-send boundary. After an authorized publication, inspect the actual post and card target. Where available, resolve card.rest_id through the long-post URL entities and check renderable card data; do not confuse note_tweet content with the truncated legacy body. A correct composer is preparation evidence only."
],
"limits": [
"URL order is an observed mitigation, not a stable X API contract or machine-enforced policy.",
"Cached metadata, removed previews and platform changes can produce different results.",
"This seed is a public distilled observation, not a raw chat, screenshot, account record or permission grant."
],
"memory_initialization": {
"mode": "caller_reviewed_opt_in",
"adapter": "scoped_feedback",
"source_kind": "reviewed_learning_card",
"recall_surface_id": "agent_workflow.turn_admission",
"identity_key": ["goal_id", "agent_id", "seed_id", "version", "content_digest"],
"query_hint": "X browser draft preparation: multi-link card selection, rich-text replacement and final readback",
"instructions": [
"Use the actor's explicitly configured corpus and provider; do not discover or reuse another Agent's private memory.",
"Review this seed against current artifacts and higher-priority instructions. Fill the existing scoped_feedback event with the configured scope, this content_summary and a seed/version/digest source reference.",
"Do not mark current_artifact_verified merely because the seed exists. Import as procedural_experience only after current verification through the existing candidate/review and ingest-event path.",
"Require the existing provider write and exact readback receipt before claiming initialization. Deduplicate the same scoped seed revision; review updates rather than overwriting newer local experience.",
"For automatic recall, configure the existing agent_workflow.turn_admission surface and include the actual X preparation work in the selected Todo. A configured provider alone does not connect a recall surface.",
"If memory is disabled or unavailable, read this bundled seed directly and report bundled guidance, not provider recall."
],
"provider_calls_performed": false,
"automatic_import": false,
"grants_publish_authority": false
}
}
13 changes: 13 additions & 0 deletions loopx/capabilities/content_ops/social_browser_x.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations

import hashlib
import json
import shutil
from importlib.resources import files
from typing import Any


Expand All @@ -12,13 +15,22 @@


def build_social_browser_x_provider_packet() -> dict[str, Any]:
# Public packaged guidance, not a second memory store or an import side effect.
seed_bytes = (
files("loopx.capabilities.content_ops")
.joinpath("experiences/x-composer-preflight-v1.json")
.read_bytes()
)
experience = json.loads(seed_bytes)
experience["content_digest"] = "sha256:" + hashlib.sha256(seed_bytes).hexdigest()
return {
"ok": True,
"schema_version": CONTENT_OPS_SOCIAL_BROWSER_X_PROVIDER_SCHEMA_VERSION,
"connector_id": SOCIAL_BROWSER_X_CONNECTOR_ID,
"outcome_capability_id": "content-ops",
"provider_module": SOCIAL_BROWSER_X_PROVIDER_MODULE,
"source_profile": {
"operating_experience": experience,
"connector_id": SOCIAL_BROWSER_X_CONNECTOR_ID,
"status": "profile_ready_when_browser_available",
"route_type": "browser-backed public/social channel",
Expand Down Expand Up @@ -50,6 +62,7 @@ def build_social_browser_x_provider_packet() -> dict[str, Any]:
],
},
"install_check": {
"operating_experience": experience,
"connector_id": SOCIAL_BROWSER_X_CONNECTOR_ID,
"status": "ready" if shutil.which("ego-browser") else "needs_ego_browser",
"install": [
Expand Down
8 changes: 8 additions & 0 deletions loopx/capabilities/value_connectors/install_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
}


def render_value_connector_install_check_markdown(payload: dict[str, Any]) -> str:

Check failure on line 122 in loopx/capabilities/value_connectors/install_check.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCa8hV7Y9lWA-gfjlys&open=AaCa8hV7Y9lWA-gfjlys&pullRequest=4343
lines = ["# LoopX Value Connector Install Check", ""]
for item in payload.get("checks") or []:
if not isinstance(item, Mapping):
Expand All @@ -134,6 +134,14 @@
)
if item.get("write_gate"):
lines.append(f"- write_gate: {item.get('write_gate')}")
experience = item.get("operating_experience")
if isinstance(experience, Mapping):
lines.append(
f"- bundled experience `{experience.get('seed_id')}` v{experience.get('version')}: {experience.get('content_summary')}"
)
lines.append(
"- Read operating_experience in the JSON packet before browser preparation; memory initialization remains caller-reviewed and opt-in."
)
lines.extend(["", "Install / use:"])
for command in item.get("install") or []:
lines.append(f"- `{command}`")
Expand Down
14 changes: 14 additions & 0 deletions loopx/capabilities/value_connectors/source_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def _source_profile(
maturity_hint: str,
write_gate: str | None = None,
stop_conditions: list[str] | None = None,
operating_experience: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
return {
"schema_version": VALUE_CONNECTOR_SOURCE_PROFILE_SCHEMA_VERSION,
Expand Down Expand Up @@ -121,6 +122,11 @@ def _source_profile(
"requested action would perform an external write without an audit gate",
],
**_outcome_provider_binding(connector_id),
**(
{"operating_experience": dict(operating_experience)}
if operating_experience is not None
else {}
),
}


Expand Down Expand Up @@ -426,6 +432,14 @@ def render_value_connector_source_map_markdown(payload: dict[str, Any]) -> str:
)
if profile.get("write_gate"):
lines.append(f"- write_gate: {profile.get('write_gate')}")
experience = profile.get("operating_experience")
if isinstance(experience, Mapping):
lines.append(
f"- bundled experience `{experience.get('seed_id')}` v{experience.get('version')}: {experience.get('content_summary')}"
)
lines.append(
"- Read operating_experience in the JSON packet for applicability and opt-in memory initialization; bundled guidance is not provider recall or publish authority."
)
lines.append("")
action_gated = payload.get("action_gated_profiles")
if isinstance(action_gated, list) and action_gated:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ include = ["loopx*"]
"loopx.control_plane.turn_driver" = ["*.ts"]
"loopx.control_plane.work_items" = ["*.ts"]
"*" = ["README.md", "README.zh-CN.md", "docs/*.md", "docs/protocols/*.md"]
"loopx.capabilities.content_ops" = ["templates/*.json"]
"loopx.capabilities.content_ops" = ["templates/*.json", "experiences/*.json"]
"loopx.capabilities.manager_context" = ["skills/loopx-manager/SKILL.md"]
"loopx.extensions.lark" = ["extension.toml", "README.md", "docs/*.md"]
"loopx.extensions.openviking_periodic_report" = ["extension.toml"]
Expand Down
Loading