@axiom-lattice/protocols 3.0.4 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * A2AApiKeyStoreProtocol
3
3
  *
4
- * Persistence interface for A2A API keys with tenant/project/workspace scoping.
4
+ * Persistence interface for A2A API keys scoped by tenant + required project,
5
+ * with an optional assistantIds whitelist.
5
6
  */
6
7
 
7
8
  import type { A2AApiKeyEntry } from "./A2AProtocol";
@@ -10,8 +11,10 @@ export interface A2AApiKeyRecord {
10
11
  id: string;
11
12
  key: string;
12
13
  tenantId: string;
13
- projectId?: string;
14
- workspaceId?: string;
14
+ /** Required project scope — must exist in the tenant */
15
+ projectId: string;
16
+ /** Assistant whitelist; empty/undefined = all exposed agents in the tenant */
17
+ assistantIds?: string[];
15
18
  label?: string;
16
19
  enabled: boolean;
17
20
  createdAt: Date;
@@ -20,8 +23,10 @@ export interface A2AApiKeyRecord {
20
23
 
21
24
  export interface CreateA2AApiKeyInput {
22
25
  tenantId: string;
23
- projectId?: string;
24
- workspaceId?: string;
26
+ /** Required project scope — must exist in the tenant */
27
+ projectId: string;
28
+ /** Assistant whitelist; empty/undefined = all exposed agents in the tenant */
29
+ assistantIds?: string[];
25
30
  label?: string;
26
31
  }
27
32
 
@@ -29,6 +34,9 @@ export interface A2AApiKeyStore {
29
34
  /** Look up a key record by its bearer token value (for auth). */
30
35
  findByKey(key: string): Promise<A2AApiKeyRecord | null>;
31
36
 
37
+ /** Look up a key record by its management identifier. */
38
+ findById(id: string): Promise<A2AApiKeyRecord | null>;
39
+
32
40
  /** List all keys, optionally filtered by tenant. */
33
41
  list(params: { tenantId?: string; limit?: number; offset?: number }): Promise<A2AApiKeyRecord[]>;
34
42
 
@@ -1,190 +1,50 @@
1
1
  /**
2
- * A2AProtocol - Google Agent-to-Agent Protocol type definitions
3
- *
4
- * Based on the A2A open protocol spec for AI agent interoperability.
5
- * @see https://github.com/google/A2A
2
+ * A2AProtocol - re-exports standard A2A 0.3 types from @a2a-js/sdk
3
+ * plus Axiom-specific auth/exposure types.
6
4
  */
5
+ export type {
6
+ AgentCard,
7
+ AgentSkill as A2ASkill,
8
+ Task as A2ATask,
9
+ Message as A2AMessage,
10
+ Part as A2APart,
11
+ TaskState as A2ATaskState,
12
+ } from "@a2a-js/sdk";
7
13
 
8
- // ─── Agent Card ───────────────────────────────────────────────────────────
9
-
10
- export interface A2ASkill {
11
- id: string;
12
- name: string;
13
- description: string;
14
- tags: string[];
15
- examples: string[];
16
- }
17
-
18
- export interface A2ACapabilities {
19
- streaming: boolean;
20
- pushNotifications: boolean;
21
- stateTransitionHistory: boolean;
22
- }
23
-
24
- export interface A2AProvider {
25
- organization: string;
26
- url?: string;
27
- }
28
-
29
- export interface AgentCard {
30
- name: string;
31
- description: string;
32
- url: string;
33
- provider: A2AProvider;
34
- version: string;
35
- documentationUrl?: string;
36
- capabilities: A2ACapabilities;
37
- defaultInputModes: string[];
38
- defaultOutputModes: string[];
39
- skills: A2ASkill[];
40
- }
41
-
42
- // ─── Artifact / Part ──────────────────────────────────────────────────────
43
-
44
- export interface A2ATextPart {
45
- type: "text";
46
- text: string;
47
- }
48
-
49
- export interface A2AFilePart {
50
- type: "file";
51
- file: {
52
- name: string;
53
- mimeType: string;
54
- bytes?: string;
55
- uri?: string;
56
- };
57
- }
58
-
59
- export interface A2ADataPart {
60
- type: "data";
61
- data: Record<string, unknown>;
62
- }
63
-
64
- export type A2APart = A2ATextPart | A2AFilePart | A2ADataPart;
65
-
66
- // ─── Message ──────────────────────────────────────────────────────────────
67
-
68
- export interface A2AMessage {
69
- role: "user" | "agent";
70
- parts: A2APart[];
71
- messageId?: string;
72
- contextId?: string;
73
- referenceTaskIds?: string[];
74
- metadata?: Record<string, unknown>;
75
- }
76
-
77
- // ─── Task ─────────────────────────────────────────────────────────────────
78
-
79
- export type A2ATaskState =
80
- | "working"
81
- | "input-required"
82
- | "completed"
83
- | "failed"
84
- | "canceled"
85
- | "rejected";
86
-
87
- export interface A2ATaskStatus {
88
- state: A2ATaskState;
89
- message?: A2AMessage;
90
- timestamp: string;
91
- }
92
-
93
- export interface A2AArtifact {
94
- name?: string;
95
- description?: string;
96
- parts: A2APart[];
97
- metadata?: Record<string, unknown>;
98
- }
99
-
100
- export interface A2ATask {
101
- id: string;
102
- sessionId?: string;
103
- contextId?: string;
104
- status: A2ATaskStatus;
105
- artifacts: A2AArtifact[];
106
- history?: A2AMessage[];
107
- metadata?: Record<string, unknown>;
108
- }
109
-
110
- // ─── Request / Response ───────────────────────────────────────────────────
111
-
112
- export interface A2ATaskSendRequest {
113
- id?: string;
114
- sessionId?: string;
115
- message: A2AMessage;
116
- pushNotification?: A2APushNotification;
117
- historyLength?: number;
118
- metadata?: Record<string, unknown>;
119
- }
120
-
121
- export interface A2APushNotification {
122
- url: string;
123
- token?: string;
124
- }
125
-
126
- export interface A2ATaskUpdatePayload {
127
- id: string;
128
- sessionId?: string;
129
- contextId?: string;
130
- status: A2ATaskStatus;
131
- final?: boolean;
132
- metadata?: Record<string, unknown>;
133
- }
134
-
135
- export interface A2ATaskArtifactUpdatePayload {
136
- id: string;
137
- sessionId?: string;
138
- contextId?: string;
139
- artifact: A2AArtifact;
140
- final?: boolean;
141
- metadata?: Record<string, unknown>;
142
- }
143
-
144
- // ─── SSE Events ───────────────────────────────────────────────────────────
145
-
146
- export type A2ASSEEvent =
147
- | { event: "task"; data: A2ATaskUpdatePayload }
148
- | { event: "status-update"; data: A2ATaskUpdatePayload }
149
- | { event: "artifact-update"; data: A2ATaskArtifactUpdatePayload }
150
- | { event: "error"; data: { code: string; message: string } };
151
-
152
- // ─── Config ───────────────────────────────────────────────────────────────
153
-
154
- export interface A2AConfig {
155
- agentName: string;
156
- agentDescription: string;
157
- agentUrl: string;
158
- organization: string;
159
- version?: string;
160
- capabilities?: Partial<A2ACapabilities>;
161
- defaultInputModes?: string[];
162
- defaultOutputModes?: string[];
163
- skills?: A2ASkill[];
164
- apiKeyMap: Map<string, A2AApiKeyEntry>;
14
+ /**
15
+ * Per-agent A2A exposure configuration — controls whether an agent is
16
+ * reachable over A2A and which skills are advertised on its AgentCard.
17
+ */
18
+ export interface A2AExposure {
19
+ /** Whether this agent is exposed over the A2A protocol */
20
+ enabled: boolean;
21
+ /** Skills advertised on the AgentCard; defaults to a single generic skill when omitted */
22
+ skills?: Array<{ id: string; name: string; description: string; tags?: string[]; examples?: string[] }>;
23
+ /** Supported input modes (MIME types); defaults to text modes when omitted */
24
+ inputModes?: string[];
25
+ /** Supported output modes (MIME types); defaults to text modes when omitted */
26
+ outputModes?: string[];
165
27
  }
166
28
 
29
+ /**
30
+ * In-memory API key entry used for request authentication.
31
+ * Empty/undefined assistantIds means all exposed agents in the tenant.
32
+ */
167
33
  export interface A2AApiKeyEntry {
168
34
  key: string;
169
- tenantId?: string;
170
- projectId?: string;
171
- workspaceId?: string;
35
+ tenantId: string;
36
+ projectId: string;
37
+ assistantIds?: string[];
172
38
  }
173
39
 
174
- export const A2A_DEFAULT_CAPABILITIES: A2ACapabilities = {
175
- streaming: true,
176
- pushNotifications: false,
177
- stateTransitionHistory: false,
178
- };
179
-
180
- export const A2A_DEFAULT_INPUT_MODES = ["text", "text/plain"];
181
- export const A2A_DEFAULT_OUTPUT_MODES = ["text", "text/plain", "text/markdown"];
182
-
40
+ /**
41
+ * Authentication context attached to an incoming A2A request after key validation.
42
+ */
183
43
  export interface A2AAuthContext {
184
44
  authenticated: boolean;
185
45
  apiKey?: string;
186
46
  tenantId?: string;
187
47
  projectId?: string;
188
- workspaceId?: string;
48
+ assistantIds?: string[];
189
49
  source?: "bearer" | "x-api-key";
190
50
  }
@@ -0,0 +1,168 @@
1
+ import type {
2
+ AgentWebAppAppearance,
3
+ AgentWebAppFeatures,
4
+ } from "./AgentWebAppStoreProtocol";
5
+
6
+ /** Server-owned metadata that isolates an external user's Web App thread. */
7
+ export interface AgentWebAppThreadMetadata {
8
+ source: "web_app";
9
+ webAppId: string;
10
+ userId: string;
11
+ projectId: string;
12
+ label?: string;
13
+ }
14
+
15
+ /** Public, redacted projection of a thread owned by an Agent Web App identity. */
16
+ export interface AgentWebAppRuntimeThread {
17
+ id: string;
18
+ projectId: string;
19
+ label?: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ }
23
+
24
+ /** Reviewed public message projection. Structured internal content is not exposed. */
25
+ export interface AgentWebAppRuntimeMessage {
26
+ id: string;
27
+ role: "human" | "ai";
28
+ content?: string | AgentWebAppGenUIBlock[];
29
+ }
30
+
31
+ export interface AgentWebAppCalloutWidget {
32
+ kind: "callout";
33
+ text: string;
34
+ title?: string;
35
+ tone?: "info" | "success" | "warning";
36
+ }
37
+
38
+ export interface AgentWebAppTableWidget {
39
+ kind: "table";
40
+ columns: string[];
41
+ rows: string[][];
42
+ caption?: string;
43
+ }
44
+
45
+ /** V1 declarative GenUI block. It deliberately has no HTML, URL, or executable fields. */
46
+ export interface AgentWebAppGenUIBlock {
47
+ type: "widget";
48
+ widget: AgentWebAppCalloutWidget | AgentWebAppTableWidget;
49
+ }
50
+
51
+ /** Public initialization data available to an external Agent Web App. */
52
+ export interface AgentWebAppBootstrap {
53
+ webApp: {
54
+ id: string;
55
+ name: string;
56
+ description?: string;
57
+ assistant: {
58
+ id: string;
59
+ name: string;
60
+ description?: string;
61
+ };
62
+ defaultProjectId: string;
63
+ defaultModelKey?: string;
64
+ features: AgentWebAppFeatures;
65
+ appearance: AgentWebAppAppearance;
66
+ identityAssurance: "unverified";
67
+ };
68
+ projects: Array<{
69
+ id: string;
70
+ name: string;
71
+ }>;
72
+ models: Array<{
73
+ key: string;
74
+ label: string;
75
+ }>;
76
+ /** Latest owned thread, created implicitly only when thread management is disabled. */
77
+ thread?: AgentWebAppRuntimeThread;
78
+ }
79
+
80
+ /** Human-in-the-loop interruption exposed through the Web App runtime. */
81
+ export interface AgentWebAppInterrupt {
82
+ id: string;
83
+ type: string;
84
+ prompt: string;
85
+ data?: Record<string, unknown>;
86
+ }
87
+
88
+ /**
89
+ * Stable machine-readable error codes returned by the Web App runtime.
90
+ *
91
+ * `INVALID_REQUEST` covers redacted request-validation failures and
92
+ * `INTERNAL_ERROR` covers redacted unexpected server failures.
93
+ */
94
+ export type AgentWebAppErrorCode =
95
+ | "WEB_APP_NOT_FOUND"
96
+ | "WEB_APP_DISABLED"
97
+ | "USER_ID_REQUIRED"
98
+ | "INVALID_USER_ID"
99
+ | "PROJECT_NOT_ALLOWED"
100
+ | "PROJECT_SELECTOR_DISABLED"
101
+ | "MODEL_NOT_ALLOWED"
102
+ | "FEATURE_DISABLED"
103
+ | "THREAD_NOT_FOUND"
104
+ | "STREAM_CONFLICT"
105
+ | "STREAM_FAILED"
106
+ | "INVALID_REQUEST"
107
+ | "INTERNAL_ERROR";
108
+
109
+ /** Public error payload returned by the Web App runtime. */
110
+ export interface AgentWebAppError {
111
+ code: AgentWebAppErrorCode;
112
+ message: string;
113
+ retryable: boolean;
114
+ }
115
+
116
+ /** Stable stream events projected from internal agent execution output. */
117
+ export type AgentWebAppStreamEvent =
118
+ | { type: "message.delta"; text: string }
119
+ | { type: "message.completed"; messageId: string }
120
+ | { type: "tool.started"; id: string; name: string }
121
+ | { type: "tool.completed"; id: string }
122
+ | { type: "interrupt.created"; interrupt: AgentWebAppInterrupt }
123
+ | { type: "genui.render"; block: AgentWebAppGenUIBlock }
124
+ | { type: "error"; error: AgentWebAppError }
125
+ | { type: "stream.completed" };
126
+
127
+ const MAX_WIDGET_TEXT = 2_000;
128
+ const MAX_TABLE_COLUMNS = 12;
129
+ const MAX_TABLE_ROWS = 100;
130
+
131
+ /** Validate and copy one strict public GenUI block at a trust boundary. */
132
+ export function parseAgentWebAppGenUIBlock(value: unknown): AgentWebAppGenUIBlock | undefined {
133
+ if (!isExactRecord(value, ["type", "widget"]) || value.type !== "widget" || !isRecord(value.widget)) return undefined;
134
+ const widget = value.widget;
135
+ if (widget.kind === "callout") {
136
+ if (!hasOnlyKeys(widget, ["kind", "text"], ["title", "tone"]) || !boundedText(widget.text)) return undefined;
137
+ if (widget.title !== undefined && !boundedText(widget.title)) return undefined;
138
+ if (widget.tone !== undefined && widget.tone !== "info" && widget.tone !== "success" && widget.tone !== "warning") return undefined;
139
+ return { type: "widget", widget: { kind: "callout", text: widget.text, ...(typeof widget.title === "string" ? { title: widget.title } : {}), ...(widget.tone ? { tone: widget.tone } : {}) } };
140
+ }
141
+ if (widget.kind === "table") {
142
+ if (!hasOnlyKeys(widget, ["kind", "columns", "rows"], ["caption"]) || !Array.isArray(widget.columns) || !Array.isArray(widget.rows)) return undefined;
143
+ const columns = widget.columns;
144
+ const rows = widget.rows;
145
+ if (columns.length === 0 || columns.length > MAX_TABLE_COLUMNS || !columns.every(boundedText)) return undefined;
146
+ if (rows.length > MAX_TABLE_ROWS || !rows.every((row) => Array.isArray(row) && row.length === columns.length && row.every(boundedText))) return undefined;
147
+ if (widget.caption !== undefined && !boundedText(widget.caption)) return undefined;
148
+ return { type: "widget", widget: { kind: "table", columns: [...columns], rows: rows.map((row) => [...row]), ...(typeof widget.caption === "string" ? { caption: widget.caption } : {}) } };
149
+ }
150
+ return undefined;
151
+ }
152
+
153
+ function boundedText(value: unknown): value is string {
154
+ return typeof value === "string" && value.length <= MAX_WIDGET_TEXT;
155
+ }
156
+
157
+ function isRecord(value: unknown): value is Record<string, unknown> {
158
+ return typeof value === "object" && value !== null && !Array.isArray(value);
159
+ }
160
+
161
+ function isExactRecord(value: unknown, keys: string[]): value is Record<string, unknown> {
162
+ return isRecord(value) && Object.keys(value).length === keys.length && keys.every((key) => key in value);
163
+ }
164
+
165
+ function hasOnlyKeys(value: Record<string, unknown>, required: string[], optional: string[]): boolean {
166
+ const keys = Object.keys(value);
167
+ return required.every((key) => key in value) && keys.every((key) => required.includes(key) || optional.includes(key));
168
+ }
@@ -0,0 +1,104 @@
1
+ /** Lifecycle state of an Agent Web App publication. */
2
+ export type AgentWebAppStatus = "draft" | "active" | "disabled";
3
+
4
+ /** Project and model boundaries exposed by an Agent Web App. */
5
+ export interface AgentWebAppScope {
6
+ defaultProjectId: string;
7
+ allowedProjectIds: string[];
8
+ defaultModelKey?: string;
9
+ allowedModelKeys?: string[];
10
+ }
11
+
12
+ /** Runtime capabilities enabled for an Agent Web App. */
13
+ export interface AgentWebAppFeatures {
14
+ projectSelector: boolean;
15
+ modelSelector: boolean;
16
+ threadManagement: boolean;
17
+ attachments: boolean;
18
+ hitl: boolean;
19
+ genUI: boolean;
20
+ }
21
+
22
+ /** Optional display customization for an Agent Web App. */
23
+ export interface AgentWebAppAppearance {
24
+ title?: string;
25
+ welcomeMessage?: string;
26
+ primaryColor?: string;
27
+ }
28
+
29
+ /** Persisted React SDK publication of an assistant. */
30
+ export interface AgentWebApp {
31
+ id: string;
32
+ tenantId: string;
33
+ assistantId: string;
34
+ name: string;
35
+ description?: string;
36
+ status: AgentWebAppStatus;
37
+ integration: {
38
+ type: "react_sdk";
39
+ };
40
+ scope: AgentWebAppScope;
41
+ features: AgentWebAppFeatures;
42
+ appearance: AgentWebAppAppearance;
43
+ createdAt: Date;
44
+ updatedAt: Date;
45
+ }
46
+
47
+ /** Client-provided fields used to create an Agent Web App. */
48
+ export interface CreateAgentWebAppInput {
49
+ assistantId: string;
50
+ name: string;
51
+ description?: string;
52
+ integration: {
53
+ type: "react_sdk";
54
+ };
55
+ scope: AgentWebAppScope;
56
+ features: AgentWebAppFeatures;
57
+ appearance: AgentWebAppAppearance;
58
+ }
59
+
60
+ /** Editable fields accepted when updating an Agent Web App. Nested objects use merge semantics. */
61
+ export interface UpdateAgentWebAppInput {
62
+ name?: string;
63
+ description?: string;
64
+ scope?: Partial<AgentWebAppScope>;
65
+ features?: Partial<AgentWebAppFeatures>;
66
+ appearance?: Partial<AgentWebAppAppearance>;
67
+ }
68
+
69
+ /** Internal persistence patch. Nested objects are complete replacements when provided. */
70
+ export interface AgentWebAppStorePatch {
71
+ name?: string;
72
+ description?: string;
73
+ scope?: AgentWebAppScope;
74
+ features?: AgentWebAppFeatures;
75
+ appearance?: AgentWebAppAppearance;
76
+ status?: AgentWebAppStatus;
77
+ }
78
+
79
+ /** Optional optimistic concurrency guard for an Agent Web App update. */
80
+ export interface AgentWebAppUpdateOptions {
81
+ /** Update only when the persisted timestamp exactly matches this snapshot. */
82
+ expectedUpdatedAt?: Date;
83
+ }
84
+
85
+ /** Tenant-scoped persistence operations for Agent Web Apps. */
86
+ export interface AgentWebAppStore {
87
+ list(tenantId: string, assistantId?: string): Promise<AgentWebApp[]>;
88
+ getById(tenantId: string, webAppId: string): Promise<AgentWebApp | null>;
89
+ /**
90
+ * Resolve a globally identifiable publication for an external runtime request.
91
+ *
92
+ * The returned record still carries its owning tenant; callers must derive all
93
+ * tenant scope from that record and must not accept a tenant from the browser.
94
+ */
95
+ findById(webAppId: string): Promise<AgentWebApp | null>;
96
+ create(tenantId: string, input: CreateAgentWebAppInput): Promise<AgentWebApp>;
97
+ update(
98
+ tenantId: string,
99
+ webAppId: string,
100
+ patch: AgentWebAppStorePatch,
101
+ options?: AgentWebAppUpdateOptions,
102
+ ): Promise<AgentWebApp | null>;
103
+ delete(tenantId: string, webAppId: string): Promise<boolean>;
104
+ }
@@ -250,4 +250,25 @@ export interface SkillStore {
250
250
  * @returns The resource content as string, or null if not found
251
251
  */
252
252
  loadSkillResource?(tenantId: string, id: string, resourcePath: string, context?: SkillStoreContext): Promise<string | null>;
253
+
254
+ /**
255
+ * Load a resource without text decoding.
256
+ * @returns The resource bytes, or null if the resource does not exist
257
+ */
258
+ loadSkillResourceRaw?(tenantId: string, id: string, resourcePath: string, context?: SkillStoreContext): Promise<Buffer | null>;
259
+
260
+ /**
261
+ * Write a resource file into a skill's resources directory
262
+ * @param tenantId Tenant identifier
263
+ * @param id Skill identifier
264
+ * @param resourcePath Path to the resource relative to resources/ directory
265
+ * @param content Resource content
266
+ * @param context Optional runtime context for sandbox resolution
267
+ */
268
+ writeSkillResource?(tenantId: string, id: string, resourcePath: string, content: string, context?: SkillStoreContext): Promise<void>;
269
+
270
+ /**
271
+ * Write a resource without text encoding.
272
+ */
273
+ writeSkillResourceRaw?(tenantId: string, id: string, resourcePath: string, data: Buffer, context?: SkillStoreContext): Promise<void>;
253
274
  }