Merge origin/main into docs/tess-ledger-sync-m1
Some checks failed
ci/woodpecker/pr/ci Pipeline was canceled
Some checks failed
ci/woodpecker/pr/ci Pipeline was canceled
Resolve tracking-doc divergence for the M4-sync PR (#741): - TASKS.md: keep the sole-writer ledger (strict superset — every row equal-or-more-advanced than main, plus the M4-W running log). - VERIFICATION-MATRIX.md: take main's newer baseline (evolved AC-TESS-04 native-port handoff / M4-001+M4-V gate) and re-apply the AC-TESS-05 TESS-MEM-001 operator-memory reachability mapping on top. - MISSION-MANIFEST.md: M4 in-progress (auto-merged). M4 remains in-progress/gate-pending — M4-V has NOT passed (2 gate FAILs; wave-2 linchpin #740 in review). No source changes; command-authz untouched.
This commit is contained in:
@@ -41,7 +41,7 @@ Every call receives an immutable, server-derived actor/tenant/channel scope and
|
||||
|
||||
`@mosaicstack/agent` owns the explicit `AgentRuntimeProviderRegistry`; duplicate provider IDs are rejected rather than replaced. Gateway owns `RuntimeProviderService`, which creates a frozen `RuntimeScope` from authenticated `ActorTenantScope` and trusted ingress channel/correlation metadata before every provider call. The service checks the declared provider capability before invoking a side effect and records metadata-only audit events (`providerId`, operation, outcome, actor/tenant/channel, correlation, and resource ID). It never records message bodies, idempotency keys, or approval references.
|
||||
|
||||
Termination is fail-closed: a runtime approval verifier must consume a one-time, exact action binding for the provider, session, actor, tenant, channel, and correlation ID before `terminate` reaches a provider. Until the durable verifier is wired, the default verifier denies termination. This internal service introduces no HTTP endpoint; later Discord, CLI, MCP, and provider adapters consume the same gateway boundary.
|
||||
Termination is fail-closed: a runtime approval verifier consumes a one-time, exact action binding for the provider, session, actor, tenant, channel, and correlation ID before `terminate` reaches a provider. The verifier reuses the Redis-backed `interaction:command-approval:*` store and its expiry/delete-on-consume semantics; it has no parallel approval store. This internal service introduces no HTTP endpoint; later Discord, CLI, MCP, and provider adapters consume the same gateway boundary.
|
||||
|
||||
## Authority Model
|
||||
|
||||
@@ -52,11 +52,49 @@ Termination is fail-closed: a runtime approval verifier must consume a one-time,
|
||||
| Destructive, privileged, external/customer-visible action | Human approval + policy | Propose, wait for durable one-time approval, then execute idempotently |
|
||||
| Provider-specific unsupported action | None | Fail closed; never emulate silently |
|
||||
|
||||
### Mos Coordination Boundary
|
||||
|
||||
`@mosaicstack/coord` exposes only the transport-neutral `MosCoordinationPort`
|
||||
verbs `handoff`, `observe`, and `result`. Gateway derives the actor, tenant,
|
||||
correlation, and interaction-agent identity from authenticated context plus
|
||||
trusted configuration; callers never provide an orchestration target. It
|
||||
rejects unconfigured identities, self-delegation, target/correlation drift, and
|
||||
cross-tenant handoff reads before an adapter call. No dispatch, assignment,
|
||||
review, merge, or cancellation API exists at this boundary.
|
||||
|
||||
M4 uses a deterministic native in-process queue adapter to prove the handoff →
|
||||
observe → result flow without coupling the contract to tmux. A fleet/tmux
|
||||
adapter is deferred to the M5 live-deployment seam and must implement the same
|
||||
port.
|
||||
|
||||
## Session and State Model
|
||||
|
||||
A Tess session has stable `sessionId`, `tenantId`, `ownerId`, provider/runtime identity, ingress bindings, cursor, checkpoint, inbox/outbox, and idempotency records. Discord and CLI bind to the same authorized session. Ownership is verified server-side on every list/read/attach/send/terminate operation.
|
||||
|
||||
Valkey may hold ephemeral coordination state; PostgreSQL is canonical for durable session bindings, approvals, audit, checkpoints, inbox/outbox, and idempotency. Pi session files are replay sources, not cross-agent truth.
|
||||
Valkey holds the existing short-lived, one-time command-approval records; PostgreSQL is canonical for durable session bindings, checkpoints, inbox/outbox, and idempotency. Pi session files are replay sources, not cross-agent truth.
|
||||
|
||||
### M2 Durable Recovery
|
||||
|
||||
`@mosaicstack/agent` owns a transport-neutral state machine and `apps/gateway` provides its
|
||||
PostgreSQL adapter. `interaction_sessions` holds immutable identity; inbox/outbox records use a
|
||||
per-session unique idempotency key and transition `pending → processing → processed|delivered`.
|
||||
Checkpoints are immutable history scoped by session and checkpoint ID: the latest checkpoint
|
||||
supports compaction recovery, while a handoff always resolves the exact checkpoint it references.
|
||||
Recovery requeues only interrupted inbox work; an ambiguous `processing` outbox record is preserved
|
||||
until separately authorized reconciliation can establish its external delivery state.
|
||||
|
||||
Provider sends travel through the existing `RuntimeProviderService` with the persisted outbox
|
||||
idempotency key. A normal dispatch claims exactly one outbox record and verifies its stored
|
||||
correlation and channel against the server-derived request scope; it never requeues or drains
|
||||
another live record. Inbox/outbox payloads and checkpoint cursor/summary pass through the existing
|
||||
secret/PII redactor and AES-256-GCM sealing before persistence; decryption occurs only in the
|
||||
scoped gateway repository path, and runtime audit remains metadata-only.
|
||||
|
||||
An external effect cannot share a database transaction. If a process dies after an effect begins
|
||||
but before its terminal outbox transition, automatic recovery does not replay that ambiguous claim.
|
||||
It remains `processing` until separately authorized reconciliation can establish delivery state;
|
||||
completed effects are never redispatched. Operators can therefore restart the gateway/Pi service,
|
||||
reconstruct the session, and resume pending inbox work without relying on process-local state.
|
||||
|
||||
## Transport Strategy
|
||||
|
||||
@@ -81,3 +119,5 @@ The provider advertises list, tree, read-only attach, send, and terminate. List/
|
||||
## Deployment
|
||||
|
||||
Tess runs as a rostered, systemd-supervised Pi agent using GPT-5.6 Sol and high reasoning. Secrets are supplied through approved runtime secret mechanisms. Startup fails when required model, gateway identity, Discord binding, or durable-state dependencies are missing. Health reports effective model/reasoning/tool policy without credential material.
|
||||
|
||||
The interaction-service identity is provisioning data, not a source identifier: the roster and per-agent environment carry the chosen display/roster name into a generic systemd instance. The service rejects a name mismatch or any drift from its pinned Pi/GPT-5.6 Sol/high/operator-interaction effective policy before launch. Its policy printer exposes only those resolved safe fields.
|
||||
|
||||
17
docs/tess/M4-003-OPERATOR-PLUGIN-SKETCH.md
Normal file
17
docs/tess/M4-003-OPERATOR-PLUGIN-SKETCH.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# TESS-M4-003 Operator Plugin Sketch
|
||||
|
||||
## Memory/retrieval slice — TESS-MEM-001
|
||||
|
||||
Introduce a transport-neutral `OperatorMemoryPlugin` in `packages/memory`. The plugin receives a server-derived `{tenantId, ownerId, sessionId}` scope and delegates to a registered `MemoryAdapter`; adapter and namespace are injected configuration, never caller input. Its operations are `capture`, `search`, `recent`, `stats`, and `startupContext`. Results carry configured instance, provenance, and namespace metadata. Capture/redaction occurs before adapter persistence; startup context uses a bounded candidate window ordered so project/flat-file truth takes precedence within returned material.
|
||||
|
||||
Registration remains replaceable-adapter based: the existing `registerMemoryAdapter(kind, factory)` / `createMemoryAdapter(config)` seam supplies the injected adapter to `createOperatorMemoryPlugin(config)`. Identity and namespace are configuration data; no interaction-agent name is embedded in keys or defaults.
|
||||
|
||||
## Remaining plugin foundations — TESS-PLG-001
|
||||
|
||||
- `packages/agent`: capability descriptors for runtime bootstrap, durable inbox/state hooks, and read-only fleet diagnostics. Each capability advertises supported operations and fails closed when absent.
|
||||
- `packages/mosaic`: a catalog/registration surface for GitOps, fleet diagnostics, runtime bootstrap, Discord, and MCP/skill discovery. Catalog entries describe authority, input schema, and safe/read-only status; they do not invoke provider transports directly.
|
||||
- Gateway/channel adapters consume these contracts through server-derived actor/tenant context and durable session state, preserving the replaceable-adapter boundary.
|
||||
|
||||
## First implementation boundary
|
||||
|
||||
The first PR slice should add the operator-memory plugin contract, configuration-injected adapter seam, scope isolation, provenance-bearing retrieval, and tests for namespace isolation plus a differently named configured instance. Durable inbox/outbox remains owned by the existing `DurableSessionCoordinator`; this plugin only supplies bounded context/capture at lifecycle boundaries.
|
||||
83
docs/tess/MOS-COORDINATION.md
Normal file
83
docs/tess/MOS-COORDINATION.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Tess–Mos Coordination Contract Sketch
|
||||
|
||||
**Task:** TESS-M4-001 · **PRD:** TESS-MOS-001 / AC-TESS-04
|
||||
|
||||
## Boundary
|
||||
|
||||
Agent identities are deployment data. A configured interaction agent may request
|
||||
Mos-owned work; the configured orchestration agent owns decomposition, worker
|
||||
assignment, reviews, and merge decisions. The interaction agent receives a
|
||||
correlated receipt, read-only activity projection, and terminal result. It has
|
||||
no dispatch, assignment, review, merge, or cancellation operation.
|
||||
|
||||
## `@mosaicstack/coord` interface
|
||||
|
||||
```ts
|
||||
interface CoordinationScope {
|
||||
readonly actorId: string;
|
||||
readonly tenantId: string;
|
||||
readonly correlationId: string;
|
||||
readonly requesterAgentId: string; // trusted gateway/configuration data
|
||||
}
|
||||
|
||||
interface MosHandoffRequest {
|
||||
readonly idempotencyKey: string;
|
||||
readonly summary: string;
|
||||
readonly context?: string;
|
||||
readonly missionId?: string;
|
||||
}
|
||||
|
||||
interface MosHandoffReceipt {
|
||||
readonly handoffId: string;
|
||||
readonly targetAgentId: string;
|
||||
readonly status: 'accepted' | 'queued';
|
||||
readonly correlationId: string;
|
||||
}
|
||||
|
||||
interface MosHandoff {
|
||||
readonly handoffId: string;
|
||||
readonly targetAgentId: string;
|
||||
readonly request: MosHandoffRequest;
|
||||
readonly scope: CoordinationScope;
|
||||
}
|
||||
|
||||
interface MosCoordinationPort {
|
||||
handoff(handoff: MosHandoff): Promise<MosHandoffReceipt>;
|
||||
observe(handoffId: string, scope: CoordinationScope): Promise<CoordinationObservation>;
|
||||
result(handoffId: string, scope: CoordinationScope): Promise<CoordinationResult>;
|
||||
}
|
||||
```
|
||||
|
||||
The port deliberately omits generic orchestrator verbs. It is tenant- and
|
||||
correlation-scoped; its gateway implementation obtains `actorId`, `tenantId`,
|
||||
and the requester agent from trusted authentication/configuration only.
|
||||
|
||||
## Enforcement point
|
||||
|
||||
`apps/gateway` owns a `MosCoordinationService` boundary that compares the
|
||||
trusted configured requester/target identities and rejects all of the following
|
||||
before calling a transport: unconfigured requester, self-delegation, target
|
||||
identity drift, cross-tenant observe/result lookup, and attempts to observe or
|
||||
receive a result for a handoff outside the originating tenant. The service exposes handoff, observe,
|
||||
and result only, and delegates delivery to an injected adapter.
|
||||
|
||||
M4 ships a native in-process `InMemoryMosCoordinationPort` as the concrete,
|
||||
deterministic adapter. It preserves the immutable handoff ID, tenant, requester
|
||||
identity, and correlation ID while demonstrating the handoff → observe → result
|
||||
round trip. It is a queue/port adapter, not a Mos-side consumer.
|
||||
|
||||
A future fleet/tmux adapter is a documented M5 deployment seam and must
|
||||
implement the same `MosCoordinationPort`; no channel client or interaction
|
||||
runtime calls a transport directly.
|
||||
|
||||
## Required tests
|
||||
|
||||
1. A configured non-default interaction identity can hand off work to a
|
||||
configured non-default orchestration identity and receive its result.
|
||||
2. The gateway passes only server-derived scope/identity to the adapter.
|
||||
3. Self-targeting, target drift, and cross-tenant observe/result all fail closed
|
||||
without invoking the adapter.
|
||||
4. The exported public contract has no worker-dispatch, assignment, review,
|
||||
merge, or cancellation capability.
|
||||
5. The native adapter round-trips queued work, activity, and a host-recorded
|
||||
terminal result without a live fleet dependency.
|
||||
@@ -5,7 +5,7 @@
|
||||
| AC-TESS-01 | TESS-PI-001, TESS-DSC-001, TESS-CLI-001 | Discord/CLI same-session integration and streaming E2E | M3-V |
|
||||
| AC-TESS-02 | TESS-ARP-001, TESS-CLI-001, TESS-FLT-001 | CLI contract tests for status/sessions/tree/attach/send/stop, typed denial/error snapshots | M3-V |
|
||||
| AC-TESS-03 | TESS-PI-001, TESS-OBS-001 | Clean service launch; status asserts GPT-5.6 Sol, high reasoning and effective tool policy with secret canaries absent | M2-V, M3-V |
|
||||
| AC-TESS-04 | TESS-MOS-001, TESS-FLT-001 | Authority E2E: coding request creates Mos handoff; safe status runs in Tess; no competing worker claim | M4-V |
|
||||
| AC-TESS-04 | TESS-MOS-001, TESS-FLT-001 | M4 contract/gateway native-port handoff → observe → result round trip; configurable identity, target-drift and tenant-denial tests; M4-V fleet authority qualification | M4-001, M4-V |
|
||||
| AC-TESS-05 | TESS-HRM-001, TESS-MEM-001 | Hermes capability contract suite: sessions/stream/send/tree plus Kanban/skills/memory/tools/cron supported-or-denied matrix; operator-memory plugin (TESS-MEM-001) reachable end-to-end — env-configured plugin registered + AgentService session-bound server-derived {tenantId,ownerId,sessionId} scoped search/capture, cross-tenant reuse denied before plugin call (M4-W-001 spine: #736 plugin + #739 consumer) | M4-V |
|
||||
| AC-TESS-06 | TESS-STA-001, TESS-SEC-008 | Kill/restart/compaction fault injection across inbox/outbox/checkpoint transitions; duplicate side-effect detector | M2-V, M5-V |
|
||||
| AC-TESS-07 | TESS-SEC-001..009 | Threat-model abuse suite: authz, tenant isolation, forged identity/approval, injection, redaction, transport identity, GC scope | M1-V, M3-V, M5-V |
|
||||
|
||||
19
docs/tess/hermes-runtime-adapter-design.md
Normal file
19
docs/tess/hermes-runtime-adapter-design.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# TESS-HRM-001 — Hermes runtime adapter boundary
|
||||
|
||||
## Normalized provider surface
|
||||
|
||||
`HermesRuntimeProvider` implements the existing Mosaic-owned `AgentRuntimeProvider` unchanged. Its public surface is therefore `capabilities`, `health`, session list/tree, stream, send, attach/detach, and terminate, accepting only `RuntimeScope`, `RuntimeMessage`, `RuntimeSession`, `RuntimeStreamEvent`, and other types from `@mosaicstack/types`. Provider id is `runtime.hermes`.
|
||||
|
||||
The provider receives a narrow injected `HermesRuntimeTransport`, whose method names and inputs may represent Hermes API operations but whose return values are explicitly private `HermesLegacy*` types defined only in `packages/agent/src/hermes-runtime-provider.ts`. Mapping functions convert those private values to Mosaic sessions, state, hierarchy, and stream events. Capability negotiation maps a supplied Hermes feature inventory onto the fixed Mosaic runtime capability vocabulary; no unknown/ambiguous legacy feature is advertised. Unsupported Mosaic operations throw the typed fail-closed `capability_unsupported` provider error before a transport call.
|
||||
|
||||
## Boundary line
|
||||
|
||||
**Hermes legacy schema ends at `HermesRuntimeTransport` and its private adapter-local `HermesLegacy*` definitions in `packages/agent`.** `packages/types` is never changed to contain a Hermes field, enum, identifier, session shape, status, or capability. `apps/gateway` registers/resolves the provider only through `AgentRuntimeProvider` and receives normalized values only. Identity remains server-derived `RuntimeScope` data and is passed to the injected transport as context, never reconstructed from a legacy response.
|
||||
|
||||
## Initial mapping and safety posture
|
||||
|
||||
- Hermes conversation/thread identifiers map to opaque Mosaic `RuntimeSession.id`; parent linkage maps only when a known parent exists.
|
||||
- Hermes status strings map through a closed lookup to `RuntimeSessionState`; unknown statuses become `failed`, never a permissive active state.
|
||||
- Legacy stream chunks map to `message.delta` / `message.complete`; malformed or unsupported events become a normalized `runtime.error` event.
|
||||
- Send, attach, and terminate require the normalized capability first. `terminate` continues to be approval-bound by the gateway service; the adapter does not weaken gateway authority.
|
||||
- Kanban, skills, memory, tools, and cron are capability-inventory entries for this transitional adapter, not additions to the core runtime contract. They are reported as explicitly unsupported until a Mosaic-owned capability contract exists.
|
||||
Reference in New Issue
Block a user