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
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ AGENTMESH_FEISHU_CHAT_ID=
AGENTMESH_FEISHU_TASK_BASE_URL=
# Default cards contain only status and Task ID; opt in to objective/result excerpts.
AGENTMESH_FEISHU_INCLUDE_CONTENT=false
# Opt-in coordinated employee start/result cards. Requires feishu_notifications.
# To include short business summaries, also opt in to INCLUDE_CONTENT above.
AGENTMESH_FEISHU_SYNC_COLLABORATION=false
AGENTMESH_FEISHU_SEND_INTERVAL_SECONDS=1
# Comma-separated trusted in-process extensions. Empty disables every installed extension.
AGENTMESH_RUNTIME_EXTENSIONS=agentmesh.music-studio
# Immutable operator allowlist used before importing third-party extension code.
Expand Down
2 changes: 2 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ x-agentmesh-environment: &agentmesh-environment
# coordinated DAG across published employees. Operators can override the full
# feature-gate set with an explicit value.
AGENTMESH_FEATURE_GATES: ${AGENTMESH_FEATURE_GATES:-coordinated_execution=true,agent_registry_management=true}
AGENTMESH_FEISHU_SYNC_COLLABORATION: ${AGENTMESH_FEISHU_SYNC_COLLABORATION:-false}
AGENTMESH_RUNTIME_EXTENSIONS: ${AGENTMESH_RUNTIME_EXTENSIONS:-agentmesh.music-studio}
AGENTMESH_EXTENSION_LOCK_PATH: ${AGENTMESH_EXTENSION_LOCK_PATH:-/app/extensions.lock}
AGENTMESH_IDENTITY_PRINCIPALS_JSON: ${AGENTMESH_IDENTITY_PRINCIPALS_JSON:-[]}
Expand Down Expand Up @@ -171,6 +172,7 @@ services:
AGENTMESH_FEISHU_CHAT_ID: ${AGENTMESH_FEISHU_CHAT_ID:-}
AGENTMESH_FEISHU_TASK_BASE_URL: ${AGENTMESH_FEISHU_TASK_BASE_URL:-}
AGENTMESH_FEISHU_INCLUDE_CONTENT: ${AGENTMESH_FEISHU_INCLUDE_CONTENT:-false}
AGENTMESH_FEISHU_SEND_INTERVAL_SECONDS: ${AGENTMESH_FEISHU_SEND_INTERVAL_SECONDS:-1}
depends_on:
migrate:
condition: service_completed_successfully
Expand Down
4 changes: 2 additions & 2 deletions docs/implementation-status.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Implementation status

Last updated: 2026-09-30 · Status: Alpha, supported single-team v1 baseline
Last updated: 2026-10-07 · Status: Alpha, supported single-team v1 baseline

This page describes the current repository, not the full target architecture. The
[v1 scope](v1-completion-scope.md) is implementation-complete and release-qualified for evaluation
Expand All @@ -14,7 +14,7 @@ and non-critical single-team use. It is **not** a production HA or multi-tenant
| Deliverable acceptance | Optional pinned Coordinated primary checks, typed/unit-aware rates, audited human decisions, and accepted-result export | Checks cover only configured fields/facts, not arbitrary prose truth or already executed tool actions; see [guide](deliverable-acceptance.md) |
| Agents and models | Published immutable Agent Versions, role-bound execution, deterministic keyless demo, encrypted OpenAI/DeepSeek model connections in the authenticated Console | A private DeepSeek Direct and three-employee Coordinated run passed; OpenAI has fixture qualification, not a live-provider acceptance claim |
| Collaboration and tools | Governed MCP registry/read tools/idempotent writes; trusted A2A peer registration, delegation, polling, cancellation, and unknown-outcome reconciliation | External providers/peers require configuration; irreversible writes and A2A streaming/push are not supported |
| External notifications | Optional Feishu bot cards for Task completion, failure, pending human review, and governed approval requests, backed by durable PostgreSQL delivery jobs | Gate is off by default; app credentials and a test group are required; inbound commands/approvals are not yet supported |
| External notifications | Optional Feishu bot cards for Task completion, failure, pending human review and governed approvals; separately opt-in live coordinated employee starts, received deliveries and provisional business summaries; durable PostgreSQL delivery jobs | Gate and collaboration sync are off by default; content excerpts require another opt-in; app credentials and a controlled group are required; inbound commands/approvals are not supported |
| Governance | Optional Identity/RBAC, Policy approvals, one-time Permits, budgets, quotas, audit, usage, and Langfuse metadata export | Identity is not enabled by default; do not expose credential entry over anonymous public HTTP |
| Company | Company/Position/Appointment, goals, Operations, Business Objects, reviewed PostgreSQL memory with optional subject-scoped review hints, internal finance controls, and installable Packs | No automatic semantic contradiction verdict; external memory ranking, accounting, payments, and most live connectors are not part of the default product |
| Console | Guided task/team creation, model and employee setup, English/Chinese UI, work cards, pinned coordinated context transfers, and durable Mission Map replay | Transfer records start with new Runs; a dependency line alone is not evidence of an Agent message. The game-style Office is retired |
Expand Down
26 changes: 26 additions & 0 deletions docs/integrations/feishu-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,34 @@ The gate is off in every profile, including `full`.
outbound-only release. If `policy_approval` is also enabled, pending governed actions produce
separate approval cards.

