@agentlensai/server 0.8.0 → 0.10.0

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.
Files changed (111) hide show
  1. package/dist/db/anonymous-id-manager.d.ts +44 -0
  2. package/dist/db/anonymous-id-manager.d.ts.map +1 -0
  3. package/dist/db/anonymous-id-manager.js +90 -0
  4. package/dist/db/anonymous-id-manager.js.map +1 -0
  5. package/dist/db/capability-store.d.ts +82 -0
  6. package/dist/db/capability-store.d.ts.map +1 -0
  7. package/dist/db/capability-store.js +221 -0
  8. package/dist/db/capability-store.js.map +1 -0
  9. package/dist/db/migrate.d.ts.map +1 -1
  10. package/dist/db/migrate.js +136 -0
  11. package/dist/db/migrate.js.map +1 -1
  12. package/dist/db/schema.sqlite.d.ts +1663 -2
  13. package/dist/db/schema.sqlite.d.ts.map +1 -1
  14. package/dist/db/schema.sqlite.js +135 -1
  15. package/dist/db/schema.sqlite.js.map +1 -1
  16. package/dist/index.d.ts +5 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +48 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/redaction/human-review-layer.d.ts +37 -0
  21. package/dist/lib/redaction/human-review-layer.d.ts.map +1 -0
  22. package/dist/lib/redaction/human-review-layer.js +62 -0
  23. package/dist/lib/redaction/human-review-layer.js.map +1 -0
  24. package/dist/lib/redaction/index.d.ts +12 -0
  25. package/dist/lib/redaction/index.d.ts.map +1 -0
  26. package/dist/lib/redaction/index.js +12 -0
  27. package/dist/lib/redaction/index.js.map +1 -0
  28. package/dist/lib/redaction/pii-detection-layer.d.ts +30 -0
  29. package/dist/lib/redaction/pii-detection-layer.d.ts.map +1 -0
  30. package/dist/lib/redaction/pii-detection-layer.js +183 -0
  31. package/dist/lib/redaction/pii-detection-layer.js.map +1 -0
  32. package/dist/lib/redaction/pipeline.d.ts +26 -0
  33. package/dist/lib/redaction/pipeline.d.ts.map +1 -0
  34. package/dist/lib/redaction/pipeline.js +91 -0
  35. package/dist/lib/redaction/pipeline.js.map +1 -0
  36. package/dist/lib/redaction/secret-detection-layer.d.ts +10 -0
  37. package/dist/lib/redaction/secret-detection-layer.d.ts.map +1 -0
  38. package/dist/lib/redaction/secret-detection-layer.js +79 -0
  39. package/dist/lib/redaction/secret-detection-layer.js.map +1 -0
  40. package/dist/lib/redaction/secret-patterns.d.ts +29 -0
  41. package/dist/lib/redaction/secret-patterns.d.ts.map +1 -0
  42. package/dist/lib/redaction/secret-patterns.js +133 -0
  43. package/dist/lib/redaction/secret-patterns.js.map +1 -0
  44. package/dist/lib/redaction/semantic-denylist-layer.d.ts +10 -0
  45. package/dist/lib/redaction/semantic-denylist-layer.d.ts.map +1 -0
  46. package/dist/lib/redaction/semantic-denylist-layer.js +64 -0
  47. package/dist/lib/redaction/semantic-denylist-layer.js.map +1 -0
  48. package/dist/lib/redaction/tenant-deidentification-layer.d.ts +10 -0
  49. package/dist/lib/redaction/tenant-deidentification-layer.d.ts.map +1 -0
  50. package/dist/lib/redaction/tenant-deidentification-layer.js +64 -0
  51. package/dist/lib/redaction/tenant-deidentification-layer.js.map +1 -0
  52. package/dist/lib/redaction/url-path-scrubbing-layer.d.ts +14 -0
  53. package/dist/lib/redaction/url-path-scrubbing-layer.d.ts.map +1 -0
  54. package/dist/lib/redaction/url-path-scrubbing-layer.js +156 -0
  55. package/dist/lib/redaction/url-path-scrubbing-layer.js.map +1 -0
  56. package/dist/routes/agents.d.ts.map +1 -1
  57. package/dist/routes/agents.js +3 -9
  58. package/dist/routes/agents.js.map +1 -1
  59. package/dist/routes/audit.d.ts +15 -0
  60. package/dist/routes/audit.d.ts.map +1 -0
  61. package/dist/routes/audit.js +177 -0
  62. package/dist/routes/audit.js.map +1 -0
  63. package/dist/routes/capabilities-top.d.ts +15 -0
  64. package/dist/routes/capabilities-top.d.ts.map +1 -0
  65. package/dist/routes/capabilities-top.js +77 -0
  66. package/dist/routes/capabilities-top.js.map +1 -0
  67. package/dist/routes/capabilities.d.ts +15 -0
  68. package/dist/routes/capabilities.d.ts.map +1 -0
  69. package/dist/routes/capabilities.js +86 -0
  70. package/dist/routes/capabilities.js.map +1 -0
  71. package/dist/routes/community.d.ts +24 -0
  72. package/dist/routes/community.d.ts.map +1 -0
  73. package/dist/routes/community.js +272 -0
  74. package/dist/routes/community.js.map +1 -0
  75. package/dist/routes/delegation.d.ts +20 -0
  76. package/dist/routes/delegation.d.ts.map +1 -0
  77. package/dist/routes/delegation.js +108 -0
  78. package/dist/routes/delegation.js.map +1 -0
  79. package/dist/routes/delegations-top.d.ts +12 -0
  80. package/dist/routes/delegations-top.d.ts.map +1 -0
  81. package/dist/routes/delegations-top.js +43 -0
  82. package/dist/routes/delegations-top.js.map +1 -0
  83. package/dist/routes/discovery.d.ts +19 -0
  84. package/dist/routes/discovery.d.ts.map +1 -0
  85. package/dist/routes/discovery.js +96 -0
  86. package/dist/routes/discovery.js.map +1 -0
  87. package/dist/routes/redaction-test.d.ts +14 -0
  88. package/dist/routes/redaction-test.d.ts.map +1 -0
  89. package/dist/routes/redaction-test.js +33 -0
  90. package/dist/routes/redaction-test.js.map +1 -0
  91. package/dist/routes/trust.d.ts +16 -0
  92. package/dist/routes/trust.d.ts.map +1 -0
  93. package/dist/routes/trust.js +23 -0
  94. package/dist/routes/trust.js.map +1 -0
  95. package/dist/services/community-service.d.ts +283 -0
  96. package/dist/services/community-service.d.ts.map +1 -0
  97. package/dist/services/community-service.js +816 -0
  98. package/dist/services/community-service.js.map +1 -0
  99. package/dist/services/delegation-service.d.ts +149 -0
  100. package/dist/services/delegation-service.d.ts.map +1 -0
  101. package/dist/services/delegation-service.js +605 -0
  102. package/dist/services/delegation-service.js.map +1 -0
  103. package/dist/services/discovery-service.d.ts +39 -0
  104. package/dist/services/discovery-service.d.ts.map +1 -0
  105. package/dist/services/discovery-service.js +186 -0
  106. package/dist/services/discovery-service.js.map +1 -0
  107. package/dist/services/trust-service.d.ts +59 -0
  108. package/dist/services/trust-service.d.ts.map +1 -0
  109. package/dist/services/trust-service.js +139 -0
  110. package/dist/services/trust-service.js.map +1 -0
  111. package/package.json +2 -2
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Anonymous ID Manager (Phase 4 — Story 1.4)
3
+ *
4
+ * Manages rotating anonymous agent IDs with 24h rotation.
5
+ * Old IDs are kept for audit trail purposes.
6
+ */
7
+ import type { SqliteDb } from './index.js';
8
+ export interface AnonymousIdManagerOptions {
9
+ /** Override for current time (useful for testing) */
10
+ now?: () => Date;
11
+ }
12
+ export declare class AnonymousIdManager {
13
+ private readonly db;
14
+ private readonly now;
15
+ constructor(db: SqliteDb, options?: AnonymousIdManagerOptions);
16
+ /**
17
+ * H4 FIX: Hash the agentId before storing in the DB so that if the DB
18
+ * is compromised, agentId → anonymousId mapping is not directly readable.
19
+ */
20
+ private hashAgentId;
21
+ /**
22
+ * Get the current anonymous ID for a tenant+agent pair,
23
+ * or create/rotate one if needed.
24
+ *
25
+ * - Lazy creation: first call creates the ID
26
+ * - 24h rotation: after expiry, a new ID is created
27
+ * - Old IDs are kept for audit trail
28
+ */
29
+ getOrRotateAnonymousId(tenantId: string, agentId: string): string;
30
+ /**
31
+ * Get all historical anonymous IDs for a tenant+agent (for audit).
32
+ */
33
+ getAuditTrail(tenantId: string, agentId: string): Array<{
34
+ anonymousAgentId: string;
35
+ validFrom: string;
36
+ validUntil: string;
37
+ }>;
38
+ /**
39
+ * Get the anonymous contributor ID for a tenant (not agent-specific).
40
+ * Used for lesson sharing where the contributor is the tenant.
41
+ */
42
+ getOrRotateContributorId(tenantId: string): string;
43
+ }
44
+ //# sourceMappingURL=anonymous-id-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymous-id-manager.d.ts","sourceRoot":"","sources":["../../src/db/anonymous-id-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAS3C,MAAM,WAAW,yBAAyB;IACxC,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,qBAAa,kBAAkB;IAI3B,OAAO,CAAC,QAAQ,CAAC,EAAE;IAHrB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;gBAGd,EAAE,EAAE,QAAQ,EAC7B,OAAO,CAAC,EAAE,yBAAyB;IAKrC;;;OAGG;IACH,OAAO,CAAC,WAAW;IAMnB;;;;;;;OAOG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IA0CjE;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QACtD,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAkBF;;;OAGG;IACH,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAGnD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Anonymous ID Manager (Phase 4 — Story 1.4)
3
+ *
4
+ * Manages rotating anonymous agent IDs with 24h rotation.
5
+ * Old IDs are kept for audit trail purposes.
6
+ */
7
+ import { eq, and, gte } from 'drizzle-orm';
8
+ import { randomUUID, createHash } from 'crypto';
9
+ import * as schema from './schema.sqlite.js';
10
+ /** Duration of anonymous ID validity (24 hours in milliseconds) */
11
+ const ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1000;
12
+ /** Salt for hashing agent IDs in the anonymous ID map (H4 fix) */
13
+ const ANON_ID_HASH_SALT = 'agentlens-anon-id-salt-v1';
14
+ export class AnonymousIdManager {
15
+ db;
16
+ now;
17
+ constructor(db, options) {
18
+ this.db = db;
19
+ this.now = options?.now ?? (() => new Date());
20
+ }
21
+ /**
22
+ * H4 FIX: Hash the agentId before storing in the DB so that if the DB
23
+ * is compromised, agentId → anonymousId mapping is not directly readable.
24
+ */
25
+ hashAgentId(tenantId, agentId) {
26
+ return createHash('sha256')
27
+ .update(`${ANON_ID_HASH_SALT}:${tenantId}:${agentId}`)
28
+ .digest('hex');
29
+ }
30
+ /**
31
+ * Get the current anonymous ID for a tenant+agent pair,
32
+ * or create/rotate one if needed.
33
+ *
34
+ * - Lazy creation: first call creates the ID
35
+ * - 24h rotation: after expiry, a new ID is created
36
+ * - Old IDs are kept for audit trail
37
+ */
38
+ getOrRotateAnonymousId(tenantId, agentId) {
39
+ const now = this.now();
40
+ const nowIso = now.toISOString();
41
+ // H4 FIX: Store hashed agentId to prevent de-anonymization if DB is compromised
42
+ const hashedAgentId = this.hashAgentId(tenantId, agentId);
43
+ // Find a valid (non-expired) anonymous ID
44
+ const existing = this.db
45
+ .select()
46
+ .from(schema.anonymousIdMap)
47
+ .where(and(eq(schema.anonymousIdMap.tenantId, tenantId), eq(schema.anonymousIdMap.agentId, hashedAgentId), gte(schema.anonymousIdMap.validUntil, nowIso)))
48
+ .get();
49
+ if (existing) {
50
+ return existing.anonymousAgentId;
51
+ }
52
+ // Create a new anonymous ID with 24h validity
53
+ const newId = randomUUID();
54
+ const validUntil = new Date(now.getTime() + ROTATION_INTERVAL_MS);
55
+ this.db
56
+ .insert(schema.anonymousIdMap)
57
+ .values({
58
+ tenantId,
59
+ agentId: hashedAgentId,
60
+ anonymousAgentId: newId,
61
+ validFrom: nowIso,
62
+ validUntil: validUntil.toISOString(),
63
+ })
64
+ .run();
65
+ return newId;
66
+ }
67
+ /**
68
+ * Get all historical anonymous IDs for a tenant+agent (for audit).
69
+ */
70
+ getAuditTrail(tenantId, agentId) {
71
+ const hashedAgentId = this.hashAgentId(tenantId, agentId);
72
+ return this.db
73
+ .select({
74
+ anonymousAgentId: schema.anonymousIdMap.anonymousAgentId,
75
+ validFrom: schema.anonymousIdMap.validFrom,
76
+ validUntil: schema.anonymousIdMap.validUntil,
77
+ })
78
+ .from(schema.anonymousIdMap)
79
+ .where(and(eq(schema.anonymousIdMap.tenantId, tenantId), eq(schema.anonymousIdMap.agentId, hashedAgentId)))
80
+ .all();
81
+ }
82
+ /**
83
+ * Get the anonymous contributor ID for a tenant (not agent-specific).
84
+ * Used for lesson sharing where the contributor is the tenant.
85
+ */
86
+ getOrRotateContributorId(tenantId) {
87
+ return this.getOrRotateAnonymousId(tenantId, '__contributor__');
88
+ }
89
+ }
90
+ //# sourceMappingURL=anonymous-id-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymous-id-manager.js","sourceRoot":"","sources":["../../src/db/anonymous-id-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Capability Registry Store (Story 5.1)
3
+ *
4
+ * CRUD operations for the capability_registry table with tenant isolation.
5
+ */
6
+ import type { SqliteDb } from './index.js';
7
+ import { type TaskType } from '@agentlensai/core';
8
+ /** Input for creating/updating a capability */
9
+ export interface CapabilityInput {
10
+ taskType: string;
11
+ customType?: string;
12
+ inputSchema: Record<string, unknown>;
13
+ outputSchema: Record<string, unknown>;
14
+ inputMimeTypes?: string[];
15
+ outputMimeTypes?: string[];
16
+ qualityMetrics?: Record<string, unknown>;
17
+ estimatedLatencyMs?: number;
18
+ estimatedCostUsd?: number;
19
+ maxInputBytes?: number;
20
+ scope?: 'internal' | 'public';
21
+ }
22
+ /** Parsed capability returned from store */
23
+ export interface Capability {
24
+ id: string;
25
+ tenantId: string;
26
+ agentId: string;
27
+ taskType: TaskType;
28
+ customType?: string;
29
+ inputSchema: Record<string, unknown>;
30
+ outputSchema: Record<string, unknown>;
31
+ qualityMetrics: Record<string, unknown>;
32
+ estimatedLatencyMs?: number;
33
+ estimatedCostUsd?: number;
34
+ maxInputBytes?: number;
35
+ scope: 'internal' | 'public';
36
+ enabled: boolean;
37
+ acceptDelegations: boolean;
38
+ inboundRateLimit: number;
39
+ outboundRateLimit: number;
40
+ createdAt: string;
41
+ updatedAt: string;
42
+ }
43
+ export declare class ValidationError extends Error {
44
+ constructor(message: string);
45
+ }
46
+ export declare class CapabilityStore {
47
+ private readonly db;
48
+ constructor(db: SqliteDb);
49
+ /**
50
+ * Validate capability input and throw ValidationError on problems.
51
+ */
52
+ private validate;
53
+ /**
54
+ * Create a new capability registration.
55
+ */
56
+ create(tenantId: string, agentId: string, input: CapabilityInput): Capability;
57
+ /**
58
+ * Get a capability by ID (tenant-scoped).
59
+ */
60
+ getById(tenantId: string, id: string): Capability | null;
61
+ /**
62
+ * List all capabilities for an agent (tenant-scoped).
63
+ */
64
+ listByTenant(tenantId: string, opts?: {
65
+ taskType?: string;
66
+ agentId?: string;
67
+ }): Capability[];
68
+ listByAgent(tenantId: string, agentId: string): Capability[];
69
+ /**
70
+ * Update a capability by ID (tenant-scoped).
71
+ */
72
+ update(tenantId: string, id: string, input: Partial<CapabilityInput>): Capability;
73
+ /**
74
+ * Delete a capability by ID (tenant-scoped).
75
+ */
76
+ delete(tenantId: string, id: string): boolean;
77
+ /**
78
+ * Delete all capabilities for an agent (tenant-scoped).
79
+ */
80
+ deleteByAgent(tenantId: string, agentId: string): number;
81
+ }
82
+ //# sourceMappingURL=capability-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-store.d.ts","sourceRoot":"","sources":["../../src/db/capability-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAc,KAAK,QAAQ,EAA+B,MAAM,mBAAmB,CAAC;AAY3F,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;CAC/B;AAED,4CAA4C;AAC5C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAuCD,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEzC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAgChB;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU;IA6B7E;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IASxD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,EAAE;IAe5F,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;IAc5D;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU;IAwCjF;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ7C;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAYzD"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Capability Registry Store (Story 5.1)
3
+ *
4
+ * CRUD operations for the capability_registry table with tenant isolation.
5
+ */
6
+ import { randomUUID } from 'node:crypto';
7
+ import { eq, and } from 'drizzle-orm';
8
+ import { capabilityRegistry } from './schema.sqlite.js';
9
+ import { TASK_TYPES } from '@agentlensai/core';
10
+ import { NotFoundError } from './errors.js';
11
+ /** Valid task types as a Set for fast lookup */
12
+ const VALID_TASK_TYPES = new Set(TASK_TYPES);
13
+ /** Regex for customType validation: alphanumeric + hyphens, max 64 chars */
14
+ const CUSTOM_TYPE_REGEX = /^[a-zA-Z0-9-]{1,64}$/;
15
+ function rowToCapability(row) {
16
+ return {
17
+ id: row.id,
18
+ tenantId: row.tenantId,
19
+ agentId: row.agentId,
20
+ taskType: row.taskType,
21
+ customType: row.customType ?? undefined,
22
+ inputSchema: JSON.parse(row.inputSchema),
23
+ outputSchema: JSON.parse(row.outputSchema),
24
+ qualityMetrics: JSON.parse(row.qualityMetrics),
25
+ estimatedLatencyMs: row.estimatedLatencyMs ?? undefined,
26
+ estimatedCostUsd: row.estimatedCostUsd ?? undefined,
27
+ maxInputBytes: row.maxInputBytes ?? undefined,
28
+ scope: row.scope,
29
+ enabled: row.enabled,
30
+ acceptDelegations: row.acceptDelegations,
31
+ inboundRateLimit: row.inboundRateLimit,
32
+ outboundRateLimit: row.outboundRateLimit,
33
+ createdAt: row.createdAt,
34
+ updatedAt: row.updatedAt,
35
+ };
36
+ }
37
+ /**
38
+ * Validate a JSON Schema object (basic validation).
39
+ * Must be a non-null object with a "type" property.
40
+ */
41
+ function validateJsonSchema(schema, label) {
42
+ if (schema === null || schema === undefined || typeof schema !== 'object' || Array.isArray(schema)) {
43
+ throw new ValidationError(`${label} must be a JSON Schema object`);
44
+ }
45
+ const s = schema;
46
+ if (!s.type && !s.$ref && !s.oneOf && !s.anyOf && !s.allOf && !s.properties && !s.enum) {
47
+ throw new ValidationError(`${label} must be a valid JSON Schema (missing type or schema keyword)`);
48
+ }
49
+ }
50
+ export class ValidationError extends Error {
51
+ constructor(message) {
52
+ super(message);
53
+ this.name = 'ValidationError';
54
+ }
55
+ }
56
+ export class CapabilityStore {
57
+ db;
58
+ constructor(db) {
59
+ this.db = db;
60
+ }
61
+ /**
62
+ * Validate capability input and throw ValidationError on problems.
63
+ */
64
+ validate(input) {
65
+ // Validate taskType
66
+ if (!VALID_TASK_TYPES.has(input.taskType)) {
67
+ throw new ValidationError(`Invalid taskType "${input.taskType}". Must be one of: ${TASK_TYPES.join(', ')}`);
68
+ }
69
+ // Validate customType format if provided
70
+ if (input.customType !== undefined && input.customType !== null) {
71
+ if (!CUSTOM_TYPE_REGEX.test(input.customType)) {
72
+ throw new ValidationError('customType must be 1-64 characters, alphanumeric and hyphens only');
73
+ }
74
+ }
75
+ // customType is required when taskType is 'custom'
76
+ if (input.taskType === 'custom' && !input.customType) {
77
+ throw new ValidationError('customType is required when taskType is "custom"');
78
+ }
79
+ // Validate input/output schemas
80
+ validateJsonSchema(input.inputSchema, 'inputSchema');
81
+ validateJsonSchema(input.outputSchema, 'outputSchema');
82
+ // Validate scope if provided
83
+ if (input.scope !== undefined && input.scope !== 'internal' && input.scope !== 'public') {
84
+ throw new ValidationError('scope must be "internal" or "public"');
85
+ }
86
+ }
87
+ /**
88
+ * Create a new capability registration.
89
+ */
90
+ create(tenantId, agentId, input) {
91
+ this.validate(input);
92
+ const now = new Date().toISOString();
93
+ const id = randomUUID();
94
+ this.db
95
+ .insert(capabilityRegistry)
96
+ .values({
97
+ id,
98
+ tenantId,
99
+ agentId,
100
+ taskType: input.taskType,
101
+ customType: input.customType ?? null,
102
+ inputSchema: JSON.stringify(input.inputSchema),
103
+ outputSchema: JSON.stringify(input.outputSchema),
104
+ qualityMetrics: JSON.stringify(input.qualityMetrics ?? {}),
105
+ estimatedLatencyMs: input.estimatedLatencyMs ?? null,
106
+ estimatedCostUsd: input.estimatedCostUsd ?? null,
107
+ maxInputBytes: input.maxInputBytes ?? null,
108
+ scope: input.scope ?? 'internal',
109
+ createdAt: now,
110
+ updatedAt: now,
111
+ })
112
+ .run();
113
+ return this.getById(tenantId, id);
114
+ }
115
+ /**
116
+ * Get a capability by ID (tenant-scoped).
117
+ */
118
+ getById(tenantId, id) {
119
+ const row = this.db
120
+ .select()
121
+ .from(capabilityRegistry)
122
+ .where(and(eq(capabilityRegistry.id, id), eq(capabilityRegistry.tenantId, tenantId)))
123
+ .get();
124
+ return row ? rowToCapability(row) : null;
125
+ }
126
+ /**
127
+ * List all capabilities for an agent (tenant-scoped).
128
+ */
129
+ listByTenant(tenantId, opts) {
130
+ let rows = this.db
131
+ .select()
132
+ .from(capabilityRegistry)
133
+ .where(eq(capabilityRegistry.tenantId, tenantId))
134
+ .all();
135
+ if (opts?.taskType) {
136
+ rows = rows.filter((r) => r.taskType === opts.taskType);
137
+ }
138
+ if (opts?.agentId) {
139
+ rows = rows.filter((r) => r.agentId === opts.agentId);
140
+ }
141
+ return rows.map(rowToCapability);
142
+ }
143
+ listByAgent(tenantId, agentId) {
144
+ const rows = this.db
145
+ .select()
146
+ .from(capabilityRegistry)
147
+ .where(and(eq(capabilityRegistry.tenantId, tenantId), eq(capabilityRegistry.agentId, agentId)))
148
+ .all();
149
+ return rows.map(rowToCapability);
150
+ }
151
+ /**
152
+ * Update a capability by ID (tenant-scoped).
153
+ */
154
+ update(tenantId, id, input) {
155
+ const existing = this.getById(tenantId, id);
156
+ if (!existing) {
157
+ throw new NotFoundError(`Capability ${id} not found`);
158
+ }
159
+ // Merge with existing for validation
160
+ const merged = {
161
+ taskType: input.taskType ?? existing.taskType,
162
+ customType: input.customType !== undefined ? input.customType : existing.customType,
163
+ inputSchema: input.inputSchema ?? existing.inputSchema,
164
+ outputSchema: input.outputSchema ?? existing.outputSchema,
165
+ scope: input.scope ?? existing.scope,
166
+ };
167
+ this.validate(merged);
168
+ const updates = { updatedAt: new Date().toISOString() };
169
+ if (input.taskType !== undefined)
170
+ updates.taskType = input.taskType;
171
+ if (input.customType !== undefined)
172
+ updates.customType = input.customType;
173
+ if (input.inputSchema !== undefined)
174
+ updates.inputSchema = JSON.stringify(input.inputSchema);
175
+ if (input.outputSchema !== undefined)
176
+ updates.outputSchema = JSON.stringify(input.outputSchema);
177
+ if (input.qualityMetrics !== undefined)
178
+ updates.qualityMetrics = JSON.stringify(input.qualityMetrics);
179
+ if (input.estimatedLatencyMs !== undefined)
180
+ updates.estimatedLatencyMs = input.estimatedLatencyMs;
181
+ if (input.estimatedCostUsd !== undefined)
182
+ updates.estimatedCostUsd = input.estimatedCostUsd;
183
+ if (input.maxInputBytes !== undefined)
184
+ updates.maxInputBytes = input.maxInputBytes;
185
+ if (input.scope !== undefined)
186
+ updates.scope = input.scope;
187
+ // Allow toggling enabled/acceptDelegations via partial update
188
+ const extra = input;
189
+ if (extra.enabled !== undefined)
190
+ updates.enabled = Boolean(extra.enabled);
191
+ if (extra.acceptDelegations !== undefined)
192
+ updates.acceptDelegations = Boolean(extra.acceptDelegations);
193
+ this.db
194
+ .update(capabilityRegistry)
195
+ .set(updates)
196
+ .where(and(eq(capabilityRegistry.id, id), eq(capabilityRegistry.tenantId, tenantId)))
197
+ .run();
198
+ return this.getById(tenantId, id);
199
+ }
200
+ /**
201
+ * Delete a capability by ID (tenant-scoped).
202
+ */
203
+ delete(tenantId, id) {
204
+ const result = this.db
205
+ .delete(capabilityRegistry)
206
+ .where(and(eq(capabilityRegistry.id, id), eq(capabilityRegistry.tenantId, tenantId)))
207
+ .run();
208
+ return result.changes > 0;
209
+ }
210
+ /**
211
+ * Delete all capabilities for an agent (tenant-scoped).
212
+ */
213
+ deleteByAgent(tenantId, agentId) {
214
+ const result = this.db
215
+ .delete(capabilityRegistry)
216
+ .where(and(eq(capabilityRegistry.tenantId, tenantId), eq(capabilityRegistry.agentId, agentId)))
217
+ .run();
218
+ return result.changes;
219
+ }
220
+ }
221
+ //# sourceMappingURL=capability-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-store.js","sourceRoot":"","sources":["../../src/db/capability-store.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAugBhD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB,CAoBA"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAypBhD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB,CAoBA"}
@@ -481,6 +481,142 @@ export function runMigrations(db) {
481
481
  }
