UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

185 lines • 16.8 kB
"use strict"; /** * hosting-providers — adapters for the two hosting ports. * * src/hosting/ ← the PORTS (`AgentHost`, `SessionLifecycle`), the * composer, and the local adapters * src/hosting-providers.ts ← adapters for somewhere you deploy (this file) * * The split is a source-tree split, not an import split: both halves come out * of the one `agentfootprint/hosting` door. It exists because the PORT files * promise that not one field, name or assumption in them comes from any cloud, * and a test greps those sources for vendor names to keep that honest. So the * cloud adapters live in this file, and the port files stay readable without * learning anybody's product names. * * One file that grows: an adapter for the next runtime lands here beside this * one. * * ── What ships here ────────────────────────────────────────────────────────── * • `agentCoreRuntimeHost({ port?, hostname?, busy? })` — an `AgentHost` for * AWS Bedrock AgentCore Runtime's container contract: `POST /invocations`, * `GET /ping`, port 8080, and the conversation id read from the * `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header. It is a * `ConversationHost` too: `serveConversations(handler)` takes the runtime's * `/ws` door on the SAME socket, with `{ maxFrameBytes: 32768, * idleMs: 900000 }` declared, session affinity readable from header or * query, and a `Sec-WebSocket-Protocol` bearer mapped into * `headers.authorization`. * • `agentCoreA2AHost({ card, port?, hostname?, maxBodyBytes? })` — an * `AgentHost` for AgentCore's **A2A** container contract, so OTHER AGENTS * can call yours: JSON-RPC 2.0 at `POST /` on port 9000, the agent card at * `/.well-known/agent-card.json`, `GET /ping` answering * `{"status":"Healthy"}`, and the session read from * `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`. It declares NO streaming * capability and its card says `streaming: false`, because `message/send` * has nowhere to put a chunk — the two agree on purpose. Errors carry a * REAL HTTP status alongside the JSON-RPC error body (the platform's * documented deviation from the A2A spec); `agentCoreA2AErrorCode` is the * table, exported so a client shares it rather than keeping a copy. * • `a2aWire({ card, health?, errorCodeFor? })` — the A2A PROTOCOL as an * `HttpWire`, with no vendor in it, for the next runtime that speaks it. * `a2aAgentCardDocument(card)` builds the discovery document on its own, * for deployments that must serve it from somewhere else. * • `foundryResponsesHost({ port?, hostname?, model?, maxBodyBytes? })` — an * `AgentHost` for Microsoft Foundry's hosted-agent contract: `POST * /responses`, a `HEAD` capability probe on the same path, `GET /readiness` * answering `{"status":"healthy"}`, port 8088, and the session read from * `conversation` / `agent_session_id` / `session_id` in that order. It * streams when the BODY says `stream: true` — not the `Accept` header — * and frames that stream as the Responses lifecycle. * **Inbound only:** it is the door callers arrive at, and says nothing * about which model the agent calls. It does NOT provide Workflow * Visualizer topology; see the adapter's header. * • `responsesWire({ defaultModel?, sessionFields?, health? })` — the * Responses PROTOCOL as an `HttpWire`, with no vendor in it, for the next * runtime that speaks it on paths of its own. * • `agentCoreSessions({ store })` — a `SessionLifecycle` whose checkpoint * home you choose at construction: a JSON file in the runtime's own session * storage (survives a stop/resume, needs no SDK), or one AgentCore Memory * event per persist (outlives the session). * • `agentEngineSessions({ project, location, reasoningEngine })` — a * `SessionLifecycle` in Vertex AI's own session service, for the row above * `sqliteSessions`: many containers sharing one conversation. The whole * `CheckpointEnvelope` rides in `Session.sessionState`, which is an * arbitrary JSON Struct, so there is no blob encoding to get wrong — and it * is written by APPENDING AN EVENT, because the service refuses a state * patch (a field trial found that the expensive way; see the adapter). * • `firestoreSessions({ project?, database?, collection? })` — the same fleet * row without a reasoning engine: one Firestore document per session, in a * project you probably already have. `listByUser` is a server-side indexed * query with a real Firestore cursor (never a client-side sort over every * document, which is the shape that quietly becomes a full read per page), * and ownership is written ONCE inside a transaction because Firestore has * no `COALESCE` and a merged write would let the last writer take a session. * It needs one composite index; the adapter names it, and says so again in * the error you get without it. * * ── How much of this is verified ───────────────────────────────────────────── * `agentCoreRuntimeHost` is plain HTTP with no SDK on its path, and it passes * the same host conformance suite as `nodeHost` over a real socket — that is * real verification, not a mapping asserted in prose. * * Everything that talks to AWS — `agentCoreSessions({ store: 'memory' })` — is * **contract-mapped and injection-tested**: the SDK calls are exercised through * the `_client` seam and no test pretends to have reached AWS. Real-cloud * verification lands with a field deployment. * * `firestoreSessions` is **field-validated except the ownership refusal * (2026-08)**: an independent trial ran it against a real Firestore and * exercised seven of its eight areas live — the round trip, the indexed and * cursored listing, the missing-index refusal, the hashed document name, the * size ceiling, derived ownership and `forget`. The eighth, refusing a turn * signed by somebody ELSE, was added after that trial and is held by tests * here and by nothing in the field. Two further caveats stand: every SDK * member it calls was read off a real install of `@google-cloud/firestore` * 9.0.0 and hand-verified there, but the package is deliberately NOT installed * in this repository (it would hoist `@opentelemetry/api` and disarm an * absent-peer refusal test), so the assertion that re-checks those names * against the real package SKIPS here and runs for anyone who installs it * locally; what CI checks is dispatch. See the adapter's module header for the * full account. * * @example An agent in an AgentCore Runtime container * import { standingAgent } from 'agentfootprint/hosting'; * import { agentCoreRuntimeHost, agentCoreSessions } from 'agentfootprint/hosting'; * * const handle = await standingAgent({ * agent, * host: agentCoreRuntimeHost(), * sessions: agentCoreSessions({ store: 'session-storage' }), * }); * process.on('SIGTERM', () => void handle.close()); * * Not an import path of its own since 9.0.0. This is the implementation barrel * behind `agentfootprint/hosting`, which re-exports every name here — same * symbols, one door. Import from the door. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FirestoreIndexMissingError = exports.EnvelopeTooLargeError = exports.FIRESTORE_MAX_ENVELOPE_BYTES = exports.FIRESTORE_MAX_DOCUMENT_BYTES = exports.EXPIRES_AT_FIELD = exports.DEFAULT_SESSION_COLLECTION = exports.firestoreSessions = exports.SESSION_EVENT_AUTHOR = exports.SESSION_STATE_KEY = exports.DEFAULT_USER_ID = exports.agentEngineSessions = exports.PUBLIC_FAILURE_MESSAGE = exports.DEFAULT_SESSION_FIELDS = exports.readResponsesSession = exports.readResponsesInput = exports.responsesWire = exports.FOUNDRY_SESSION_FIELDS = exports.FOUNDRY_READINESS_PATH = exports.FOUNDRY_INVOKE_PATH = exports.DEFAULT_FOUNDRY_PORT = exports.foundryResponsesHost = exports.JSONRPC_INTERNAL_ERROR = exports.JSONRPC_INVALID_PARAMS = exports.JSONRPC_METHOD_NOT_FOUND = exports.A2A_SEND_METHOD = exports.A2A_AGENT_CARD_PATH = exports.A2A_PROTOCOL_VERSION = exports.readA2AMessageText = exports.a2aAgentCardDocument = exports.a2aWire = exports.AGENTCORE_SESSION_HEADER = exports.AGENTCORE_PING_PATH = exports.AGENTCORE_A2A_INVOKE_PATH = exports.DEFAULT_AGENTCORE_A2A_PORT = exports.agentCoreA2AErrorCode = exports.agentCoreA2AHost = exports.DEFAULT_SESSION_STORAGE_PATH = exports.agentCoreSessions = exports.readAgentCoreConversation = exports.agentCoreRuntimeWire = exports.agentCoreRuntimeHost = void 0; var agentcore_js_1 = require("./adapters/hosting/agentcore.js"); Object.defineProperty(exports, "agentCoreRuntimeHost", { enumerable: true, get: function () { return agentcore_js_1.agentCoreRuntimeHost; } }); Object.defineProperty(exports, "agentCoreRuntimeWire", { enumerable: true, get: function () { return agentcore_js_1.agentCoreRuntimeWire; } }); Object.defineProperty(exports, "readAgentCoreConversation", { enumerable: true, get: function () { return agentcore_js_1.readAgentCoreConversation; } }); Object.defineProperty(exports, "agentCoreSessions", { enumerable: true, get: function () { return agentcore_js_1.agentCoreSessions; } }); Object.defineProperty(exports, "DEFAULT_SESSION_STORAGE_PATH", { enumerable: true, get: function () { return agentcore_js_1.DEFAULT_SESSION_STORAGE_PATH; } }); var agentCoreA2A_js_1 = require("./adapters/hosting/agentCoreA2A.js"); Object.defineProperty(exports, "agentCoreA2AHost", { enumerable: true, get: function () { return agentCoreA2A_js_1.agentCoreA2AHost; } }); Object.defineProperty(exports, "agentCoreA2AErrorCode", { enumerable: true, get: function () { return agentCoreA2A_js_1.agentCoreA2AErrorCode; } }); Object.defineProperty(exports, "DEFAULT_AGENTCORE_A2A_PORT", { enumerable: true, get: function () { return agentCoreA2A_js_1.DEFAULT_AGENTCORE_A2A_PORT; } }); Object.defineProperty(exports, "AGENTCORE_A2A_INVOKE_PATH", { enumerable: true, get: function () { return agentCoreA2A_js_1.AGENTCORE_A2A_INVOKE_PATH; } }); Object.defineProperty(exports, "AGENTCORE_PING_PATH", { enumerable: true, get: function () { return agentCoreA2A_js_1.AGENTCORE_PING_PATH; } }); Object.defineProperty(exports, "AGENTCORE_SESSION_HEADER", { enumerable: true, get: function () { return agentCoreA2A_js_1.AGENTCORE_SESSION_HEADER; } }); // The protocol under it, on the barrel in its own right — A2A is an open // protocol, and reaching it should not mean importing a vendor's module. var a2aWire_js_1 = require("./adapters/hosting/a2aWire.js"); Object.defineProperty(exports, "a2aWire", { enumerable: true, get: function () { return a2aWire_js_1.a2aWire; } }); Object.defineProperty(exports, "a2aAgentCardDocument", { enumerable: true, get: function () { return a2aWire_js_1.a2aAgentCardDocument; } }); Object.defineProperty(exports, "readA2AMessageText", { enumerable: true, get: function () { return a2aWire_js_1.readA2AMessageText; } }); Object.defineProperty(exports, "A2A_PROTOCOL_VERSION", { enumerable: true, get: function () { return a2aWire_js_1.A2A_PROTOCOL_VERSION; } }); Object.defineProperty(exports, "A2A_AGENT_CARD_PATH", { enumerable: true, get: function () { return a2aWire_js_1.A2A_AGENT_CARD_PATH; } }); Object.defineProperty(exports, "A2A_SEND_METHOD", { enumerable: true, get: function () { return a2aWire_js_1.A2A_SEND_METHOD; } }); Object.defineProperty(exports, "JSONRPC_METHOD_NOT_FOUND", { enumerable: true, get: function () { return a2aWire_js_1.JSONRPC_METHOD_NOT_FOUND; } }); Object.defineProperty(exports, "JSONRPC_INVALID_PARAMS", { enumerable: true, get: function () { return a2aWire_js_1.JSONRPC_INVALID_PARAMS; } }); Object.defineProperty(exports, "JSONRPC_INTERNAL_ERROR", { enumerable: true, get: function () { return a2aWire_js_1.JSONRPC_INTERNAL_ERROR; } }); var foundryResponses_js_1 = require("./adapters/hosting/foundryResponses.js"); Object.defineProperty(exports, "foundryResponsesHost", { enumerable: true, get: function () { return foundryResponses_js_1.foundryResponsesHost; } }); Object.defineProperty(exports, "DEFAULT_FOUNDRY_PORT", { enumerable: true, get: function () { return foundryResponses_js_1.DEFAULT_FOUNDRY_PORT; } }); Object.defineProperty(exports, "FOUNDRY_INVOKE_PATH", { enumerable: true, get: function () { return foundryResponses_js_1.FOUNDRY_INVOKE_PATH; } }); Object.defineProperty(exports, "FOUNDRY_READINESS_PATH", { enumerable: true, get: function () { return foundryResponses_js_1.FOUNDRY_READINESS_PATH; } }); Object.defineProperty(exports, "FOUNDRY_SESSION_FIELDS", { enumerable: true, get: function () { return foundryResponses_js_1.FOUNDRY_SESSION_FIELDS; } }); // The protocol under it, on the barrel in its own right: a deployment that // speaks Responses on paths of its own is a `httpHost` away, and having to // import a vendor-named module to reach a vendor-neutral dialect would be the // split this pair exists to make. var responsesWire_js_1 = require("./adapters/hosting/responsesWire.js"); Object.defineProperty(exports, "responsesWire", { enumerable: true, get: function () { return responsesWire_js_1.responsesWire; } }); Object.defineProperty(exports, "readResponsesInput", { enumerable: true, get: function () { return responsesWire_js_1.readResponsesInput; } }); Object.defineProperty(exports, "readResponsesSession", { enumerable: true, get: function () { return responsesWire_js_1.readResponsesSession; } }); Object.defineProperty(exports, "DEFAULT_SESSION_FIELDS", { enumerable: true, get: function () { return responsesWire_js_1.DEFAULT_SESSION_FIELDS; } }); Object.defineProperty(exports, "PUBLIC_FAILURE_MESSAGE", { enumerable: true, get: function () { return responsesWire_js_1.PUBLIC_FAILURE_MESSAGE; } }); var googleAgentEngine_js_1 = require("./adapters/hosting/googleAgentEngine.js"); Object.defineProperty(exports, "agentEngineSessions", { enumerable: true, get: function () { return googleAgentEngine_js_1.agentEngineSessions; } }); Object.defineProperty(exports, "DEFAULT_USER_ID", { enumerable: true, get: function () { return googleAgentEngine_js_1.DEFAULT_USER_ID; } }); Object.defineProperty(exports, "SESSION_STATE_KEY", { enumerable: true, get: function () { return googleAgentEngine_js_1.SESSION_STATE_KEY; } }); Object.defineProperty(exports, "SESSION_EVENT_AUTHOR", { enumerable: true, get: function () { return googleAgentEngine_js_1.SESSION_EVENT_AUTHOR; } }); // `documentIdFor`, `firestoreFailure` and the gRPC status readers stay OFF this // barrel on purpose. The first is reachable as a method on the store, where it // carries the store's own context; the others are this adapter's internals, and // a name as generic as `grpcStatusOf` on a package-wide door is a collision // waiting for the second gRPC adapter. Import them from the module path if a // test needs them. var firestoreSessions_js_1 = require("./adapters/hosting/firestoreSessions.js"); Object.defineProperty(exports, "firestoreSessions", { enumerable: true, get: function () { return firestoreSessions_js_1.firestoreSessions; } }); Object.defineProperty(exports, "DEFAULT_SESSION_COLLECTION", { enumerable: true, get: function () { return firestoreSessions_js_1.DEFAULT_SESSION_COLLECTION; } }); // The field a native TTL policy is configured against (9.42.0). On the // barrel beside the collection name for the same reason: an operator types // both of them into a console, and a name that lives only inside a string // literal is a name somebody mistypes at 2am. `ttlPolicyCommand` stays off — // it is reachable, already filled in, as `store.retention().enableWith`. Object.defineProperty(exports, "EXPIRES_AT_FIELD", { enumerable: true, get: function () { return firestoreSessions_js_1.EXPIRES_AT_FIELD; } }); Object.defineProperty(exports, "FIRESTORE_MAX_DOCUMENT_BYTES", { enumerable: true, get: function () { return firestoreSessions_js_1.FIRESTORE_MAX_DOCUMENT_BYTES; } }); Object.defineProperty(exports, "FIRESTORE_MAX_ENVELOPE_BYTES", { enumerable: true, get: function () { return firestoreSessions_js_1.FIRESTORE_MAX_ENVELOPE_BYTES; } }); Object.defineProperty(exports, "EnvelopeTooLargeError", { enumerable: true, get: function () { return firestoreSessions_js_1.EnvelopeTooLargeError; } }); Object.defineProperty(exports, "FirestoreIndexMissingError", { enumerable: true, get: function () { return firestoreSessions_js_1.FirestoreIndexMissingError; } }); //# sourceMappingURL=hosting-providers.js.map