@hazeljs/organism 2.0.6

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 (170) hide show
  1. package/README.md +205 -0
  2. package/dist/__tests__/organism.embedding.test.d.ts +2 -0
  3. package/dist/__tests__/organism.embedding.test.d.ts.map +1 -0
  4. package/dist/__tests__/organism.embedding.test.js +166 -0
  5. package/dist/__tests__/organism.embedding.test.js.map +1 -0
  6. package/dist/__tests__/organism.phase1.test.d.ts +2 -0
  7. package/dist/__tests__/organism.phase1.test.d.ts.map +1 -0
  8. package/dist/__tests__/organism.phase1.test.js +375 -0
  9. package/dist/__tests__/organism.phase1.test.js.map +1 -0
  10. package/dist/__tests__/organism.phase2-3.test.d.ts +2 -0
  11. package/dist/__tests__/organism.phase2-3.test.d.ts.map +1 -0
  12. package/dist/__tests__/organism.phase2-3.test.js +320 -0
  13. package/dist/__tests__/organism.phase2-3.test.js.map +1 -0
  14. package/dist/__tests__/organism.phase4.test.d.ts +2 -0
  15. package/dist/__tests__/organism.phase4.test.d.ts.map +1 -0
  16. package/dist/__tests__/organism.phase4.test.js +217 -0
  17. package/dist/__tests__/organism.phase4.test.js.map +1 -0
  18. package/dist/bridge/signal-bridge.d.ts +46 -0
  19. package/dist/bridge/signal-bridge.d.ts.map +1 -0
  20. package/dist/bridge/signal-bridge.js +75 -0
  21. package/dist/bridge/signal-bridge.js.map +1 -0
  22. package/dist/core/capability-registry.d.ts +16 -0
  23. package/dist/core/capability-registry.d.ts.map +1 -0
  24. package/dist/core/capability-registry.js +71 -0
  25. package/dist/core/capability-registry.js.map +1 -0
  26. package/dist/core/clock.d.ts +17 -0
  27. package/dist/core/clock.d.ts.map +1 -0
  28. package/dist/core/clock.js +63 -0
  29. package/dist/core/clock.js.map +1 -0
  30. package/dist/core/mission.d.ts +4 -0
  31. package/dist/core/mission.d.ts.map +1 -0
  32. package/dist/core/mission.js +74 -0
  33. package/dist/core/mission.js.map +1 -0
  34. package/dist/core/organism-context.d.ts +139 -0
  35. package/dist/core/organism-context.d.ts.map +1 -0
  36. package/dist/core/organism-context.js +62 -0
  37. package/dist/core/organism-context.js.map +1 -0
  38. package/dist/core/organism-runtime.d.ts +199 -0
  39. package/dist/core/organism-runtime.d.ts.map +1 -0
  40. package/dist/core/organism-runtime.js +1016 -0
  41. package/dist/core/organism-runtime.js.map +1 -0
  42. package/dist/decorators/index.d.ts +39 -0
  43. package/dist/decorators/index.d.ts.map +1 -0
  44. package/dist/decorators/index.js +165 -0
  45. package/dist/decorators/index.js.map +1 -0
  46. package/dist/economy/market-engine.d.ts +57 -0
  47. package/dist/economy/market-engine.d.ts.map +1 -0
  48. package/dist/economy/market-engine.js +258 -0
  49. package/dist/economy/market-engine.js.map +1 -0
  50. package/dist/economy/resource-allocator.d.ts +39 -0
  51. package/dist/economy/resource-allocator.d.ts.map +1 -0
  52. package/dist/economy/resource-allocator.js +153 -0
  53. package/dist/economy/resource-allocator.js.map +1 -0
  54. package/dist/economy/utility-engine.d.ts +16 -0
  55. package/dist/economy/utility-engine.d.ts.map +1 -0
  56. package/dist/economy/utility-engine.js +80 -0
  57. package/dist/economy/utility-engine.js.map +1 -0
  58. package/dist/economy/utility-forecaster.d.ts +24 -0
  59. package/dist/economy/utility-forecaster.d.ts.map +1 -0
  60. package/dist/economy/utility-forecaster.js +60 -0
  61. package/dist/economy/utility-forecaster.js.map +1 -0
  62. package/dist/errors/organism.errors.d.ts +23 -0
  63. package/dist/errors/organism.errors.d.ts.map +1 -0
  64. package/dist/errors/organism.errors.js +50 -0
  65. package/dist/errors/organism.errors.js.map +1 -0
  66. package/dist/events/organism-events.d.ts +61 -0
  67. package/dist/events/organism-events.d.ts.map +1 -0
  68. package/dist/events/organism-events.js +95 -0
  69. package/dist/events/organism-events.js.map +1 -0
  70. package/dist/evolution/evolution-engine.d.ts +41 -0
  71. package/dist/evolution/evolution-engine.d.ts.map +1 -0
  72. package/dist/evolution/evolution-engine.js +135 -0
  73. package/dist/evolution/evolution-engine.js.map +1 -0
  74. package/dist/evolution/mutation-engine.d.ts +21 -0
  75. package/dist/evolution/mutation-engine.d.ts.map +1 -0
  76. package/dist/evolution/mutation-engine.js +84 -0
  77. package/dist/evolution/mutation-engine.js.map +1 -0
  78. package/dist/genealogy/genealogy-manager.d.ts +18 -0
  79. package/dist/genealogy/genealogy-manager.d.ts.map +1 -0
  80. package/dist/genealogy/genealogy-manager.js +64 -0
  81. package/dist/genealogy/genealogy-manager.js.map +1 -0
  82. package/dist/governance/constitution.d.ts +20 -0
  83. package/dist/governance/constitution.d.ts.map +1 -0
  84. package/dist/governance/constitution.js +109 -0
  85. package/dist/governance/constitution.js.map +1 -0
  86. package/dist/host/organism-host-registry.d.ts +24 -0
  87. package/dist/host/organism-host-registry.d.ts.map +1 -0
  88. package/dist/host/organism-host-registry.js +65 -0
  89. package/dist/host/organism-host-registry.js.map +1 -0
  90. package/dist/host/organism-host.d.ts +32 -0
  91. package/dist/host/organism-host.d.ts.map +1 -0
  92. package/dist/host/organism-host.js +64 -0
  93. package/dist/host/organism-host.js.map +1 -0
  94. package/dist/index.d.ts +43 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +117 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/integration/create-ops-organism.d.ts +34 -0
  99. package/dist/integration/create-ops-organism.d.ts.map +1 -0
  100. package/dist/integration/create-ops-organism.js +43 -0
  101. package/dist/integration/create-ops-organism.js.map +1 -0
  102. package/dist/lifecycle/birth-engine.d.ts +41 -0
  103. package/dist/lifecycle/birth-engine.d.ts.map +1 -0
  104. package/dist/lifecycle/birth-engine.js +182 -0
  105. package/dist/lifecycle/birth-engine.js.map +1 -0
  106. package/dist/lifecycle/decision-engine.d.ts +19 -0
  107. package/dist/lifecycle/decision-engine.d.ts.map +1 -0
  108. package/dist/lifecycle/decision-engine.js +137 -0
  109. package/dist/lifecycle/decision-engine.js.map +1 -0
  110. package/dist/lifecycle/reproduction-engine.d.ts +58 -0
  111. package/dist/lifecycle/reproduction-engine.d.ts.map +1 -0
  112. package/dist/lifecycle/reproduction-engine.js +207 -0
  113. package/dist/lifecycle/reproduction-engine.js.map +1 -0
  114. package/dist/lifecycle/survival-engine.d.ts +17 -0
  115. package/dist/lifecycle/survival-engine.d.ts.map +1 -0
  116. package/dist/lifecycle/survival-engine.js +83 -0
  117. package/dist/lifecycle/survival-engine.js.map +1 -0
  118. package/dist/observability/metrics.d.ts +37 -0
  119. package/dist/observability/metrics.d.ts.map +1 -0
  120. package/dist/observability/metrics.js +80 -0
  121. package/dist/observability/metrics.js.map +1 -0
  122. package/dist/perception/perception-engine.d.ts +40 -0
  123. package/dist/perception/perception-engine.d.ts.map +1 -0
  124. package/dist/perception/perception-engine.js +126 -0
  125. package/dist/perception/perception-engine.js.map +1 -0
  126. package/dist/persistence/in-memory-organism-repository.d.ts +2 -0
  127. package/dist/persistence/in-memory-organism-repository.d.ts.map +1 -0
  128. package/dist/persistence/in-memory-organism-repository.js +18 -0
  129. package/dist/persistence/in-memory-organism-repository.js.map +1 -0
  130. package/dist/persistence/organism-repository.d.ts +42 -0
  131. package/dist/persistence/organism-repository.d.ts.map +1 -0
  132. package/dist/persistence/organism-repository.js +105 -0
  133. package/dist/persistence/organism-repository.js.map +1 -0
  134. package/dist/types/organism.types.d.ts +482 -0
  135. package/dist/types/organism.types.d.ts.map +1 -0
  136. package/dist/types/organism.types.js +45 -0
  137. package/dist/types/organism.types.js.map +1 -0
  138. package/examples/ecommerce-organism/run.ts +245 -0
  139. package/package.json +73 -0
  140. package/src/bridge/signal-bridge.ts +116 -0
  141. package/src/core/capability-registry.ts +72 -0
  142. package/src/core/clock.ts +58 -0
  143. package/src/core/mission.ts +83 -0
  144. package/src/core/organism-context.ts +176 -0
  145. package/src/core/organism-runtime.ts +1369 -0
  146. package/src/decorators/index.ts +180 -0
  147. package/src/economy/market-engine.ts +324 -0
  148. package/src/economy/resource-allocator.ts +188 -0
  149. package/src/economy/utility-engine.ts +103 -0
  150. package/src/economy/utility-forecaster.ts +88 -0
  151. package/src/errors/organism.errors.ts +47 -0
  152. package/src/events/organism-events.ts +122 -0
  153. package/src/evolution/evolution-engine.ts +167 -0
  154. package/src/evolution/mutation-engine.ts +110 -0
  155. package/src/genealogy/genealogy-manager.ts +70 -0
  156. package/src/governance/constitution.ts +120 -0
  157. package/src/host/organism-host-registry.ts +72 -0
  158. package/src/host/organism-host.ts +103 -0
  159. package/src/index.ts +78 -0
  160. package/src/integration/create-ops-organism.ts +80 -0
  161. package/src/lifecycle/birth-engine.ts +225 -0
  162. package/src/lifecycle/decision-engine.ts +158 -0
  163. package/src/lifecycle/reproduction-engine.ts +289 -0
  164. package/src/lifecycle/survival-engine.ts +95 -0
  165. package/src/observability/metrics.ts +120 -0
  166. package/src/perception/perception-engine.ts +140 -0
  167. package/src/persistence/in-memory-organism-repository.ts +1 -0
  168. package/src/persistence/organism-repository.ts +134 -0
  169. package/src/types/organism.types.ts +591 -0
  170. package/tsconfig.jest.json +8 -0
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Ecommerce organism simulation demo (Phase 1).
3
+ *
4
+ * Run: npx ts-node --transpile-only examples/ecommerce-organism/run.ts
5
+ * (from packages/organism after build, or via jest-integrated path)
6
+ */
7
+
8
+ import {
9
+ AgentGene,
10
+ Constitution,
11
+ Environment,
12
+ Mission,
13
+ createOrganism,
14
+ resetAgentSeqForTests,
15
+ type EnvironmentSignal,
16
+ } from '../../src';
17
+
18
+ @Mission({
19
+ id: 'ecommerce-ops',
20
+ objective: 'Run a simulated ecommerce store support and operations function',
21
+ successCriteria: [
22
+ { name: 'csat', operator: 'gte', target: 90 },
23
+ { name: 'refund_rate', operator: 'lte', target: 0.05 },
24
+ ],
25
+ constraints: ['Never expose customer PII', 'Remain within operating budget'],
26
+ })
27
+ class EcommerceMission {}
28
+
29
+ @Constitution({
30
+ id: 'commerce-constitution',
31
+ rules: [
32
+ {
33
+ id: 'privacy',
34
+ rule: 'Never expose customer personally identifiable information',
35
+ severity: 'critical',
36
+ },
37
+ {
38
+ id: 'refund-limit',
39
+ rule: 'Refunds above $200 require human approval',
40
+ severity: 'high',
41
+ },
42
+ {
43
+ id: 'budget',
44
+ rule: 'Never exceed allocated operational budget',
45
+ severity: 'critical',
46
+ },
47
+ ],
48
+ })
49
+ class EcommerceConstitution {}
50
+
51
+ @Environment({ id: 'ecommerce-environment' })
52
+ class EcommerceEnvironment {}
53
+
54
+ @AgentGene({
55
+ id: 'commerce-generalist',
56
+ capabilities: ['commerce', 'customer-support', 'operations'],
57
+ initialPrompt: 'You are a commerce generalist agent.',
58
+ reproduction: { enabled: true },
59
+ mutation: { enabled: true },
60
+ })
61
+ class CommerceGene {}
62
+
63
+ @AgentGene({
64
+ id: 'analysis-gene',
65
+ capabilities: ['analytics', 'analysis'],
66
+ initialPrompt: 'You analyze operational metrics.',
67
+ })
68
+ class AnalysisGene {}
69
+
70
+ @AgentGene({
71
+ id: 'research-gene',
72
+ capabilities: ['research'],
73
+ initialPrompt: 'You research product and market issues.',
74
+ })
75
+ class ResearchGene {}
76
+
77
+ @AgentGene({
78
+ id: 'support-gene',
79
+ capabilities: ['customer-support'],
80
+ initialPrompt: 'You handle customer support.',
81
+ })
82
+ class SupportGene {}
83
+
84
+ const SIGNAL_MAPPINGS = [
85
+ {
86
+ signalType: 'refunds.increased',
87
+ need: 'refund-analysis',
88
+ requiredCapabilities: ['analytics', 'commerce'],
89
+ urgency: 0.88,
90
+ confidence: 0.93,
91
+ },
92
+ {
93
+ signalType: 'support.volume_spike',
94
+ need: 'support-surge',
95
+ requiredCapabilities: ['customer-support'],
96
+ urgency: 0.7,
97
+ confidence: 0.85,
98
+ },
99
+ {
100
+ signalType: 'inventory.low',
101
+ need: 'inventory-ops',
102
+ requiredCapabilities: ['commerce', 'operations'],
103
+ urgency: 0.75,
104
+ confidence: 0.8,
105
+ },
106
+ {
107
+ signalType: 'checkout.failure_rate',
108
+ need: 'checkout-recovery',
109
+ requiredCapabilities: ['commerce', 'operations'],
110
+ urgency: 0.9,
111
+ confidence: 0.9,
112
+ },
113
+ ];
114
+
115
+ export async function runEcommerceOrganismDemo(
116
+ opts: {
117
+ log?: (line: string) => void;
118
+ } = {}
119
+ ): Promise<void> {
120
+ resetAgentSeqForTests();
121
+ const log = opts.log ?? ((line: string) => console.log(line));
122
+
123
+ const organism = await createOrganism({
124
+ mission: EcommerceMission,
125
+ constitution: EcommerceConstitution,
126
+ environment: EcommerceEnvironment,
127
+ genes: [CommerceGene, AnalysisGene, ResearchGene, SupportGene],
128
+ resources: {
129
+ tokenBudget: 500_000,
130
+ monthlyBudget: { amount: 500, currency: 'USD' },
131
+ },
132
+ limits: {
133
+ maxAgents: 10,
134
+ maxGenerationDepth: 3,
135
+ maxChildrenPerAgent: 3,
136
+ maxSpawnRatePerMinute: 10,
137
+ maxTotalCostPerHour: 50,
138
+ },
139
+ signalNeedMappings: SIGNAL_MAPPINGS,
140
+ survival: {
141
+ minimumUtility: 0.25,
142
+ minimumEvaluationAgeMs: 1,
143
+ minimumSampleSize: 2,
144
+ cooldownMs: 0,
145
+ },
146
+ debug: true,
147
+ simulation: true,
148
+ });
149
+
150
+ // Route logs
151
+ const originalLog = console.log;
152
+ console.log = (...args: unknown[]) => {
153
+ log(args.map(String).join(' '));
154
+ };
155
+
156
+ try {
157
+ await organism.start();
158
+
159
+ // Day 1 — normal complaint (may spawn support if needed)
160
+ await organism.observe({
161
+ type: 'customer.complaint',
162
+ source: 'support',
163
+ severity: 0.4,
164
+ relevance: 0.5,
165
+ data: { topic: 'shipping delay' },
166
+ });
167
+
168
+ // Day 3 — refund spike → need refund-analysis → birth
169
+ await organism.observe({
170
+ type: 'refunds.increased',
171
+ source: 'analytics',
172
+ severity: 0.9,
173
+ relevance: 0.95,
174
+ data: { baseline: 0.041, current: 0.073 },
175
+ } as Partial<EnvironmentSignal> & Pick<EnvironmentSignal, 'type' | 'source'>);
176
+
177
+ const agents = await organism.listAgents();
178
+ const refundAgent = agents.find((a) =>
179
+ a.capabilities.map((c) => c.toLowerCase()).includes('analytics')
180
+ );
181
+
182
+ if (refundAgent) {
183
+ await organism.reportOutcome(refundAgent.id, {
184
+ result: '68% of returns linked to sizing confusion',
185
+ metrics: { valueGenerated: 250, confidence: 0.91, cost: 2 },
186
+ evidence: ['68% of refund complaints mention incorrect sizing'],
187
+ missionMetricUpdates: { refund_rate: 0.048, csat: 91 },
188
+ });
189
+
190
+ const sizingAgent = await organism.reproduceAgent(refundAgent.id, {
191
+ reason: 'Need sizing research specialization',
192
+ specialization: ['sizing', 'product-research'],
193
+ objective: 'Research sizing guide issues and recommend changes',
194
+ tokens: 25_000,
195
+ });
196
+ log(
197
+ `[REPRODUCTION] ${sizingAgent.name}#${sizingAgent.id} born parent: ${refundAgent.name}#${refundAgent.id} generation: ${sizingAgent.generation}`
198
+ );
199
+
200
+ await organism.reportOutcome(sizingAgent.id, {
201
+ result: 'Size guide experiment proposed',
202
+ metrics: { valueGenerated: 400, confidence: 0.88, cost: 3 },
203
+ missionMetricUpdates: { refund_rate: 0.048, csat: 91 },
204
+ });
205
+
206
+ // After mission improves, low-utility agents are released
207
+ await organism.reportOutcome(refundAgent.id, {
208
+ result: 'No further refund anomalies',
209
+ metrics: { valueGenerated: 0, confidence: 0.4, cost: 5 },
210
+ });
211
+ await organism.reportOutcome(refundAgent.id, {
212
+ result: 'Idle',
213
+ metrics: { valueGenerated: 0, confidence: 0.3, cost: 5 },
214
+ });
215
+ await organism.reportOutcome(sizingAgent.id, {
216
+ result: 'No active work',
217
+ metrics: { valueGenerated: 0, confidence: 0.3, cost: 4 },
218
+ });
219
+ await organism.reportOutcome(sizingAgent.id, {
220
+ result: 'Idle',
221
+ metrics: { valueGenerated: 0, confidence: 0.2, cost: 4 },
222
+ });
223
+
224
+ organism.clock.useAccelerated(Date.now());
225
+ organism.clock.advance(120_000);
226
+ await organism.runSurvivalCycle();
227
+
228
+ const tree = await organism.formatGenealogy();
229
+ log(`[GENEALOGY]\n${tree}`);
230
+ }
231
+
232
+ const state = await organism.inspect();
233
+ log(`[INSPECT] status=${state.status} agents=${state.agents.length}`);
234
+ await organism.terminate();
235
+ } finally {
236
+ console.log = originalLog;
237
+ }
238
+ }
239
+
240
+ if (require.main === module) {
241
+ runEcommerceOrganismDemo().catch((err) => {
242
+ console.error(err);
243
+ process.exitCode = 1;
244
+ });
245
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@hazeljs/organism",
3
+ "version": "2.0.6",
4
+ "description": "Agentic Organism Runtime for HazelJS — mission-defined, self-organizing ephemeral agent societies",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc --build",
9
+ "typecheck": "tsc --noEmit -p tsconfig.json",
10
+ "test": "jest --coverage --passWithNoTests",
11
+ "test:watch": "jest --watch",
12
+ "lint": "eslint src --ext .ts",
13
+ "lint:fix": "eslint src --ext .ts --fix",
14
+ "format": "prettier --write \"src/**/*.ts\"",
15
+ "format:check": "prettier --check \"src/**/*.ts\"",
16
+ "clean": "rm -rf dist"
17
+ },
18
+ "keywords": [
19
+ "hazeljs",
20
+ "organism",
21
+ "agent",
22
+ "multi-agent",
23
+ "agentic",
24
+ "mission",
25
+ "self-organizing",
26
+ "agent-os"
27
+ ],
28
+ "author": "Muhammad Arslan <muhammad.arslan@hazeljs.ai>",
29
+ "license": "Apache-2.0",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/hazel-js/hazeljs",
33
+ "directory": "packages/organism"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "devDependencies": {
39
+ "@hazeljs/agent": "^2.0.6",
40
+ "@hazeljs/core": "^2.0.6",
41
+ "@types/jest": "^29.5.14",
42
+ "@types/node": "^20.19.39",
43
+ "jest": "^29.7.0",
44
+ "prettier": "^3.8.1",
45
+ "ts-jest": "^29.4.9",
46
+ "typescript": "^6.0.3"
47
+ },
48
+ "peerDependencies": {
49
+ "@hazeljs/agent": "^2.0.6",
50
+ "@hazeljs/core": "^2.0.6",
51
+ "@hazeljs/agent-gatekeeper": "^2.0.6",
52
+ "@hazeljs/observability": "^2.0.6",
53
+ "@opentelemetry/api": "^1.9.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "@hazeljs/agent-gatekeeper": {
57
+ "optional": true,
58
+ "description": "Optional tool authorization / human approval gates"
59
+ },
60
+ "@hazeljs/observability": {
61
+ "optional": true,
62
+ "description": "Optional OpenTelemetry integration"
63
+ },
64
+ "@opentelemetry/api": {
65
+ "optional": true,
66
+ "description": "OpenTelemetry API for organism metrics"
67
+ }
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/hazel-js/hazeljs/issues"
71
+ },
72
+ "homepage": "https://hazeljs.ai"
73
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Signal / outcome bridge helpers for embedding organism in product platforms.
3
+ * Keep organism generic — callers map industry payloads into these shapes.
4
+ */
5
+
6
+ import type {
7
+ AgentOutcomeReport,
8
+ EnvironmentSignal,
9
+ SignalNeedMapping,
10
+ } from '../types/organism.types';
11
+
12
+ export type ExternalSignalLike = {
13
+ id?: string;
14
+ type: string;
15
+ source: string;
16
+ timestamp?: Date | string | number;
17
+ severity?: number;
18
+ confidence?: number;
19
+ relevance?: number;
20
+ data?: unknown;
21
+ };
22
+
23
+ /**
24
+ * Normalize an external/product signal into an EnvironmentSignal.
25
+ * Strips tenancy / vertical fields — those belong outside organism.
26
+ */
27
+ export function toEnvironmentSignal(input: ExternalSignalLike): EnvironmentSignal {
28
+ const timestamp =
29
+ input.timestamp instanceof Date
30
+ ? input.timestamp
31
+ : input.timestamp != null
32
+ ? new Date(input.timestamp)
33
+ : new Date();
34
+
35
+ return {
36
+ id: input.id ?? `sig_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
37
+ type: input.type,
38
+ source: input.source,
39
+ timestamp,
40
+ data: input.data ?? {},
41
+ severity: input.severity,
42
+ relevance: input.relevance ?? input.confidence,
43
+ };
44
+ }
45
+
46
+ /** Observe-ready signal when an incident is opened by an outer detection layer. */
47
+ export function toIncidentEnvironmentSignal(input: {
48
+ incidentType: string;
49
+ source?: string;
50
+ severity?: number;
51
+ data?: Record<string, unknown>;
52
+ id?: string;
53
+ }): EnvironmentSignal {
54
+ return toEnvironmentSignal({
55
+ id: input.id,
56
+ type: `incident.${input.incidentType}`,
57
+ source: input.source ?? 'incident-engine',
58
+ severity: input.severity,
59
+ data: input.data ?? {},
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Build SignalNeedMapping entries that route incident.<type> into investigation needs.
65
+ * Domain detectors stay outside organism; this only wires need→capability after detection.
66
+ */
67
+ export function incidentNeedMappings(
68
+ incidentTypes: string[],
69
+ requiredCapabilities: string[],
70
+ opts?: { urgency?: number; confidence?: number }
71
+ ): SignalNeedMapping[] {
72
+ return incidentTypes.map((type) => ({
73
+ signalType: `incident.${type}`,
74
+ need: `investigate-${type}`,
75
+ requiredCapabilities: [...requiredCapabilities],
76
+ urgency: opts?.urgency ?? 0.85,
77
+ confidence: opts?.confidence ?? 0.9,
78
+ }));
79
+ }
80
+
81
+ export type OutcomeVerdictLike =
82
+ | 'successful'
83
+ | 'partially_successful'
84
+ | 'neutral'
85
+ | 'unsuccessful'
86
+ | string;
87
+
88
+ /** Map an outer outcome evaluation into AgentOutcomeReport for utility/reputation. */
89
+ export function toAgentOutcomeReport(input: {
90
+ verdict: OutcomeVerdictLike;
91
+ confidence?: number;
92
+ cost?: number;
93
+ summary?: string;
94
+ missionMetricUpdates?: Record<string, number>;
95
+ }): AgentOutcomeReport {
96
+ const valueGenerated =
97
+ input.verdict === 'successful'
98
+ ? 1
99
+ : input.verdict === 'partially_successful'
100
+ ? 0.5
101
+ : input.verdict === 'neutral'
102
+ ? 0.2
103
+ : 0;
104
+
105
+ return {
106
+ result: input.summary ?? String(input.verdict),
107
+ metrics: {
108
+ valueGenerated,
109
+ confidence: input.confidence ?? 0.8,
110
+ cost: input.cost,
111
+ riskPenalty: input.verdict === 'unsuccessful' ? 0.5 : 0,
112
+ },
113
+ missionMetricUpdates: input.missionMetricUpdates,
114
+ evidence: input.summary ? [input.summary] : undefined,
115
+ };
116
+ }
@@ -0,0 +1,72 @@
1
+ import type { RuntimeAgentRecord } from '../types/organism.types';
2
+
3
+ /**
4
+ * Runtime capability graph for need detection and routing.
5
+ */
6
+ export class CapabilityRegistry {
7
+ private byCapability = new Map<string, Set<string>>();
8
+ private agentCaps = new Map<string, Set<string>>();
9
+
10
+ register(agentId: string, capabilities: string[]): void {
11
+ this.unregister(agentId);
12
+ const set = new Set(capabilities.map((c) => c.toLowerCase()));
13
+ this.agentCaps.set(agentId, set);
14
+ for (const cap of set) {
15
+ let agents = this.byCapability.get(cap);
16
+ if (!agents) {
17
+ agents = new Set();
18
+ this.byCapability.set(cap, agents);
19
+ }
20
+ agents.add(agentId);
21
+ }
22
+ }
23
+
24
+ unregister(agentId: string): void {
25
+ const existing = this.agentCaps.get(agentId);
26
+ if (!existing) return;
27
+ for (const cap of existing) {
28
+ this.byCapability.get(cap)?.delete(agentId);
29
+ }
30
+ this.agentCaps.delete(agentId);
31
+ }
32
+
33
+ find(capability: string): string[] {
34
+ return Array.from(this.byCapability.get(capability.toLowerCase()) ?? []);
35
+ }
36
+
37
+ findGap(required: string[], liveAgents: RuntimeAgentRecord[]): string[] {
38
+ const live = new Set(
39
+ liveAgents
40
+ .filter((a) => a.status === 'active' || a.status === 'idle')
41
+ .flatMap((a) => a.capabilities.map((c) => c.toLowerCase()))
42
+ );
43
+ return required.filter((c) => !live.has(c.toLowerCase()));
44
+ }
45
+
46
+ findCapableAgents(required: string[], liveAgents: RuntimeAgentRecord[]): RuntimeAgentRecord[] {
47
+ const needed = required.map((c) => c.toLowerCase());
48
+ return liveAgents.filter((a) => {
49
+ if (a.status !== 'active' && a.status !== 'idle') return false;
50
+ const caps = new Set(a.capabilities.map((c) => c.toLowerCase()));
51
+ return needed.every((n) => caps.has(n));
52
+ });
53
+ }
54
+
55
+ findPartialAgents(required: string[], liveAgents: RuntimeAgentRecord[]): RuntimeAgentRecord[] {
56
+ const needed = required.map((c) => c.toLowerCase());
57
+ return liveAgents.filter((a) => {
58
+ if (a.status !== 'active' && a.status !== 'idle') return false;
59
+ const caps = new Set(a.capabilities.map((c) => c.toLowerCase()));
60
+ const hits = needed.filter((n) => caps.has(n)).length;
61
+ return hits > 0 && hits < needed.length;
62
+ });
63
+ }
64
+
65
+ snapshot(): Record<string, string[]> {
66
+ const out: Record<string, string[]> = {};
67
+ for (const [cap, agents] of this.byCapability) {
68
+ out[cap] = Array.from(agents);
69
+ }
70
+ return out;
71
+ }
72
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parse duration strings like "5s", "1m", "10m", "30d" into milliseconds.
3
+ */
4
+ export function parseDurationMs(input: string | number | undefined, fallback: number): number {
5
+ if (input == null) return fallback;
6
+ if (typeof input === 'number') return input;
7
+ const m = String(input)
8
+ .trim()
9
+ .match(/^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/i);
10
+ if (!m) return fallback;
11
+ const n = Number(m[1]);
12
+ const unit = (m[2] ?? 'ms').toLowerCase();
13
+ switch (unit) {
14
+ case 'ms':
15
+ return n;
16
+ case 's':
17
+ return n * 1000;
18
+ case 'm':
19
+ return n * 60_000;
20
+ case 'h':
21
+ return n * 3_600_000;
22
+ case 'd':
23
+ return n * 86_400_000;
24
+ default:
25
+ return fallback;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Simulation / test clock. Real mode uses Date; accelerated advances manually.
31
+ */
32
+ export class OrganismClock {
33
+ private accelerated = false;
34
+ private current = Date.now();
35
+
36
+ useReal(): void {
37
+ this.accelerated = false;
38
+ }
39
+
40
+ useAccelerated(start = Date.now()): void {
41
+ this.accelerated = true;
42
+ this.current = start;
43
+ }
44
+
45
+ now(): Date {
46
+ return new Date(this.accelerated ? this.current : Date.now());
47
+ }
48
+
49
+ advance(ms: number): void {
50
+ if (this.accelerated) {
51
+ this.current += ms;
52
+ }
53
+ }
54
+
55
+ get isAccelerated(): boolean {
56
+ return this.accelerated;
57
+ }
58
+ }
@@ -0,0 +1,83 @@
1
+ import type { MissionDefinition, MissionMetric, MissionProgress } from '../types/organism.types';
2
+
3
+ export function createMissionProgress(mission: MissionDefinition): MissionProgress {
4
+ const metrics: Record<string, number> = {};
5
+ const criteriaMet: Record<string, boolean> = {};
6
+ for (const c of mission.successCriteria ?? []) {
7
+ metrics[c.name] = 0;
8
+ criteriaMet[c.name] = false;
9
+ }
10
+ return {
11
+ missionId: mission.id,
12
+ objective: mission.objective,
13
+ metrics,
14
+ criteriaMet,
15
+ completed: false,
16
+ updatedAt: new Date(),
17
+ };
18
+ }
19
+
20
+ export function applyMissionMetricUpdates(
21
+ progress: MissionProgress,
22
+ mission: MissionDefinition,
23
+ updates: Record<string, number>
24
+ ): MissionProgress {
25
+ const metrics = { ...progress.metrics, ...updates };
26
+ const criteriaMet: Record<string, boolean> = {};
27
+ let allMet = true;
28
+ const criteria = mission.successCriteria ?? [];
29
+
30
+ if (criteria.length === 0) {
31
+ return {
32
+ ...progress,
33
+ metrics,
34
+ criteriaMet,
35
+ completed: false,
36
+ updatedAt: new Date(),
37
+ };
38
+ }
39
+
40
+ for (const criterion of criteria) {
41
+ const value = metrics[criterion.name] ?? 0;
42
+ const met = evaluateCriterion(criterion, value);
43
+ criteriaMet[criterion.name] = met;
44
+ if (criterion.operator !== 'maximize' && criterion.operator !== 'minimize' && !met) {
45
+ allMet = false;
46
+ }
47
+ }
48
+
49
+ // maximize/minimize never block completion alone; only target operators do
50
+ const targetCriteria = criteria.filter(
51
+ (c) => c.operator !== 'maximize' && c.operator !== 'minimize'
52
+ );
53
+ const completed = targetCriteria.length > 0 && targetCriteria.every((c) => criteriaMet[c.name]);
54
+
55
+ return {
56
+ ...progress,
57
+ metrics,
58
+ criteriaMet,
59
+ completed: completed && allMet,
60
+ updatedAt: new Date(),
61
+ };
62
+ }
63
+
64
+ function evaluateCriterion(criterion: MissionMetric, value: number): boolean {
65
+ const target = criterion.target;
66
+ switch (criterion.operator) {
67
+ case 'gt':
68
+ return target != null && value > target;
69
+ case 'gte':
70
+ return target != null && value >= target;
71
+ case 'lt':
72
+ return target != null && value < target;
73
+ case 'lte':
74
+ return target != null && value <= target;
75
+ case 'eq':
76
+ return target != null && value === target;
77
+ case 'maximize':
78
+ case 'minimize':
79
+ return true;
80
+ default:
81
+ return false;
82
+ }
83
+ }