482
482
  // Partial index for finding paused agents efficiently
483
483
  db.run(sql `CREATE INDEX IF NOT EXISTS idx_agents_paused ON agents(tenant_id, paused_at) WHERE paused_at IS NOT NULL`);
484
+ // ─── Phase 4: Sharing & Discovery tables (Stories 1.3) ──────────
485
+ db.run(sql `
486
+ CREATE TABLE IF NOT EXISTS sharing_config (
487
+ tenant_id TEXT PRIMARY KEY,
488
+ enabled INTEGER NOT NULL DEFAULT 0,
489
+ human_review_enabled INTEGER NOT NULL DEFAULT 0,
490
+ pool_endpoint TEXT,
491
+ anonymous_contributor_id TEXT,
492
+ purge_token TEXT,
493
+ rate_limit_per_hour INTEGER NOT NULL DEFAULT 50,
494
+ volume_alert_threshold INTEGER NOT NULL DEFAULT 100,
495
+ updated_at TEXT NOT NULL
496
+ )
497
+ `);
498
+ db.run(sql `
499
+ CREATE TABLE IF NOT EXISTS agent_sharing_config (
500
+ tenant_id TEXT NOT NULL,
501
+ agent_id TEXT NOT NULL,
502
+ enabled INTEGER NOT NULL DEFAULT 0,
503
+ categories TEXT NOT NULL DEFAULT '[]',
504
+ updated_at TEXT NOT NULL,
505
+ PRIMARY KEY (tenant_id, agent_id)
506
+ )
507
+ `);
508
+ db.run(sql `
509
+ CREATE TABLE IF NOT EXISTS deny_list_rules (
510
+ id TEXT PRIMARY KEY,
511
+ tenant_id TEXT NOT NULL,
512
+ pattern TEXT NOT NULL,
513
+ is_regex INTEGER NOT NULL DEFAULT 0,
514
+ reason TEXT NOT NULL,
515
+ created_at TEXT NOT NULL
516
+ )
517
+ `);
518
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_deny_list_rules_tenant ON deny_list_rules(tenant_id)`);
519
+ db.run(sql `
520
+ CREATE TABLE IF NOT EXISTS sharing_audit_log (
521
+ id TEXT PRIMARY KEY,
522
+ tenant_id TEXT NOT NULL,
523
+ event_type TEXT NOT NULL,
524
+ lesson_id TEXT,
525
+ anonymous_lesson_id TEXT,
526
+ lesson_hash TEXT,
527
+ redaction_findings TEXT,
528
+ query_text TEXT,
529
+ result_ids TEXT,
530
+ pool_endpoint TEXT,
531
+ initiated_by TEXT,
532
+ timestamp TEXT NOT NULL
533
+ )
534
+ `);
535
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_sharing_audit_tenant_ts ON sharing_audit_log(tenant_id, timestamp)`);
536
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_sharing_audit_type ON sharing_audit_log(tenant_id, event_type)`);
537
+ db.run(sql `
538
+ CREATE TABLE IF NOT EXISTS sharing_review_queue (
539
+ id TEXT PRIMARY KEY,
540
+ tenant_id TEXT NOT NULL,
541
+ lesson_id TEXT NOT NULL,
542
+ original_title TEXT NOT NULL,
543
+ original_content TEXT NOT NULL,
544
+ redacted_title TEXT NOT NULL,
545
+ redacted_content TEXT NOT NULL,
546
+ redaction_findings TEXT NOT NULL,
547
+ status TEXT NOT NULL DEFAULT 'pending',
548
+ reviewed_by TEXT,
549
+ reviewed_at TEXT,
550
+ created_at TEXT NOT NULL,
551
+ expires_at TEXT NOT NULL
552
+ )
553
+ `);
554
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_review_queue_tenant_status ON sharing_review_queue(tenant_id, status)`);
555
+ db.run(sql `
556
+ CREATE TABLE IF NOT EXISTS anonymous_id_map (
557
+ tenant_id TEXT NOT NULL,
558
+ agent_id TEXT NOT NULL,
559
+ anonymous_agent_id TEXT NOT NULL,
560
+ valid_from TEXT NOT NULL,
561
+ valid_until TEXT NOT NULL,
562
+ PRIMARY KEY (tenant_id, agent_id, valid_from)
563
+ )
564
+ `);
565
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_anon_map_anon_id ON anonymous_id_map(anonymous_agent_id)`);
566
+ db.run(sql `
567
+ CREATE TABLE IF NOT EXISTS capability_registry (
568
+ id TEXT PRIMARY KEY,
569
+ tenant_id TEXT NOT NULL,
570
+ agent_id TEXT NOT NULL,
571
+ task_type TEXT NOT NULL,
572
+ custom_type TEXT,
573
+ input_schema TEXT NOT NULL,
574
+ output_schema TEXT NOT NULL,
575
+ quality_metrics TEXT NOT NULL DEFAULT '{}',
576
+ estimated_latency_ms INTEGER,
577
+ estimated_cost_usd REAL,
578
+ max_input_bytes INTEGER,
579
+ scope TEXT NOT NULL DEFAULT 'internal',
580
+ enabled INTEGER NOT NULL DEFAULT 1,
581
+ accept_delegations INTEGER NOT NULL DEFAULT 0,
582
+ inbound_rate_limit INTEGER NOT NULL DEFAULT 10,
583
+ outbound_rate_limit INTEGER NOT NULL DEFAULT 20,
584
+ created_at TEXT NOT NULL,
585
+ updated_at TEXT NOT NULL
586
+ )
587
+ `);
588
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_capability_tenant_agent ON capability_registry(tenant_id, agent_id)`);
589
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_capability_task_type ON capability_registry(tenant_id, task_type)`);
590
+ db.run(sql `
591
+ CREATE TABLE IF NOT EXISTS delegation_log (
592
+ id TEXT PRIMARY KEY,
593
+ tenant_id TEXT NOT NULL,
594
+ direction TEXT NOT NULL,
595
+ agent_id TEXT NOT NULL,
596
+ anonymous_target_id TEXT,
597
+ anonymous_source_id TEXT,
598
+ task_type TEXT NOT NULL,
599
+ status TEXT NOT NULL,
600
+ request_size_bytes INTEGER,
601
+ response_size_bytes INTEGER,
602
+ execution_time_ms INTEGER,
603
+ cost_usd REAL,
604
+ created_at TEXT NOT NULL,
605
+ completed_at TEXT
606
+ )
607
+ `);
608
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_delegation_tenant_ts ON delegation_log(tenant_id, created_at)`);
609
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_delegation_agent ON delegation_log(tenant_id, agent_id)`);
610
+ db.run(sql `CREATE INDEX IF NOT EXISTS idx_delegation_status ON delegation_log(tenant_id, status)`);
611
+ // ─── Discovery Config (Story 5.4) ──────────────────────
612
+ db.run(sql `
613
+ CREATE TABLE IF NOT EXISTS discovery_config (
614
+ tenant_id TEXT PRIMARY KEY,
615
+ min_trust_threshold INTEGER NOT NULL DEFAULT 60,
616
+ delegation_enabled INTEGER NOT NULL DEFAULT 0,
617
+ updated_at TEXT NOT NULL
618
+ )
619
+ `);
484
620
  }
485
621
  /**
486
622
  * Verify that WAL mode is enabled.