@baremetallabs-ai/agent-apps-eve
v0.3.2
Published
This prebuilt eve extension connects an eve agent to the owner's installed AgentApps tools through the AgentApps cell. It can be published independently; npm publication is handled separately.
Readme
AgentApps for eve
This prebuilt eve extension connects an eve agent to the owner's installed AgentApps tools through the AgentApps cell. It can be published independently; npm publication is handled separately.
Install and mount
Extension 0.3.2 requires Node 24 or newer, eve 0.74.x, and ai >=7.0.128. The declared peer range is >=0.74.0 <0.75.0; later patches require their own packed consumer checks.
npm install @baremetallabs-ai/[email protected] [email protected] [email protected] [email protected]Create agent/extensions/apps.ts:
import apps from '@baremetallabs-ai/agent-apps-eve';
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
});Use a sandbox provider explicitly. For JustBash, create agent/sandbox.ts:
import { defineSandbox } from 'eve/sandbox';
import { JustBashSandbox } from 'eve/sandbox/just-bash';
export const environment = JustBashSandbox.environment();
export default defineSandbox(() => environment.open());Set the cell address and owner-issued bearer in the agent's environment. Paste the full https://<host>/c/<cell-id>/mcp cell address shown beside the owner credential in AgentApps. The per-cell base form https://<host>/c/<cell-id> also works; either form may have a trailing slash and reaches the same cell. A bare shared host, malformed path, embedded credentials, query string, or fragment is rejected when eve loads the mount. Keep credentials in the environment and out of prompts, logs, and shell commands.
Optional approval for cell tools
Approval is opt-in. Without approval, installed cell tools keep their existing unprompted behavior. To ask a person before each call outside a trusted read-only exemption, add:
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
approval: {
response: ({ response }) =>
response.principal.authenticator === 'http-basic' &&
response.principal.principalId === process.env.EVE_OWNER_USERNAME
? { status: 'allowed' }
: { status: 'rejected', reason: 'Owner or authorized delegate required.' },
},
});You can explicitly trust annotations from named installed app aliases and set a policy for other calls:
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
approval: {
response: ({ response }) =>
response.principal.authenticator === 'http-basic' &&
response.principal.principalId === process.env.EVE_OWNER_USERNAME
? { status: 'allowed' }
: { status: 'rejected', reason: 'Owner or authorized delegate required.' },
trustedAnnotations: ['google-ads'],
policy: (ctx) => (ctx.toolName.endsWith('__report') ? 'approved' : 'user-approval'),
},
});Authenticate the Eve decision surface in agent/channels/eve.ts:
import { eveChannel } from 'eve/channels/eve';
import { httpBasic } from 'eve/channels/auth';
export default eveChannel({
auth: httpBasic({
username: process.env.EVE_OWNER_USERNAME!,
password: process.env.EVE_OWNER_PASSWORD!,
}),
});Use an authenticated Eve client for the complete decision interaction, including snapshots and response streams:
import { Client } from 'eve/client';
const client = new Client({
host: process.env.EVE_URL!,
auth: {
basic: {
username: process.env.EVE_OWNER_USERNAME!,
password: process.env.EVE_OWNER_PASSWORD!,
},
},
});
const session = client.sessions.attach(sessionId);
await (await session.respond([{ requestId, optionId: 'approve' }])).result();
// Use optionId: 'cancel' to cancel with the same response authority.Both Approve and Cancel run the explicit response rule. Extend it using your verified delegate identities and authentication rules; JWT/OIDC rules also check the verified issuer. Never authorize by a generic user type, requester, initiator, or notification sender alone. A signed notification authorizes delivery and grants no decision authority. Missing approval.response fails mount validation. For a response rule that needs a provider sign-in, use ctx.auth.getToken(provider) and ctx.auth.requireAuth(provider) to request Eve's native authentication challenge; obtaining a token does not replace the owner/delegate principal check. Policy exceptions and authentication challenges fail closed; a rejected response leaves the request open for an authorized person.
Only a tool from this mount's own explicitly trusted app alias with explicit readOnlyHint: true and destructiveHint: false bypasses approval. The exemption wins over the policy. Missing, unknown, contradictory, and untrusted declarations follow the policy; with no policy they request human approval. An absent read-only hint defaults to false, and an absent destructive hint defaults to true. run_script, including alias-qualified alias__run_script, is one proposed action with its nested calls and never receives the annotation exemption. An explicit consumer policy can still authorize scripts and other actions automatically.
For an "always approve" choice within one session, use Eve's once() policy. It remembers identical normalized cell endpoint, full descriptor, and parsed input within this mount/session. Key ordering does not change the action; changed arguments or descriptors require normal fresh authorization:
import { once } from 'eve/tools/approval';
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
approval: {
response: ({ response }) =>
response.principal.authenticator === 'http-basic' &&
response.principal.principalId === process.env.EVE_OWNER_USERNAME
? { status: 'allowed' }
: { status: 'rejected', reason: 'Owner or authorized delegate required.' },
policy: once(),
},
});For a persistent rule, supply a consumer policy that returns 'approved' for specific tool names, such as ctx.toolName === 'apps__cell__google-ads__report', and 'user-approval' otherwise. Trusted read-only exemptions still take precedence over either policy.
An app's read-only hint is the app author's advisory claim. Approval is a human decision at call time, not durable access enforcement. For Google Ads, give the app credential access only to intended customer accounts and a user role whose permissions match the operations the agent may perform, even when trusted read tools skip prompts. Pending requests can be answered through Eve's existing approval surface, including when Mail or Phone started the turn; approval rendering inside those conversations is separate.
Declared local subagents
Mount apps independently at the root and at each declared local subagent. Consumers supply
usable external AgentApps agents, cells, and restricted app credentials. Root copies, remote
agents, and arbitrary mount names are outside this support claim. Eve 0.74 uses the same subagent
channel for declared children and built-in root copies. A copy inherits the root mount; when that
mount enables notifications, AgentApps tools are unavailable in the copy in either approval mode.
Keep notification work at the root and delegate AgentApps work to an independently configured
child with notifications disabled. The copy cannot remove notifications from the inherited mount
without changing the root configuration.
For a marketing assistant, external agent A handles notifications, automatic Mail questions,
and non-sensitive installs. External agent B has only sensitive Google Ads installs and serves
a paid-search child with notifications disabled. Use distinct identities and credentials, rather
than two cell processes sharing one bearer. Approval is recommended for sensitive Ads operations,
but remains optional on each mount. Removing approval preserves the unprompted native
connection_search / connection_execute experience regardless of the other mount's policy.
Create agent/agent.ts and agent/instructions.md:
import { defineAgent } from 'eve';
export default defineAgent({ model: process.env.EVE_MODEL! });Handle marketing notifications and non-sensitive work. Delegate paid-search work to paid-search.Create agent/extensions/apps.ts for external agent A:
import apps from '@baremetallabs-ai/agent-apps-eve';
export default apps({
cellUrl: process.env.MARKETING_A_CELL_URL!,
bearer: process.env.MARKETING_A_BEARER!,
notifications: {
agentId: process.env.MARKETING_A_AGENT_ID!,
currentSecret: process.env.MARKETING_A_OUTBOUND_SECRET!,
allowOriginatingSessionAdoption: true,
attemptStorePath: process.env.MARKETING_A_ATTEMPT_STORE_PATH!,
},
});Create agent/subagents/paid-search/agent.ts and its own instructions.md:
import { defineAgent } from 'eve';
export default defineAgent({
description: 'Manage paid-search campaigns using only the dedicated Google Ads agent.',
model: process.env.PAID_SEARCH_MODEL!,
});Use your dedicated Google Ads installs. Ask the human when needed.
Return only the authored campaign summary the marketing agent needs; protect sensitive details.Create agent/subagents/paid-search/extensions/apps.ts for external agent B:
import apps from '@baremetallabs-ai/agent-apps-eve';
export default apps({
cellUrl: process.env.PAID_SEARCH_B_CELL_URL!,
bearer: process.env.PAID_SEARCH_B_BEARER!,
// Recommended for sensitive Ads operations; omit approval for an ungated child.
approval: {
response: ({ response }) =>
response.principal.authenticator === 'http-basic' &&
response.principal.principalId === process.env.EVE_OWNER_USERNAME
? { status: 'allowed' }
: { status: 'rejected', reason: 'Paid-search owner or delegate required.' },
},
// Omit notifications: the child uses Eve's ordinary human-input experience.
});Create both agent/sandbox.ts and agent/subagents/paid-search/sandbox.ts using the explicit
JustBash sandbox snippet above. Authenticate the root's agent/channels/eve.ts using the
HTTP Basic example above; its authenticated conversation exposes the child's human decisions.
Set every referenced environment variable, including the model IDs and owner credentials.
No child channel, email forwarding, or SMS approval setup is required.
Each mount has its own installed catalog, instructions, connections, hooks, credentials,
configuration, durable state, annotation trust, request policy, and response authority. The root
receives no automatic child catalog or configuration; the child inherits no root extension
contributions. Each can independently configure approval.policy, trustedAnnotations, and
approval.response using the examples above. A root trust declaration or remembered grant cannot
authorize a child action, or vice versa. Annotation declarations are advisory, not access controls.
Ordinary authored delegated replies can return to the root. Consumers control sensitive reply
content; replies are not automatically redacted. Authorized humans see necessary action details.
A pending child approval is reachable from the parent conversation through Eve's existing authenticated decision experience, identifying the child and proposed action. Pending work stays pending and executes zero times. Unauthorized Approve or Cancel leaves it pending on the child session; see the known Eve limitation below for subsequent decisions. Authorized Cancel prevents execution and reports cancellation to the child; authorized Approve permits at most one execution of the original request, including after restart or repeated decisions. Root-model activity and notification delivery grant no decision authority. No child approvals are forwarded or settled through email or SMS. Changed input, descriptor, removal, or endpoint cannot use earlier authorization; later executable actions follow fresh child-policy authorization. Restart never automatically authorizes work.
Known Eve 0.74.0 limitation: when a decision arrives through the parent and the child
rejects it (unauthorized or anonymous), Eve retires the parent's copy of the request. The
request stays pending on the child session with zero executions. An authorized responder
answers it on the child session through Eve's authenticated client. The parent's
agent.started event gives that sessionId, matched by taskId. This is the supported
recovery behavior for v1; no framework patch or custom broker is supplied by this extension.
Immediate calls identify the executing child's own session and turn and use B's credentials. Approval-delayed calls preserve the original child attribution. Inherited parent notification or Mail authentication is not a child inbound trigger: root recipient IDs and inbound email facts are not substituted into child caller context.
Notifications and automatic Mail questions are root-only. Relayed child questions remain ordinary Eve human input and never enter the root's automatic Mail/Phone exchange. Enabling notifications on a child prevents cell discovery in either mode and supplies this exact diagnostic:
AgentApps notifications are unsupported in subagent runs, including declared children and root copies. Disable notifications on an independently configured child mount; a root copy inherits the root mount, so use a notification-disabled declared child for AgentApps work.The child's instructions require reporting the diagnostic through an ordinary delegated reply and stopping AgentApps work. Discovery guards prevent cell access; this is not a guarantee of a framework-generated error publication. Unrelated authored child capabilities remain available.
Notification receiver
To enable signed AgentMail and AgentPhone notifications, add the optional strict notifications setting to the mount:
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
notifications: {
agentId: process.env.AGENTAPPS_AGENT_ID!,
currentSecret: process.env.AGENTAPPS_OUTBOUND_SECRET!,
previousSecret: process.env.AGENTAPPS_PREVIOUS_OUTBOUND_SECRET || undefined,
allowOriginatingSessionAdoption: true,
attemptStorePath: process.env.AGENTAPPS_ATTEMPT_STORE_PATH || undefined,
},
});Configure the same URL, POST /agentapps/agents/:agentId/notifications, as the AgentApps destination for both Mail and Phone. Use the agent's outbound notification signing secret as currentSecret; no app-specific signer ID or inbound signing credential is needed. Send exact signed JSON bytes with X-Webhook-Signature: sha256=<hex HMAC> and Idempotency-Key. The path agent ID must match this mount. The optional previous secret remains valid exactly while configured. Keep it configured for at least five minutes after AgentApps switches signing, then remove it; eve imposes no separate expiration timer. After signature verification, the agentmail: or phone: correlation selects the app and its existing rules. A successful 201 JSON receipt contains sessionId, the destination actually used, and messageId, a stable logical notification ID rather than an eve internal message ID. Invalid payloads return 400; invalid signatures return 401; a wrong agent target returns 404; changed content under a recorded key returns 409; temporary or uncertain delivery returns 503 without a success receipt.
A mount without notifications returns 404 on this route. Configure a durable attempt store before sending production notifications; a bad store path is logged as a store failure and requests return 503.
Expose the receiver on a hosted site
The ordinary Eve API address, /eve/v1/* for an unnamed agent or /eve/<name>/v1/* for a named agent, does not expose the separate AgentApps channel destination. Route POST /agentapps/agents/:agentId/notifications to the Eve service that mounts this extension. Here :agentId is the external AgentApps ext-… agent ID, not the Eve workspace name.
For example, suppose https://assistant.example.com hosts a named Eve agent support. Set AGENTAPPS_AGENT_ID on that agent to its AgentApps external ID, ext-11111111-1111-4111-8111-111111111111. Supply the owner-issued AgentApps outbound notification signing secret through AGENTAPPS_OUTBOUND_SECRET; the mount above reads it as currentSecret. These are example identifiers and environment variable names, not credentials to copy into the file. Set the real secret in the Eve service environment.
For a Next.js-hosted site using withEve from eve/next, add this root vercel.json route before any site fallback. The named agent's generated service is eve-support in this example; the service destination preserves the original request path:
{
"routes": [
{
"src": "^/agentapps/agents/ext-11111111-1111-4111-8111-111111111111/notifications$",
"destination": { "type": "service", "service": "eve-support" }
},
{ "handle": "filesystem" }
]
}If vercel.json already uses rewrites, redirects, headers, cleanUrls, or trailingSlash, convert those settings to compatible routes or move them to next.config; Vercel does not allow them alongside routes in the same file.
If the site instead composes an Eve workspace with peer services using withEve from eve/vercel, author the same route in the root vercel.ts, ahead of the web catch-all:
import { withEve } from 'eve/vercel';
export default await withEve({
services: {
web: { framework: 'nextjs', root: 'apps/web' },
},
routes: [
{
src: '^/agentapps/agents/ext-11111111-1111-4111-8111-111111111111/notifications$',
destination: { type: 'service', service: 'eve-support' },
},
{ src: '^(.*)$', destination: { type: 'service', service: 'web' } },
],
});Use vercel.ts as the project's only Vercel configuration source in the peer-service pattern; do not add vercel.json alongside it. With either pattern, save this exact HTTPS destination for the support agent in AgentApps:
https://assistant.example.com/agentapps/agents/ext-11111111-1111-4111-8111-111111111111/notificationsFor a single unnamed agent, route its own external ID to generated service eve. For several agents on one public host, give each external ID an exact route to the corresponding Eve service and set that ID in that service's AGENTAPPS_AGENT_ID. A blanket ^/agentapps/(.*)$ route points to only one service, so it cannot deliver notifications to several Eve agents. Find the actual generated service keys in the withEve result or the post-build .vercel/output/config.json; do not assume every valid agent name maps literally to eve-<name>.
Author the route in the deployment configuration. Patching generated .vercel/output/config.json from next.config is ineffective because the Vercel Next adapter assembles the final service graph after the Next configuration runs.
To check the public route after deployment, send an unsigned POST to the exact destination saved in AgentApps:
curl -i -X POST 'https://assistant.example.com/agentapps/agents/ext-11111111-1111-4111-8111-111111111111/notifications'A JSON 401 response containing Authentication failed. confirms that the request reached the configured receiver. An HTML site or Next.js 404 page means the public route is missing. A JSON 404 containing Agent not found. means the path's external agent ID does not match that service's mount or notifications are not configured there. Any other 404, including a JSON error without Agent not found., can mean the route targets an Eve service that does not mount this extension; recheck the service name. This unsigned check establishes reachability only; it does not prove signed end-to-end delivery.
Ordinary notifications use a channel-owned agentapps: correlation address. A Phone continuation may target an existing channel-owned address or a named eve session owned by another channel. A missing local ownership snapshot is checked again after a brief settling interval; if it remains absent, delivery falls back to the Phone correlation address. A definitively inactive fixed session also falls back. Lookup errors, retryable inactivity, and uncertain delivery return 503. Separate channel context carries the verified app, correlation ID, optional Mail source message ID, idempotency key, and actual destination. Delivered authentication attributes expose recipientId, correlationId, optional validated sourceMessageId, and optional validated Phone senderNumber through @baremetallabs-ai/agent-apps-eve/notification-attributes. Delivery queues behind an active turn.
attemptStorePath is optional. When set, it must name durable storage that survives restarts and is used by one receiver host. Completed retries replay the stored receipt; expired in-progress attempts can be retried. Without a store, the receiver warns at startup and retries can duplicate delivery. The delivery guarantee is at-least-once with sender retries, best effort otherwise. An expired lease or uncertain acceptance can also duplicate delivery; this is not an exactly-once guarantee. Valid AgentPhone consult notifications are accepted and return the normal HTTP 201 delivery receipt. The selected recipientId is available in the next AgentApps tool call’s caller context. Full live phase 3 validation of the joined consult flow, cold start and reset, and timing under load remains pending; see eve interop acceptance.
Email tool approvals
Enable notifications.emailToolApprovals: true on each agent that needs email decisions. The
setting is off when absent or false, affects only that agent, and adds no approval requirements:
only existing conversation-owned apps__cell__ tool-approval requests get email prompts.
Child/subagent requests keep their existing decision surfaces.
Prompts reply to the originating email with its subject and correspondent. They identify the app, tool, and a short reference, and show declared argument structure (types, lengths, and counts). Long previews are truncated; scalar values, free text, URLs, nested keys, extra properties, and credential/secret fields are excluded. Full argument details remain in the web UI.
Only the entire newly authored reply can be a command: approve, cancel, approve <ref>, or
cancel <ref>. Case and surrounding whitespace are ignored. Quoted history, recognized signatures,
and prose supply no decision. A bare command works only with exactly one pending approval and no
ordinary question. With several approvals, use the prompt's 12-hex reference; unknown, missing,
ambiguous, or colliding references select no approval. A reference collision requires the web UI.
When an ordinary question is pending, a valid referenced command addresses its approval. Other replies, including bare commands and invalid references, retain the existing question-answer path. With no question pending, unclear eligible replies receive a short command instruction. Ineligible senders get no new prompt or correction. While an approval is pending, mail is not delivered to the agent unless it answers a pending ordinary question. This includes conversational follow-ups and owner-released mail; consumed mail is not redelivered after the approval settles. With no pending approvals, existing ordinary-mail behavior applies. Ordinary questions retain their current behavior whether this setting is enabled or not.
Approval senders must have a fetched, identity-checked inbound message in the bound mailbox/thread, with aligned sender authentication and recorded direct admission. Directly admitted aligned mail qualifies even without an active correspondent allowlist. Owner-released mail and legacy records without admission provenance do not qualify. A signed AgentApps notification alone does not confer human approval authority.
Eligible decisions pass through Eve's existing approval.response policy, including cancel. Email
uses response.principal.authenticator === 'agentapps-mail', principalType === 'email-participant',
and the fetched sender address as principalId. AgentMail stores sender addresses in lowercase;
use the lowercase address when deliberately allowing email participants. Unchanged web-only policies
can reject both email decisions. For example, preserve your existing web owner/delegate checks and
add a selected email identity:
apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
notifications: {
agentId: process.env.AGENTAPPS_AGENT_ID!,
currentSecret: process.env.AGENTAPPS_OUTBOUND_SECRET!,
allowOriginatingSessionAdoption: false,
emailToolApprovals: true,
},
approval: {
response: ({ response }) => {
const principal = response.principal;
const webAllowed =
principal.authenticator === 'http-basic' &&
[process.env.EVE_OWNER_USERNAME, process.env.EVE_DELEGATE_USERNAME].includes(
principal.principalId
);
const emailAllowed =
principal.authenticator === 'agentapps-mail' &&
principal.principalType === 'email-participant' &&
principal.principalId === '[email protected]';
return webAllowed || emailAllowed
? { status: 'allowed' }
: { status: 'rejected', reason: 'Authorized responder required.' };
},
},
});The web UI stays available and Eve's first accepted decision wins. There are no new outcome,
confirmation, cancellation, policy-rejection, or already-settled emails. Existing outbound
restrictions still apply; a held, failed, or missing prompt leaves the web UI as fallback. This
feature promises web-button parity only, with no additional retry, recovery, delivery, duplicate
prevention, restart finality, or decision-to-action guarantees. HTTP 201 for an approval reply
confirms submission or intentional consumption, without waiting for policy success or execution.
SMS questions and tool approvals
Enable notifications.smsInputs: true separately on each agent. It is off when absent or false.
Only identity-bearing, verified direct 1:1 AgentPhone text conversations can offer their own questions
and apps__cell__ tool approvals. Group texts, live calls, identity-free input, subagent/task requests,
and requests owned by other channels retain their existing surfaces. Disabling the setting stops SMS
responses; pending work stays accessible in the Eve web UI.
export default apps({
cellUrl: process.env.AGENTAPPS_CELL_URL!,
bearer: process.env.AGENTAPPS_BEARER!,
notifications: {
agentId: process.env.AGENTAPPS_AGENT_ID!,
currentSecret: process.env.AGENTAPPS_OUTBOUND_SECRET!,
allowOriginatingSessionAdoption: false,
smsInputs: true,
attemptStorePath: process.env.AGENTAPPS_ATTEMPT_STORE_PATH || undefined,
},
approval: {
response: ({ response }) => {
const principal = response.principal;
// Refuse phone decisions for risky actions, for both approve and cancel.
if (
principal.authenticator === 'agentapps-phone' &&
principal.principalType === 'phone-number'
) {
return { status: 'rejected', reason: 'Use an authenticated decision surface.' };
}
const webAllowed =
principal.authenticator === 'http-basic' &&
[process.env.EVE_OWNER_USERNAME, process.env.EVE_DELEGATE_USERNAME].includes(
principal.principalId
);
const emailAllowed =
principal.authenticator === 'agentapps-mail' &&
principal.principalType === 'email-participant' &&
principal.principalId === '[email protected]';
return webAllowed || emailAllowed
? { status: 'allowed' }
: { status: 'rejected', reason: 'Authorized responder required.' };
},
},
});A conversation has one offered SMS request. When its slot is free, the first eligible renderable new question or approval is offered in request order. Work skipped while occupied stays in the Eve web UI and is never promoted after settlement. SMS or web settlement frees the slot only for a subsequent newly requested input. A reply affects only the currently offered request; there is no special stale-reply confirmation.
Questions arrive in the same conversation as [AgentApps], a newline, the complete unchanged question,
and a closing [End of question] frame. The next eligible ordinary text supplies its actual fetched
body as the answer, preserving whitespace, newlines, and nonblank media captions. Notification summaries
remain ordinary activity. Exact-message lookup reads at most five history pages (250 messages on
the current Phone service). When the source is beyond that bound, the text follows ordinary
delivery and the offer remains pending for the Eve web UI. The originating message, carrier/opt-out controls, blank or media-only input,
held or owner-released input, and unverified or mismatched input cannot answer or decide an offer and
receive no digit reminder.
Approval prompts identify the app and full qualified tool name, show the same declared argument
structure as email (types, lengths, and counts), direct you to full details in the Eve web UI, and end
exactly Reply 1 to cancel, 2 to approve. Scalar values, nested keys, undeclared fields, credentials,
and caller context are excluded. Only the preview may be visibly truncated. A complete prompt that
cannot fit remains unanswered and web-only; questions are never shortened.
Only an entire body of 1 or 2 after trimming surrounding whitespace submits Cancel or Approve.
Eve applies the consumer's existing approval.response policy to both choices, with authenticator
agentapps-phone, principal type phone-number, and the verified correspondent number as principal
ID. An allowed approval permits the pending action; an allowed cancellation prevents it. HTTP 201
confirms dispatch, not policy acceptance or execution. A policy-rejected digit is consumed without
ordinary delivery, a refusal SMS, or sensitive detail to the agent; the same offer stays pending and
occupied, and later eligible digits may try again. Other eligible nonblank text remains ordinary queued
conversation activity and gets a short best-effort digit reminder on each message. That ordinary
activity can wait behind the approval. With no offer, existing ordinary texting behavior applies.
Existing Phone texting eligibility, opt-out, unread-input gates, receipts and bounded retries still apply. No further SMS is sent after opt-out. A definitively refused, failed, or unsent prompt releases only its own reservation and leaves unanswered web work. Uncertain sending retains the reservation until settlement. Neither outcome generates an answer, retries a prompt, times out an offer, or promotes skipped work.
The existing optional attemptStorePath persists the offered-request association across restart,
without another service or consumer setup. Reconciliation recognizes web settlement without resending
or promotion. Without that path, reservations use memory and recovery is best effort; pending Eve work
remains available in the web UI. A crash after reservation but before deferred sending may conservatively
occupy the slot until web settlement. This adds no stronger delivery or duplicate guarantee to the
existing optional-storage service level.
A signed carrier webhook or AgentApps notification establishes transport provenance, not personal
identity. Phone numbers can be spoofed. For risky approvals, refuse phone-number responders through
response policy for both approval and cancellation, as above, and use authenticated email when available
or the Eve web UI. Enable email decisions separately with emailToolApprovals: true if using the email
alternative. Preserve your authenticated owner/delegate checks. Individual SMS prompts carry no
additional warning.
AgentMail questions and replies
An AgentMail notification with a verified sourceMessageId can start an in-thread question exchange with or without attemptStorePath and without owner setup. Eve records questions and resolutions in the owning session's durable event stream. Each new question reads its originating message through the installed AgentMail cell tool and calls send_email with that message as reply_to_message_id, using its correspondent and subject. AgentMail's outbound policy and owner-approval gate still apply. The receiver selects the oldest unresolved question in the address session for an addressed response. The packed supported-version matrix remains the acceptance check for this behavior.
An owner-approval hold leaves the question pending. A failed send, policy refusal, or rejection while pending submits a labeled [AgentApps] notice with a safe category (delivery_failed, policy_refused, or owner_rejected) and states that no reply was received. A send still uncertain after 30 seconds uses delivery_unconfirmed. Notices exclude raw provider errors and secrets. Held-question ordering and late approval behavior require packed verification.
The answer is the message's real body from read_message, with HTML converted to readable text when plain text is absent. Its notification summary is never the answer. If no readable body exists, the agent receives an [AgentApps] content notice listing each attachment's filename, content type, byte size, and attachment ID. The agent can call apps__cell__agent-mail__get_attachment with attachment_id to obtain a fresh download link. The source message ID in the received and resolved stream events identifies retries across routine Eve restarts. Distinct mail after the pending queue drains arrives as ordinary mail. A definitively lost owner uses the ordinary-mail correlation address rather than another session's questions. A phone continuation skips a conversation with a pending question and delivers the update to its phone correlation conversation, leaving the original question pending.
HTTP 201 for a question answer follows an observed input.resolved; for ordinary mail it confirms durable inbox dispatch. Optional SQLite storage improves completed receipt replay and ordinary-mail duplicate protection; without it those protections are best effort. It is not required for question answer binding. With SMS inputs disabled or ownership excluded, each question in an AgentPhone-originated turn in an AgentApps-owned conversation receives a prompt [AgentApps] notice: the phone update cannot present it directly to the caller, so the agent can ask the live call's voice agent through answer_consult or use its AgentPhone contact tools. A question in a continuation owned by another channel remains with that channel and its recipient. Later caller speech, consultations, and excluded or non-opted-in texts and phone updates remain ordinary activity and never answer that question. Eligible opted-in direct SMS questions use the exchange above. A phone delivery receipt confirms only notification delivery. Approval-gated tool actions have separate behavior outside this exchange.
Mount this extension as apps. This supported mount makes the connection apps__cell; other mount names are unsupported because the 64-character tool-name guard uses the apps__cell__ prefix. Without approvals, use connection_search({ connection: 'apps__cell', query: 'report' }), then connection_execute({ connection: result.connection, tool: result.tool, input: args }) with the exact returned pair. The raw tool can be zeta__report. With approvals, call the dynamic apps__cell__zeta__report tool directly. Installed tools added between turns become discoverable on the next turn. The actual complete exposed tool name includes apps__cell__: exactly 64 characters is discoverable and callable; 65 or more is excluded from discovery and calls, while other tools remain usable. A declared child's separate invocation name does not impose a guessed fixed prefix budget.
Files returned by tools
When a tool returns a signed download link, ask eve's bash tool to fetch it into the agent workspace:
curl -L '<signed download URL>' -o /workspace/report.pdfWhen a tool supplies a signed upload URL, use the method and headers it specifies. For a PUT upload of a file already in /workspace:
curl -X PUT --upload-file /workspace/report.pdf '<signed upload URL>'Do not put signed links in durable instructions. They expire and may grant temporary access.
Compatibility and known limitation
The bounded peer range expresses compatibility intent for 0.74 only. The table below names exactly the patches CI verifies; later 0.74 patches are covered by the weekly current-eve check, not claimed as tested. The compiler is pinned to 0.74.0. No 0.66 or 0.67 compatibility is claimed for this release. Previously published packages remain available with their original claims.
CI-verified Eve patches: the complete packed real-Eve suite passes on each one in the supported-eve matrix on main and in the publish workflow.
| Eve patch | | --------- | | 0.74.0 |
A cell that becomes healthy within the first process check's 15-second limit (five seconds on subsequent checks) exposes tools on that turn. Otherwise that mount hides its stale tools for the current turn. An execution-time failure causes next-turn discovery to hide stale tools if its cell remains unavailable. Recovery restores tools at next-turn rediscovery. Healthy mounts continue independently; neither borrows the other's catalog or credentials.
Discovery and extension-owned approval-gated execution expose fixed category-specific guidance for cell, transport, and protocol failures:
| Category | Guidance |
| -------------------------- | ----------------------------------------------------- |
| credential rejected | Check this mount's credential. |
| timed out | Retry the action after a timeout. |
| unreachable | Check this mount's cell address and availability. |
| unexpected cell response | Contact the cell operator for an unexpected response. |
HTTP 403 is unexpected, not proof of a rejected credential. Well-formed app tool errors retain the app’s text so the model can recover, with this mount’s bearer stripped. They do not prove authentication rejection or timeout. Cell, transport, and protocol failures do not forward secrets, URLs, bodies, or raw provider errors. The extension never inserts its bearer into error text. Ungated native connection execution does not guarantee sanitization of provider errors; preserving that compatibility path does not provide the gated path's safety guarantee. Failed or uncertain gated dispatch is never retried automatically.
Pending and cancelled actions execute zero times; approved actions execute at most once. If an app action, input, descriptor, or endpoint changes or disappears, old authorization is invalidated with “AgentApps action changed or is unavailable; request it again for fresh authorization.” Request the action again and obtain its normal fresh authorization.
Repeating Approve or Cancel for a settled request authorizes nothing and never executes the original action again, including after restart. Eve may convert that repeat into new model input. If the model proposes another action, that is a distinct request which remains pending until separately decided. The repeat itself is not a successful decision. Legitimate later calls with identical details can still receive their own authorization. Restart within 0.74 grants no approval and executes no pending work; original context and intentional decision/answer handling remain available.
Ordinary notification summaries and Phone speech/text arrive as structured message content and never answer pending questions. With SMS inputs enabled, eligible direct replies instead answer the single offered question using the fetched actual body. Intentional threaded Mail body answers and direct Eve-user answers continue to work. Phone question origin and notice status live in owning channel state, so retry/restart recovery does not depend on process-local timers.
Manual upgrade from 0.66
- Keep the old deployment pinned to its old package/Eve versions. Finish or cancel outstanding approvals and questions there.
- Retain the old deployment's data and storage for its existing work and records.
- Install 0.3.2 with the prerequisites above and authenticated response authority. Start fresh 0.74 sessions with a fresh storage/session identity. Do not point 0.74 at pending old work.
The version-1 durable session reader rejects legacy sessions with “Start a new session.” A separate checkpoint migration path can upgrade a compatible idle checkpoint, but refuses pending input, approval candidates, and open turns. That distinction does not provide automatic continuation of active legacy work or a rollback guarantee.
AgentPhone text notifications use phone-text:<64 lowercase hex> correlations and
agentphone-text:<64 lowercase hex> idempotency keys. The relay selects AgentPhone for this
prefix and creates one channel-owned address per text conversation. Legacy text payloads contain only
correlationId and content. Complete payloads also carry sourceMessageId,
senderNumber, and an internal signed conversationBinding used to validate the sender
against the conversation. Eve exposes the two validated facts in authenticated turn
attributes. The cell caller context forwards source identity for AgentMail and authenticated direct Phone text,
with complete session, turn, and execution attribution. The validated phoneTextKind attribute
distinguishes direct and group identity-bearing texts; legacy texts and live calls have no direct classification. Texts do not request origin
adoption.
Maintainer release
Source: https://github.com/baremetallabs-ai/sovereign-ai. This prebuilt package versions independently from the notification contract. Its 0.3.2 release depends on the already public @baremetallabs-ai/agent-apps-notifications@^0.1.3. Commit a new extension manifest version and lockfile entry whenever published behavior or supported eve changes. To support newer eve, rebuild, test, update the bounded peer and README, and rebuild the builtWithEve manifest as needed, and increase the extension version before release. The weekly current-eve check is an early warning; it does not change supported eve 0.74.x or block a release for that accurate support claim.
The @baremetallabs-ai npm organization uses the existing short-lived NPM_TOKEN for manual releases. supported-eve.json lists the exact Eve patches CI verifies. The supported-eve matrix packs one candidate tarball pair and checks it on every listed patch. Pull requests run its fast tier (build, package checks, unit tests and one packed real-Eve smoke); pushes to main and every Eve publish run the complete real-Eve suite, and that passing run is the evidence. Notification publications run their own package checks without the real-Eve gate. Release checks verify the packed consumer on every listed patch and require this README table to match the list. To add a patch, add it to supported-eve.json and this table and let CI verify it. Additional repeat runs, the ordered release dry run, and live publication are post-merge orchestrator responsibilities. After merge, the orchestrator manually dispatches Publish AgentApps npm package with package=notifications, version=0.1.3, dry_run=false, then package=eve, version=0.3.2, dry_run=false. Normal merges and tags never publish. A dry run uses dry_run=true and cannot write to the registry.
A private source repository reports provenance disabled: private source repository and must pass every other gate. New releases from a public source require provenance. Historical private-source releases do not need retroactive attestation. Every exact registry version, including an already occupied one, must pass packed-content, whole-tarball secret, clean-consumer, and registry-signature verification. An unsafe occupied version stops the release without choosing another version.
After publication, release checks allow five minutes for the exact selected package/version to become visible on npm, including a final visibility check at the deadline. Once visible, every required verification check finishes even if it runs past that deadline. Only confirmed E404/ETARGET absence of that exact selection during registry retrieval or consumer installation counts as propagation. Other retrieval, content, secret-scanning, consumer, signature/provenance, wrong-version, and digest failures remain genuine verification failures (exit 1).
Exit 2 means the named package/version is “published; registry verification pending, re-run to verify”: affirmative publication evidence exists, but registry verification is not yet complete. Exit 1 means a genuine failure or unavailable verification without confirmed publication; absence alone does not establish publication. A pending release requires no version bump, republish, or rollback.
Re-dispatch the same package and exact version with dry_run=false to verify an existing publication: package=notifications, version=0.1.3, or package=eve, version=0.3.2. For example, the pending selection is @baremetallabs-ai/[email protected] or @baremetallabs-ai/[email protected]. A visible release skips publication and runs real content/scanner/consumer/signature verification. If an invisible version was previously published, npm’s refusal to overwrite it establishes publication evidence and starts a fresh five-minute visibility window, without a second publication attempt. All required verification still applies; recovery can succeed, remain pending (exit 2), or fail (exit 1).
Successfully verify notifications 0.1.3 before proceeding with Eve 0.3.2. Eve owns the registry contract gate once and cannot publish while the prerequisite is unavailable, pending, or failed. It reports “Eve publication did not proceed; verify the notifications contract first.” Until both exact releases pass verification, the pair is “not yet verified”, with the reason and known publication state. A pending release is not a failed publication or a complete pair. A published contract remains published if the extension fails. Trusted publishing can replace the token later; the workflow retains id-token: write for that switch.
The opt-in email and direct SMS interactions above use the supported Eve 0.74 response policy for both approval and cancellation.
Canvas wake rollout
Publish and verify notifications 0.1.3 before Eve 0.3.2. Deploy a Canvas-capable receiver before attaching the authorized Canvas binding and enabling automatic delivery. Pending event inspection and explicit acknowledgment remain usable without any notification connection. Canvas notifications use the ordinary finite retry/receipt guarantees; receiver acceptance never acknowledges Canvas events. Rollout is operator/orchestrator-owned.