## Live employee collaboration

After enabling the notification gate, explicitly opt in to coordinated employee updates:

```dotenv
AGENTMESH_FEISHU_SYNC_COLLABORATION=true
# Only if every group member may see business excerpts:
AGENTMESH_FEISHU_INCLUDE_CONTENT=true
AGENTMESH_FEISHU_SEND_INTERVAL_SECONDS=1
```

Restart API, Worker and notifier. This extra setting defaults off. Only coordinated execution
workers with pinned work-item evidence emit start, received-delivery, provisional-result and
failure cards; technical supervisors are not presented as employee discussion. Existing Tasks
are not automatically backfilled. Multiple incoming deliveries are grouped into one start card.

These are actual work-item transfers and bounded public business summaries, not chain-of-thought,
full prompts, raw memory or free-form chat. Metadata-only mode hides summaries. Content opt-in
uses credential-pattern filtering, **not a guarantee of semantic confidentiality**: normal
business conclusions may use permitted knowledge. Use a controlled group and reviewed data
policy. Low-volume notifications normally arrive within seconds; queues, rate limits and retries
can delay delivery. Replying in Feishu does not execute a command, teach memory or approve work.

## Delivery and operations

The [live qualification record](../qualification/feishu-collaboration.md) documents a real
four-employee model run and nine acknowledged Feishu messages, with test scope and limitations.

Task transitions and notification jobs commit atomically in PostgreSQL. The notifier independently
claims jobs, sends with the stable job UUID as Feishu's deduplication UUID, and retries transient
errors with bounded backoff. After eight failed attempts a job becomes `DEAD`; inspect the
Expand Down
27 changes: 27 additions & 0 deletions docs/integrations/feishu-notifications.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,35 @@
如果还开启了 `policy_approval`,待处理的治理审批也会产生单独卡片。
机器人需要在群内并有发言权限。单向通知不需要配置飞书回调地址。

## 运行中的员工协作

在原有通知 Gate 开启后,可额外配置:

```dotenv
AGENTMESH_FEISHU_SYNC_COLLABORATION=true
# 只有群成员有权看到业务内容时才开启摘要:
AGENTMESH_FEISHU_INCLUDE_CONTENT=true
AGENTMESH_FEISHU_SEND_INTERVAL_SECONDS=1
```

重启 API、Worker 和通知进程。该选项默认关闭,适用于协作模式下具有已固定工作输入的执行员工;
不会自动回放旧任务,也不会将技术汇总节点冒充真实员工发言。

群内会看到「员工开始工作」「收到其他员工的交付」「阶段成果」「执行失败」卡片。
接收关系来自真正固定的交接快照,成果来自该次执行的公开业务摘要,不是模型内部思考、
完整提示词、原始记忆记录或自由聊天。每位员工的多个输入交接合并进一条开始卡片,
避免每条依赖单独刷屏。低负载时通常数秒内发送,积压、限流或重试时会延后。

不开启 `INCLUDE_CONTENT` 时仅发送身份、关系和状态。开启后会发送有长度限制、
凭据模式遮盖的业务摘要;它不是任意敏感信息的语义脱敏保证,员工可能在正常业务结论中
使用被允许读取的知识。因此只接入受控群,发送前确认数据权限,不把密钥填写到业务内容中。
这不是飞书双向指挥功能:群中回复不会直接改变任务、员工记忆或批准作品。

## 可靠性与边界

[真实验收记录](../qualification/feishu-collaboration.md)记录了四员工真实模型协作及九条飞书消息
成功接收的结果,并明确验收范围和未覆盖的边界。

任务状态和通知待办在同一个 PostgreSQL 事务提交。通知进程独立领取待办,使用固定 UUID
去重,失败后有界退避重试;连续失败八次进入 `DEAD`,可先检查 `feishu_notifications`
表和通知日志,再由管理员手动恢复。状态已变化或过期的旧审批通知会跳过。飞书故障不会让任务失败。
Expand Down
48 changes: 48 additions & 0 deletions docs/qualification/feishu-collaboration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Live Feishu collaboration qualification

[Setup and limits](../integrations/feishu-notifications.md) · [简体中文配置](../integrations/feishu-notifications.zh-CN.md)

