feat(foundation): FOUNDATION_UPDATE implementation

## Documentation (20 files)
- DAARION Ontology Core v1 (Agent → MicroDAO → Node → District)
- User Onboarding & Identity Layer (DAIS)
- Data Model UPDATE, Event Catalog, Governance & Permissions
- Rooms Layer, City/MicroDAO/Agents/Nodes Interface Architecture
- Helper files: ontology-summary, lifecycles, event-schemas

## Database Migration (027)
- DAIS tables: dais_identities, dais_emails, dais_wallets, dais_keys
- agent_assignments table for Assignment Layer
- rooms table for Rooms Layer
- event_outbox for NATS event delivery
- New enums: agent_role, microdao_type, node_kind, node_status, etc.
- Updated agents, microdaos, nodes tables with ontology fields

## Backend
- DAIS service & routes (/api/v1/dais/*)
- Assignment service & routes (/api/v1/assignments/*)
- Domain types for DAIS and Ontology

## Frontend
- Ontology types (Agent, MicroDAO, Node, DAIS, Assignments)
- API clients for DAIS and Assignments
- UI components: DaisProfileCard, AssignmentsPanel, OntologyBadge

Non-breaking update - all existing functionality preserved.
This commit is contained in:
Apple
2025-11-29 15:24:38 -08:00
parent deeaf26b0b
commit 7b91c8e83c
43 changed files with 5733 additions and 47 deletions

View File

@@ -0,0 +1,226 @@
/**
* Agent Assignment Service
* Based on: docs/foundation/microdao_Governance_And_Permissions_v1.md
*
* Manages agent work assignments to other MicroDAO/District/City
*/
import { db } from '../../infra/db/client';
import { logger } from '../../infra/logger/logger';
import type {
AgentAssignment,
CreateAssignmentRequest,
AssignmentScope,
} from '../../domain/ontology/types';
import { v4 as uuidv4 } from 'uuid';
export class AssignmentService {
/**
* Create a new agent assignment
*/
async createAssignment(request: CreateAssignmentRequest): Promise<AgentAssignment> {
try {
const result = await db.query<AgentAssignment>(
`INSERT INTO agent_assignments
(agent_id, target_microdao_id, scope, role, metadata)
VALUES ($1, $2, $3, $4, $5)
RETURNING *`,
[
request.agentId,
request.targetMicrodaoId,
request.scope,
request.role,
JSON.stringify(request.metadata || {}),
]
);
const assignment = result.rows[0];
// Publish event to outbox
await this.publishEvent('dagion.agent.assignment_created', {
assignmentId: assignment.id,
agentId: request.agentId,
targetMicrodaoId: request.targetMicrodaoId,
scope: request.scope,
role: request.role,
timestamp: new Date().toISOString(),
});
logger.info(`Created assignment: ${assignment.id} for agent ${request.agentId}`);
return assignment;
} catch (error) {
logger.error('Failed to create assignment', error);
throw error;
}
}
/**
* End an agent assignment
*/
async endAssignment(assignmentId: string): Promise<void> {
try {
const result = await db.query<AgentAssignment>(
`UPDATE agent_assignments
SET end_ts = now()
WHERE id = $1
RETURNING *`,
[assignmentId]
);
if (result.rows.length === 0) {
throw new Error(`Assignment not found: ${assignmentId}`);
}
const assignment = result.rows[0];
// Publish event to outbox
await this.publishEvent('dagion.agent.assignment_ended', {
assignmentId,
agentId: assignment.agentId,
timestamp: new Date().toISOString(),
});
logger.info(`Ended assignment: ${assignmentId}`);
} catch (error) {
logger.error(`Failed to end assignment: ${assignmentId}`, error);
throw error;
}
}
/**
* Get all active assignments for an agent
*/
async getAgentAssignments(agentId: string): Promise<AgentAssignment[]> {
try {
const result = await db.query<AgentAssignment>(
`SELECT * FROM agent_assignments
WHERE agent_id = $1 AND end_ts IS NULL
ORDER BY created_at DESC`,
[agentId]
);
return result.rows;
} catch (error) {
logger.error(`Failed to get assignments for agent: ${agentId}`, error);
throw error;
}
}
/**
* Get all assignments for a MicroDAO
*/
async getMicrodaoAssignments(microdaoId: string): Promise<AgentAssignment[]> {
try {
const result = await db.query<AgentAssignment>(
`SELECT a.*, ag.name as agent_name
FROM agent_assignments a
JOIN agents ag ON ag.id = a.agent_id
WHERE a.target_microdao_id = $1 AND a.end_ts IS NULL
ORDER BY a.created_at DESC`,
[microdaoId]
);
return result.rows;
} catch (error) {
logger.error(`Failed to get assignments for microdao: ${microdaoId}`, error);
throw error;
}
}
/**
* Get citywide assignments (DAARION108)
*/
async getCitywideAssignments(): Promise<AgentAssignment[]> {
try {
const result = await db.query<AgentAssignment>(
`SELECT a.*, ag.name as agent_name
FROM agent_assignments a
JOIN agents ag ON ag.id = a.agent_id
WHERE a.scope = 'city' AND a.end_ts IS NULL
ORDER BY a.created_at DESC`,
[]
);
return result.rows;
} catch (error) {
logger.error('Failed to get citywide assignments', error);
throw error;
}
}
/**
* Check if agent has assignment to target
*/
async hasAssignment(agentId: string, targetMicrodaoId: string): Promise<boolean> {
try {
const result = await db.query(
`SELECT 1 FROM agent_assignments
WHERE agent_id = $1 AND target_microdao_id = $2 AND end_ts IS NULL
LIMIT 1`,
[agentId, targetMicrodaoId]
);
return result.rows.length > 0;
} catch (error) {
logger.error('Failed to check assignment', error);
throw error;
}
}
/**
* Get agent's effective scope (home + assignments)
*/
async getAgentScope(agentId: string): Promise<{
homeMicrodaoId: string | null;
assignments: AgentAssignment[];
effectiveScope: AssignmentScope;
}> {
try {
// Get agent's home MicroDAO
const agent = await db.query(
`SELECT home_microdao_id, agent_service_scope FROM agents WHERE id = $1`,
[agentId]
);
if (agent.rows.length === 0) {
throw new Error(`Agent not found: ${agentId}`);
}
const assignments = await this.getAgentAssignments(agentId);
// Determine effective scope
let effectiveScope: AssignmentScope = 'microdao';
if (agent.rows[0].agent_service_scope === 'city') {
effectiveScope = 'city';
} else if (assignments.some(a => a.scope === 'city')) {
effectiveScope = 'city';
} else if (assignments.some(a => a.scope === 'district')) {
effectiveScope = 'district';
}
return {
homeMicrodaoId: agent.rows[0].home_microdao_id,
assignments,
effectiveScope,
};
} catch (error) {
logger.error(`Failed to get agent scope: ${agentId}`, error);
throw error;
}
}
/**
* Publish event to outbox for NATS
*/
private async publishEvent(eventType: string, payload: Record<string, unknown>): Promise<void> {
await db.query(
`INSERT INTO event_outbox (event_type, subject, payload)
VALUES ($1, $2, $3)`,
[eventType, eventType, JSON.stringify(payload)]
);
}
}
export const assignmentService = new AssignmentService();

View File

@@ -0,0 +1,278 @@
/**
* DAIS Service - DAARION Autonomous Identity System
* Based on: docs/foundation/DAARION_Identity_And_Access_Draft_v1.md
*/
import { db } from '../../infra/db/client';
import { logger } from '../../infra/logger/logger';
import type {
DaisIdentity,
DaisProfile,
DaisEmail,
DaisWallet,
CreateDaisRequest,
DaisCreationResult,
DaisTrustLevel,
} from '../../domain/dais/types';
import { v4 as uuidv4 } from 'uuid';
export class DaisService {
/**
* Create a new DAIS identity
*/
async createIdentity(request: CreateDaisRequest): Promise<DaisCreationResult> {
const id = `dais-${uuidv4()}`;
const did = `did:daarion:${uuidv4()}`;
try {
// Create DAIS identity
const identity = await db.query<DaisIdentity>(
`INSERT INTO dais_identities (id, did, default_email, default_wallet, trust_level)
VALUES ($1, $2, $3, $4, $5)
RETURNING *`,
[id, did, request.email || null, request.walletAddress || null, 'agent']
);
// Add email if provided
if (request.email) {
await db.query(
`INSERT INTO dais_emails (dais_id, email, verified)
VALUES ($1, $2, false)`,
[id, request.email]
);
}
// Add wallet if provided
if (request.walletAddress) {
await db.query(
`INSERT INTO dais_wallets (dais_id, wallet_address, network, verified)
VALUES ($1, $2, $3, false)`,
[id, request.walletAddress, request.network || 'evm']
);
}
// Create agent linked to DAIS
const agentId = `agent-${uuidv4()}`;
const matrixHandle = `@${agentId}:matrix.daarion.city`;
await db.query(
`INSERT INTO agents (id, name, kind, dais_identity_id, agent_role, home_microdao_id)
VALUES ($1, $2, $3, $4, $5, $6)`,
[agentId, 'New Agent', 'personal', id, 'regular', 'daarion']
);
// Update DAIS with matrix handle
await db.query(
`UPDATE dais_identities SET matrix_handle = $1 WHERE id = $2`,
[matrixHandle, id]
);
logger.info(`Created DAIS identity: ${id}, agent: ${agentId}`);
return {
identity: identity.rows[0],
agentId,
matrixHandle,
};
} catch (error) {
logger.error('Failed to create DAIS identity', error);
throw error;
}
}
/**
* Get DAIS profile with all linked identities
*/
async getProfile(daisId: string): Promise<DaisProfile | null> {
try {
const identity = await db.query<DaisIdentity>(
`SELECT * FROM dais_identities WHERE id = $1`,
[daisId]
);
if (identity.rows.length === 0) {
return null;
}
const emails = await db.query<DaisEmail>(
`SELECT * FROM dais_emails WHERE dais_id = $1`,
[daisId]
);
const wallets = await db.query<DaisWallet>(
`SELECT * FROM dais_wallets WHERE dais_id = $1`,
[daisId]
);
const keys = await db.query(
`SELECT * FROM dais_keys WHERE dais_id = $1 AND revoked_at IS NULL`,
[daisId]
);
return {
identity: identity.rows[0],
emails: emails.rows,
wallets: wallets.rows,
keys: keys.rows,
};
} catch (error) {
logger.error(`Failed to get DAIS profile: ${daisId}`, error);
throw error;
}
}
/**
* Get DAIS by agent ID
*/
async getByAgentId(agentId: string): Promise<DaisProfile | null> {
try {
const agent = await db.query(
`SELECT dais_identity_id FROM agents WHERE id = $1`,
[agentId]
);
if (agent.rows.length === 0 || !agent.rows[0].dais_identity_id) {
return null;
}
return this.getProfile(agent.rows[0].dais_identity_id);
} catch (error) {
logger.error(`Failed to get DAIS by agent: ${agentId}`, error);
throw error;
}
}
/**
* Add email to DAIS
*/
async addEmail(daisId: string, email: string): Promise<DaisEmail> {
try {
const result = await db.query<DaisEmail>(
`INSERT INTO dais_emails (dais_id, email, verified)
VALUES ($1, $2, false)
RETURNING *`,
[daisId, email]
);
logger.info(`Added email to DAIS ${daisId}: ${email}`);
return result.rows[0];
} catch (error) {
logger.error(`Failed to add email to DAIS: ${daisId}`, error);
throw error;
}
}
/**
* Verify email
*/
async verifyEmail(daisId: string, email: string): Promise<void> {
try {
await db.query(
`UPDATE dais_emails
SET verified = true, verified_at = now()
WHERE dais_id = $1 AND email = $2`,
[daisId, email]
);
// Update trust level if this is first verified email
await this.updateTrustLevel(daisId);
logger.info(`Verified email for DAIS ${daisId}: ${email}`);
} catch (error) {
logger.error(`Failed to verify email: ${daisId}`, error);
throw error;
}
}
/**
* Add wallet to DAIS
*/
async addWallet(
daisId: string,
walletAddress: string,
network: 'evm' | 'ton' | 'solana' = 'evm'
): Promise<DaisWallet> {
try {
const result = await db.query<DaisWallet>(
`INSERT INTO dais_wallets (dais_id, wallet_address, network, verified)
VALUES ($1, $2, $3, false)
RETURNING *`,
[daisId, walletAddress, network]
);
logger.info(`Added wallet to DAIS ${daisId}: ${walletAddress}`);
return result.rows[0];
} catch (error) {
logger.error(`Failed to add wallet to DAIS: ${daisId}`, error);
throw error;
}
}
/**
* Verify wallet (after SIWE signature)
*/
async verifyWallet(daisId: string, walletAddress: string): Promise<void> {
try {
await db.query(
`UPDATE dais_wallets
SET verified = true, verified_at = now()
WHERE dais_id = $1 AND wallet_address = $2`,
[daisId, walletAddress]
);
// Update trust level
await this.updateTrustLevel(daisId);
logger.info(`Verified wallet for DAIS ${daisId}: ${walletAddress}`);
} catch (error) {
logger.error(`Failed to verify wallet: ${daisId}`, error);
throw error;
}
}
/**
* Update trust level based on verified identities
*/
private async updateTrustLevel(daisId: string): Promise<void> {
const profile = await this.getProfile(daisId);
if (!profile) return;
const hasVerifiedEmail = profile.emails.some(e => e.verified);
const hasVerifiedWallet = profile.wallets.some(w => w.verified);
let newLevel: DaisTrustLevel = 'guest';
if (hasVerifiedEmail && hasVerifiedWallet) {
newLevel = 'verified';
} else if (hasVerifiedEmail) {
newLevel = 'agent';
}
await db.query(
`UPDATE dais_identities SET trust_level = $1, updated_at = now() WHERE id = $2`,
[newLevel, daisId]
);
}
/**
* Promote agent to orchestrator (updates DAIS trust level)
*/
async promoteToOrchestrator(daisId: string): Promise<void> {
try {
await db.query(
`UPDATE dais_identities
SET trust_level = 'orchestrator', updated_at = now()
WHERE id = $1`,
[daisId]
);
logger.info(`Promoted DAIS to orchestrator: ${daisId}`);
} catch (error) {
logger.error(`Failed to promote to orchestrator: ${daisId}`, error);
throw error;
}
}
}
export const daisService = new DaisService();