-
Notifications
You must be signed in to change notification settings - Fork 61
fix(cli): expose direct-message enqueue state #1468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| export type DirectMessageMode = 'wait' | 'steer'; | ||
|
|
||
| export type DirectMessageDeliveryReceipt = Record<string, unknown> & { | ||
| target?: { kind: 'agent'; agentName: string }; | ||
| delivery: { | ||
| status: 'queued_unconfirmed' | 'recipient_mismatch' | 'recipient_unresolved'; | ||
| mode: DirectMessageMode; | ||
| requestedRecipient: string; | ||
| resolvedRecipient: string | null; | ||
| recipientMatched: boolean | null; | ||
| readConfirmed: false; | ||
| note: string; | ||
| }; | ||
| }; | ||
|
|
||
| function asRecord(value: unknown): Record<string, unknown> { | ||
| return typeof value === 'object' && value !== null && !Array.isArray(value) | ||
| ? (value as Record<string, unknown>) | ||
| : { value }; | ||
| } | ||
|
|
||
| /** Resolve only a full, exact agent name; never fall back to a prefix. */ | ||
| export function resolveExactAgentName(agents: readonly unknown[], requestedRecipient: string): string { | ||
| const resolvedRecipient = agents | ||
| .map((agent) => { | ||
| const name = asRecord(agent).name; | ||
| return typeof name === 'string' ? name : undefined; | ||
| }) | ||
| .find((name) => name === requestedRecipient); | ||
| if (!resolvedRecipient) { | ||
| throw new Error(`Recipient "${requestedRecipient}" was not found by exact agent-name match.`); | ||
| } | ||
| return resolvedRecipient; | ||
| } | ||
|
|
||
| /** | ||
| * Add the delivery facts that Relaycast's create-message response does not | ||
| * contain. A message id confirms durable enqueue only; delivery/read | ||
| * confirmation remains observable through get_message_readers. The resolved | ||
| * recipient must come from an independent directory lookup; the created | ||
| * message's target may only echo the request and is deliberately not trusted. | ||
| */ | ||
| export function directMessageReceipt( | ||
| value: unknown, | ||
| requestedRecipient: string, | ||
| mode: DirectMessageMode = 'wait', | ||
| resolvedRecipient?: string | ||
| ): DirectMessageDeliveryReceipt { | ||
| const message = asRecord(value); | ||
| const messageWithoutUntrustedTarget = { ...message }; | ||
| delete messageWithoutUntrustedTarget.target; | ||
| const recipientMatched = resolvedRecipient ? resolvedRecipient === requestedRecipient : null; | ||
| const status = | ||
| recipientMatched === null | ||
| ? 'recipient_unresolved' | ||
| : recipientMatched | ||
| ? 'queued_unconfirmed' | ||
| : 'recipient_mismatch'; | ||
| const note = | ||
| recipientMatched === null | ||
| ? `Recipient resolution was unavailable for ${requestedRecipient}; enqueue is not reported as successful delivery.` | ||
| : recipientMatched | ||
| ? mode === 'steer' | ||
| ? 'Queued as an immediate injection request that may interrupt active work. This receipt does not confirm delivery or reading; call get_message_readers with the message id.' | ||
| : "Queued for injection at the recipient's next safe idle boundary. It can remain unread while the recipient is busy. This receipt does not confirm delivery or reading; call get_message_readers with the message id." | ||
| : `Recipient mismatch: requested ${requestedRecipient}, but the directory resolved ${resolvedRecipient}.`; | ||
|
|
||
| return { | ||
| ...messageWithoutUntrustedTarget, | ||
| ...(resolvedRecipient ? { target: { kind: 'agent' as const, agentName: resolvedRecipient } } : {}), | ||
| delivery: { | ||
| status, | ||
| mode, | ||
| requestedRecipient, | ||
| resolvedRecipient: resolvedRecipient ?? null, | ||
| recipientMatched, | ||
| readConfirmed: false, | ||
| note, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export function messageReadersReceipt(readers: unknown[]): { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The new Prompt for AI agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 49f8ba3. CLI message inbox get_readers now emits the same read or queued_or_unread receipt contract as the MCP tool, with a dedicated regression. |
||
| readers: unknown[]; | ||
| delivery: { status: 'read' | 'queued_or_unread'; readConfirmed: boolean; signal: string }; | ||
| } { | ||
| const readConfirmed = readers.length > 0; | ||
| return { | ||
| readers, | ||
| delivery: { | ||
| status: readConfirmed ? 'read' : 'queued_or_unread', | ||
| readConfirmed, | ||
| signal: readConfirmed | ||
| ? 'At least one agent has read this message.' | ||
| : 'No agent has read this message. A send receipt confirms enqueue only; the recipient may still be busy or offline.', | ||
| }, | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.