Validated on 2026-10-07 in an authenticated, private single-team deployment using the owner's
existing bot and controlled test group. Credentials and deployment addresses are not published.

## Scenario and observed result

A coordinated original-song planning task used four separately published employees backed by
the live DeepSeek model connection: creative director, researcher, lyricist, and composer.
The director supplied creative constraints; the researcher used the supplied evidence; lyrics
and arrangement work consumed pinned predecessor results. No live chart research, audio
generation or audio listening is claimed by this text-only qualification.

The task completed, and the private scenario entered its waiting-for-audio stage. PostgreSQL
delivery records showed:

| Event | Delivered | Attempts per event |
| --- | ---: | ---: |
| Employee started work, including pinned incoming deliveries | 4 | 1 |
| Employee provisional result | 4 | 1 |
| Task completed | 1 | 1 |

All nine deliveries received successful acknowledgements from the real Feishu send API.
This confirms API acceptance, not a claim that a human read the messages. Transfers are actual
pinned collaboration evidence, not model internal reasoning or invented chat.

## Regression evidence

- 50 focused configuration, projection, filtering and notifier tests passed.
- 23 tests passed against an isolated PostgreSQL database, including atomic rollback,
duplicate-save protection, gate defaults, tenant/task/run binding and delivery after completion.
- The full non-PostgreSQL regression suite passed.
- GitHub CI passed quality, unit coverage, PostgreSQL integration, Compose E2E,
dependency review and CodeQL checks.

## Limits

The shared public demo keeps live collaboration sync disabled. New deployments default to
metadata-only notifications with both the parent feature gate and collaboration opt-in off.
Private business excerpts require explicit content opt-in. Credential-pattern filtering is not
semantic confidentiality; do not place secrets in Task objectives or employee summaries.
No prompts, raw memory, raw provider errors or full creative artifacts are selected for cards.
This is outbound notification, not two-way group chat or approval from Feishu.

The live run did not exercise provider outage, exhausted retries, scale, rate limits, audio
review or duplicate delivery after Feishu's deduplication window; those are not qualified here.
3 changes: 3 additions & 0 deletions src/agentmesh/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ def _database_components(settings: Settings):
return engine, session_factory, SqlAlchemyUnitOfWorkFactory(
session_factory,
feishu_notifications_enabled=gates.is_enabled(Feature.FEISHU_NOTIFICATIONS),
feishu_collaboration_enabled=(
gates.is_enabled(Feature.FEISHU_NOTIFICATIONS) and settings.feishu_sync_collaboration
),
)


Expand Down
2 changes: 2 additions & 0 deletions src/agentmesh/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class Settings(BaseSettings):
feishu_chat_id: str | None = None
feishu_task_base_url: str | None = None
feishu_include_content: bool = False
feishu_sync_collaboration: bool = False
feishu_send_interval_seconds: float = Field(default=1.0, ge=0.1, le=10)
feishu_timeout_seconds: int = Field(default=5, ge=1, le=30)
feishu_scan_seconds: int = Field(default=3, ge=1, le=60)
runtime_extensions: str = "agentmesh.music-studio"
Expand Down
2 changes: 2 additions & 0 deletions src/agentmesh/entrypoints/feishu_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def main() -> None:
client=client,
task_base_url=settings.feishu_task_base_url,
include_content=settings.feishu_include_content,
sync_collaboration=settings.feishu_sync_collaboration,
send_interval_seconds=settings.feishu_send_interval_seconds,
)
try:
while True:
Expand Down
78 changes: 76 additions & 2 deletions src/agentmesh/infrastructure/postgres/repositories.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime, timezone
from uuid import UUID, uuid4
from uuid import NAMESPACE_URL, UUID, uuid4, uuid5

from sqlalchemy import Select, delete, select
from sqlalchemy import func as sa_func
Expand Down Expand Up @@ -264,11 +264,13 @@ def _to_domain(record: TaskResolutionRecord) -> TaskResolution:


class SqlAlchemyTaskRunRepository:
def __init__(self, session: Session) -> None:
def __init__(self, session: Session, *, feishu_collaboration_enabled: bool = False) -> None:
self._session = session
self._feishu_collaboration_enabled = feishu_collaboration_enabled

def add(self, run: TaskRun) -> None:
self._session.add(self._to_record(run))
self._queue_collaboration(run, previous_status=None, previously_pinned=False)

