macp-sdk-typescriptModes
Handoff Mode
Mode identifier: macp.mode.handoff.v1
Participant model: delegated
Determinism: context-frozen
Purpose
Transfer responsibility from one participant to another, with optional context sharing.
Session Lifecycle
SessionStart → HandoffOffer → HandoffContext? → HandoffAccept/HandoffDecline → CommitmentAPI
HandoffSession
import { HandoffSession } from 'macp-sdk-typescript';
const session = new HandoffSession(client);
await session.start({ intent: '...', participants: ['bob'], ttlMs: 60_000 });Methods
| Method | Message Type | Description |
|---|---|---|
offer(input) | HandoffOffer | Offer a handoff to a target participant |
sendContext(input) | HandoffContext | Provide context for the handoff |
acceptHandoff(input) | HandoffAccept | Accept the handoff |
decline(input) | HandoffDecline | Decline the handoff |
commit(input) | Commitment | Finalize the session |
Offer
await session.offer({
handoffId: 'h1',
targetParticipant: 'bob',
scope: 'frontend-ownership',
reason: 'moving to backend team',
});Send Context
Provide information the recipient needs:
await session.sendContext({
handoffId: 'h1',
contentType: 'application/json',
context: Buffer.from(JSON.stringify({
repository: 'acme/web-app',
documentation: 'https://wiki.acme.com/frontend',
contacts: ['design-team@acme.com'],
openIssues: 12,
})),
});Accept / Decline
// Target participant accepts
await session.acceptHandoff({
handoffId: 'h1',
acceptedBy: 'bob',
reason: 'ready to take ownership',
sender: 'bob',
auth: Auth.devAgent('bob'),
});
// Or declines
await session.decline({
handoffId: 'h1',
declinedBy: 'bob',
reason: 'no bandwidth this quarter',
sender: 'bob',
auth: Auth.devAgent('bob'),
});HandoffProjection
State
| Property | Type | Description |
|---|---|---|
handoffs | Map<string, HandoffRecord> | Handoffs with status tracking |
transcript | Envelope[] | All accepted envelopes |
phase | 'Offering' | 'ContextSharing' | 'Resolved' | 'Committed' | Current phase |
HandoffRecord Status
| Status | Meaning |
|---|---|
offered | Handoff proposed, awaiting response |
context_sent | Additional context provided |
accepted | Target accepted the handoff |
declined | Target declined the handoff |
Query Helpers
session.projection.getHandoff('h1'); // full HandoffRecord
session.projection.isAccepted('h1'); // true after HandoffAccept
session.projection.isDeclined('h1'); // true after HandoffDecline
session.projection.pendingHandoffs(); // handoffs in offered/context_sent statusRFC Validation Rules
- Every
handoff_ididentifies one specific offer HandoffContext/HandoffAccept/HandoffDeclinemust reference an existinghandoff_id- Accept/Decline must come from the offer's
target_participant - Only one final accept per
handoff_id - A session may contain multiple serial handoff offers, but only one final Commitment
Context-Frozen Determinism
Handoff mode uses context-frozen determinism — semantic determinism holds only if the external context bound at SessionStart is replayed exactly. The context provided via HandoffContext messages is part of this frozen state.