fix: enforce atomic agent registration contract

This commit is contained in:
ZacharyZhang-NY committed 2026-07-19 15:38:40 -04:00
1 parent 47d323c17b
commit f64d00673c
58 files changed
+2692 -1667

No files matched your search

+1
View File
@@ -159,6 +159,7 @@ const result = await client.verifyOperation(operationId);
// Register a new agent
const agent = await client.registerAgent({
agent_id: 'my-agent',
integration_type: 'codex',
display_name: 'My Agent',
responsible_entity: 'team@example.com',
keys: [{ kid: 'key-v1', public_key: '<base64url>', algorithm: 'ed25519' }],
+2
View File
@@ -28,6 +28,7 @@ import type {
ErrorResponse,
} from './types.js';
import { ElydoraError } from './errors.js';
import { assertIntegrationType } from './integration-types.js';
import {
jcsCanonicalise,
computePayloadHash,
@@ -148,6 +149,7 @@ export class ElydoraClient {
// -------------------------------------------------------------------------
async registerAgent(request: RegisterAgentRequest): Promise<RegisterAgentResponse> {
assertIntegrationType(request?.integration_type);
return this.request<RegisterAgentResponse>('POST', '/v1/agents/register', request);
}
+2
View File
@@ -1,5 +1,6 @@
export { ElydoraClient } from './client.js';
export { ElydoraError } from './errors.js';
export { INTEGRATION_TYPES } from './types.js';
export {
jcsCanonicalise,
@@ -24,6 +25,7 @@ export type {
KeyStatus,
ExportStatus,
RbacRole,
IntegrationType,
ErrorCode,
// Entities
+11
View File
@@ -0,0 +1,11 @@
import { INTEGRATION_TYPES, type IntegrationType } from './types.js';
export function assertIntegrationType(value: unknown): asserts value is IntegrationType {
const isSupported = typeof value === 'string'
&& INTEGRATION_TYPES.some((integrationType) => integrationType === value);
if (!isSupported) {
throw new TypeError(
`Invalid integration_type "${String(value)}". Expected one of: ${INTEGRATION_TYPES.join(', ')}`,
);
}
}
+24
View File
@@ -11,6 +11,28 @@ export type RbacRole =
| 'compliance_auditor'
| 'readonly_investigator'
| 'integration_engineer';
export const INTEGRATION_TYPES = [
'augment',
'claudecode',
'cline',
'codex',
'copilot',
'cursor',
'droid',
'gemini',
'grok',
'kimi',
'kirocli',
'kiroide',
'letta',
'opencode',
'qwen',
'enterprise',
'gui',
'sdk',
'other',
] as const;
export type IntegrationType = (typeof INTEGRATION_TYPES)[number];
export type ErrorCode =
| 'INVALID_SIGNATURE'
| 'UNKNOWN_AGENT'
@@ -36,6 +58,7 @@ export interface Agent {
readonly org_id: string;
readonly display_name: string;
readonly responsible_entity: string;
readonly integration_type: IntegrationType;
readonly status: AgentStatus;
readonly created_at: number;
readonly updated_at: number;
@@ -160,6 +183,7 @@ export interface EAR {
export interface RegisterAgentRequest {
readonly agent_id: string;
readonly integration_type: IntegrationType;
readonly display_name?: string;
readonly responsible_entity?: string;
readonly keys: ReadonlyArray<{
@@ -0,0 +1,131 @@
import assert from 'node:assert/strict';
import http from 'node:http';
import test from 'node:test';
import { ElydoraClient, INTEGRATION_TYPES } from '../dist/index.js';
const EXPECTED_INTEGRATION_TYPES = [
'augment',
'claudecode',
'cline',
'codex',
'copilot',
'cursor',
'droid',
'gemini',
'grok',
'kimi',
'kirocli',
'kiroide',
'letta',
'opencode',
'qwen',
'enterprise',
'gui',
'sdk',
'other',
];
async function createFixture() {
const requests = [];
const server = http.createServer(async (request, response) => {
const chunks = [];
for await (const chunk of request) chunks.push(chunk);
const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
requests.push({
method: request.method,
path: request.url,
authorization: request.headers.authorization,
body,
});
response.writeHead(201, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({
agent: {
agent_id: body.agent_id,
org_id: 'org-1',
display_name: body.display_name,
responsible_entity: body.responsible_entity,
integration_type: body.integration_type,
status: 'active',
created_at: 1,
updated_at: 1,
},
keys: [],
}));
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const address = server.address();
assert(address && typeof address === 'object');
const client = new ElydoraClient({
orgId: 'org-1',
agentId: 'admin-agent',
privateKey: 'unused',
baseUrl: `http://127.0.0.1:${address.port}`,
maxRetries: 0,
});
client.setToken('api-token');
return {
client,
requests,
async close() {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
},
};
}
test('integration types match the public API contract', () => {
assert.deepEqual(INTEGRATION_TYPES, EXPECTED_INTEGRATION_TYPES);
});
test('agent registration sends integration_type in one authenticated request', async () => {
const fixture = await createFixture();
try {
await assert.rejects(
fixture.client.registerAgent({ agent_id: 'agent-1', keys: [] }),
{
name: 'TypeError',
message: /Invalid integration_type "undefined"/,
},
);
await assert.rejects(
fixture.client.registerAgent({
agent_id: 'agent-1',
integration_type: 'future-cli',
keys: [],
}),
{
name: 'TypeError',
message: /Invalid integration_type "future-cli"/,
},
);
assert.equal(fixture.requests.length, 0);
const response = await fixture.client.registerAgent({
agent_id: 'agent-1',
integration_type: 'grok',
display_name: 'Grok Agent',
responsible_entity: 'platform@example.com',
keys: [{ kid: 'key-v1', public_key: 'public-key', algorithm: 'ed25519' }],
});
assert.equal(response.agent.integration_type, 'grok');
assert.deepEqual(fixture.requests, [{
method: 'POST',
path: '/v1/agents/register',
authorization: 'Bearer api-token',
body: {
agent_id: 'agent-1',
integration_type: 'grok',
display_name: 'Grok Agent',
responsible_entity: 'platform@example.com',
keys: [{ kid: 'key-v1', public_key: 'public-key', algorithm: 'ed25519' }],
},
}]);
} finally {
await fixture.close();
}
});