def get(self, run_id: UUID, *, for_update: bool = False) -> TaskRun | None:
record = self._session.get(TaskRunRecord, run_id, with_for_update=for_update)
Expand All @@ -278,6 +280,8 @@ def save(self, run: TaskRun) -> None:
record = self._session.get(TaskRunRecord, run.id)
if record is None:
raise LookupError(f"Task run record {run.id} was not found")
previous_status = record.status
previously_pinned = record.work_item_snapshot is not None
if (
record.runtime_authority != run.runtime_authority
or record.comparison_mode != run.comparison_mode
Expand Down Expand Up @@ -311,6 +315,76 @@ def save(self, run: TaskRun) -> None:
record.paused_from_status = (
run.paused_from_status.value if run.paused_from_status is not None else None
)
if record.task_id == run.task_id:
self._queue_collaboration(
run, previous_status=previous_status, previously_pinned=previously_pinned
)

def _queue_collaboration(
self, run: TaskRun, *, previous_status: str | None, previously_pinned: bool
) -> None:
if (
not self._feishu_collaboration_enabled
or run.role is not RunRole.EXECUTOR
or run.subtask_id is None
or run.work_item_pinned_at is None
):
return
snapshot = run.work_item_snapshot
if not isinstance(snapshot, dict):
return
item = snapshot.get("work_item")
if (
type(snapshot.get("schema_version")) is not int
or snapshot["schema_version"] != 1
or not isinstance(item, dict)
or not isinstance(item.get("objective"), str)
or not item["objective"].strip()
or not isinstance(item.get("input"), dict)
or not isinstance(snapshot.get("transfers"), list)
):
return
# Notification tenancy comes only from the persisted Task. Queue entries
# carry references, never prompt inputs, transfer payloads or model output.
with self._session.no_autoflush:
task = self._session.get(TaskRecord, run.task_id)
subtask = self._session.get(SubtaskRecord, run.subtask_id)
if (
task is None
or task.execution_mode != TaskExecutionMode.COORDINATED.value
or subtask is None
or subtask.task_id != task.id
):
return
events: list[tuple[str, datetime]] = []
if not previously_pinned:
events.append(("COLLAB_STARTED", run.work_item_pinned_at))
if previous_status != run.status.value:
if run.status is RunStatus.SUCCEEDED:
events.append(("COLLAB_RESULT", run.completed_at or run.work_item_pinned_at))
elif run.status in {RunStatus.FAILED, RunStatus.CANCELED}:
events.append(("COLLAB_FAILED", run.completed_at or run.work_item_pinned_at))
for event_kind, occurred_at in events:
self._session.execute(
insert(FeishuNotificationRecord)
.values(
id=uuid5(NAMESPACE_URL, f"agentmesh:feishu:run:{run.id}:{event_kind}"),
tenant_id=task.tenant_id,
subject_type="TASK_RUN",
subject_id=run.id,
subject_revision=1,
event_kind=event_kind,
status="PENDING",
created_at=occurred_at,
available_at=occurred_at,
claimed_by=None,
claimed_until=None,
attempt_count=0,
delivered_at=None,
last_error=None,
)
.on_conflict_do_nothing()
)

def list_for_task(self, task_id: UUID, *, for_update: bool = False) -> list[TaskRun]:
statement = (
Expand Down
13 changes: 12 additions & 1 deletion src/agentmesh/infrastructure/postgres/uow.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ def __init__(
session_factory: sessionmaker[Session],
*,
feishu_notifications_enabled: bool = False,
feishu_collaboration_enabled: bool = False,
) -> None:
self._session_factory = session_factory
self._feishu_notifications_enabled = feishu_notifications_enabled
self._feishu_collaboration_enabled = (
feishu_notifications_enabled and feishu_collaboration_enabled
)

def __enter__(self) -> SqlAlchemyUnitOfWork:
self._session = self._session_factory()
Expand All @@ -118,7 +122,9 @@ def __enter__(self) -> SqlAlchemyUnitOfWork:
self._session
)
self.handoffs = SqlAlchemyHandoffRepository(self._session)
self.runs = SqlAlchemyTaskRunRepository(self._session)
self.runs = SqlAlchemyTaskRunRepository(
self._session, feishu_collaboration_enabled=self._feishu_collaboration_enabled
)
self.runtimes = SqlAlchemyRuntimeRepository(self._session)
self.runtime_comparisons = SqlAlchemyRuntimeComparisonRepository(self._session)
self.attempts = SqlAlchemyTaskAttemptRepository(self._session)
Expand Down Expand Up @@ -177,12 +183,17 @@ def __init__(
session_factory: sessionmaker[Session],
*,
feishu_notifications_enabled: bool = False,
feishu_collaboration_enabled: bool = False,
) -> None:
self._session_factory = session_factory
self._feishu_notifications_enabled = feishu_notifications_enabled
self._feishu_collaboration_enabled = (
feishu_notifications_enabled and feishu_collaboration_enabled
)

def __call__(self) -> SqlAlchemyUnitOfWork:
return SqlAlchemyUnitOfWork(
self._session_factory,
feishu_notifications_enabled=self._feishu_notifications_enabled,
feishu_collaboration_enabled=self._feishu_collaboration_enabled,
)
Loading
Loading