@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.
- package/README.md +205 -0
- package/dist/__tests__/organism.embedding.test.d.ts +2 -0
- package/dist/__tests__/organism.embedding.test.d.ts.map +1 -0
- package/dist/__tests__/organism.embedding.test.js +166 -0
- package/dist/__tests__/organism.embedding.test.js.map +1 -0
- package/dist/__tests__/organism.phase1.test.d.ts +2 -0
- package/dist/__tests__/organism.phase1.test.d.ts.map +1 -0
- package/dist/__tests__/organism.phase1.test.js +375 -0
- package/dist/__tests__/organism.phase1.test.js.map +1 -0
- package/dist/__tests__/organism.phase2-3.test.d.ts +2 -0
- package/dist/__tests__/organism.phase2-3.test.d.ts.map +1 -0
- package/dist/__tests__/organism.phase2-3.test.js +320 -0
- package/dist/__tests__/organism.phase2-3.test.js.map +1 -0
- package/dist/__tests__/organism.phase4.test.d.ts +2 -0
- package/dist/__tests__/organism.phase4.test.d.ts.map +1 -0
- package/dist/__tests__/organism.phase4.test.js +217 -0
- package/dist/__tests__/organism.phase4.test.js.map +1 -0
- package/dist/bridge/signal-bridge.d.ts +46 -0
- package/dist/bridge/signal-bridge.d.ts.map +1 -0
- package/dist/bridge/signal-bridge.js +75 -0
- package/dist/bridge/signal-bridge.js.map +1 -0
- package/dist/core/capability-registry.d.ts +16 -0
- package/dist/core/capability-registry.d.ts.map +1 -0
- package/dist/core/capability-registry.js +71 -0
- package/dist/core/capability-registry.js.map +1 -0
- package/dist/core/clock.d.ts +17 -0
- package/dist/core/clock.d.ts.map +1 -0
- package/dist/core/clock.js +63 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/mission.d.ts +4 -0
- package/dist/core/mission.d.ts.map +1 -0
- package/dist/core/mission.js +74 -0
- package/dist/core/mission.js.map +1 -0
- package/dist/core/organism-context.d.ts +139 -0
- package/dist/core/organism-context.d.ts.map +1 -0
- package/dist/core/organism-context.js +62 -0
- package/dist/core/organism-context.js.map +1 -0
- package/dist/core/organism-runtime.d.ts +199 -0
- package/dist/core/organism-runtime.d.ts.map +1 -0
- package/dist/core/organism-runtime.js +1016 -0
- package/dist/core/organism-runtime.js.map +1 -0
- package/dist/decorators/index.d.ts +39 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +165 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/economy/market-engine.d.ts +57 -0
- package/dist/economy/market-engine.d.ts.map +1 -0
- package/dist/economy/market-engine.js +258 -0
- package/dist/economy/market-engine.js.map +1 -0
- package/dist/economy/resource-allocator.d.ts +39 -0
- package/dist/economy/resource-allocator.d.ts.map +1 -0
- package/dist/economy/resource-allocator.js +153 -0
- package/dist/economy/resource-allocator.js.map +1 -0
- package/dist/economy/utility-engine.d.ts +16 -0
- package/dist/economy/utility-engine.d.ts.map +1 -0
- package/dist/economy/utility-engine.js +80 -0
- package/dist/economy/utility-engine.js.map +1 -0
- package/dist/economy/utility-forecaster.d.ts +24 -0
- package/dist/economy/utility-forecaster.d.ts.map +1 -0
- package/dist/economy/utility-forecaster.js +60 -0
- package/dist/economy/utility-forecaster.js.map +1 -0
- package/dist/errors/organism.errors.d.ts +23 -0
- package/dist/errors/organism.errors.d.ts.map +1 -0
- package/dist/errors/organism.errors.js +50 -0
- package/dist/errors/organism.errors.js.map +1 -0
- package/dist/events/organism-events.d.ts +61 -0
- package/dist/events/organism-events.d.ts.map +1 -0
- package/dist/events/organism-events.js +95 -0
- package/dist/events/organism-events.js.map +1 -0
- package/dist/evolution/evolution-engine.d.ts +41 -0
- package/dist/evolution/evolution-engine.d.ts.map +1 -0
- package/dist/evolution/evolution-engine.js +135 -0
- package/dist/evolution/evolution-engine.js.map +1 -0
- package/dist/evolution/mutation-engine.d.ts +21 -0
- package/dist/evolution/mutation-engine.d.ts.map +1 -0
- package/dist/evolution/mutation-engine.js +84 -0
- package/dist/evolution/mutation-engine.js.map +1 -0
- package/dist/genealogy/genealogy-manager.d.ts +18 -0
- package/dist/genealogy/genealogy-manager.d.ts.map +1 -0
- package/dist/genealogy/genealogy-manager.js +64 -0
- package/dist/genealogy/genealogy-manager.js.map +1 -0
- package/dist/governance/constitution.d.ts +20 -0
- package/dist/governance/constitution.d.ts.map +1 -0
- package/dist/governance/constitution.js +109 -0
- package/dist/governance/constitution.js.map +1 -0
- package/dist/host/organism-host-registry.d.ts +24 -0
- package/dist/host/organism-host-registry.d.ts.map +1 -0
- package/dist/host/organism-host-registry.js +65 -0
- package/dist/host/organism-host-registry.js.map +1 -0
- package/dist/host/organism-host.d.ts +32 -0
- package/dist/host/organism-host.d.ts.map +1 -0
- package/dist/host/organism-host.js +64 -0
- package/dist/host/organism-host.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +117 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/create-ops-organism.d.ts +34 -0
- package/dist/integration/create-ops-organism.d.ts.map +1 -0
- package/dist/integration/create-ops-organism.js +43 -0
- package/dist/integration/create-ops-organism.js.map +1 -0
- package/dist/lifecycle/birth-engine.d.ts +41 -0
- package/dist/lifecycle/birth-engine.d.ts.map +1 -0
- package/dist/lifecycle/birth-engine.js +182 -0
- package/dist/lifecycle/birth-engine.js.map +1 -0
- package/dist/lifecycle/decision-engine.d.ts +19 -0
- package/dist/lifecycle/decision-engine.d.ts.map +1 -0
- package/dist/lifecycle/decision-engine.js +137 -0
- package/dist/lifecycle/decision-engine.js.map +1 -0
- package/dist/lifecycle/reproduction-engine.d.ts +58 -0
- package/dist/lifecycle/reproduction-engine.d.ts.map +1 -0
- package/dist/lifecycle/reproduction-engine.js +207 -0
- package/dist/lifecycle/reproduction-engine.js.map +1 -0
- package/dist/lifecycle/survival-engine.d.ts +17 -0
- package/dist/lifecycle/survival-engine.d.ts.map +1 -0
- package/dist/lifecycle/survival-engine.js +83 -0
- package/dist/lifecycle/survival-engine.js.map +1 -0
- package/dist/observability/metrics.d.ts +37 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +80 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/perception/perception-engine.d.ts +40 -0
- package/dist/perception/perception-engine.d.ts.map +1 -0
- package/dist/perception/perception-engine.js +126 -0
- package/dist/perception/perception-engine.js.map +1 -0
- package/dist/persistence/in-memory-organism-repository.d.ts +2 -0
- package/dist/persistence/in-memory-organism-repository.d.ts.map +1 -0
- package/dist/persistence/in-memory-organism-repository.js +18 -0
- package/dist/persistence/in-memory-organism-repository.js.map +1 -0
- package/dist/persistence/organism-repository.d.ts +42 -0
- package/dist/persistence/organism-repository.d.ts.map +1 -0
- package/dist/persistence/organism-repository.js +105 -0
- package/dist/persistence/organism-repository.js.map +1 -0
- package/dist/types/organism.types.d.ts +482 -0
- package/dist/types/organism.types.d.ts.map +1 -0
- package/dist/types/organism.types.js +45 -0
- package/dist/types/organism.types.js.map +1 -0
- package/examples/ecommerce-organism/run.ts +245 -0
- package/package.json +73 -0
- package/src/bridge/signal-bridge.ts +116 -0
- package/src/core/capability-registry.ts +72 -0
- package/src/core/clock.ts +58 -0
- package/src/core/mission.ts +83 -0
- package/src/core/organism-context.ts +176 -0
- package/src/core/organism-runtime.ts +1369 -0
- package/src/decorators/index.ts +180 -0
- package/src/economy/market-engine.ts +324 -0
- package/src/economy/resource-allocator.ts +188 -0
- package/src/economy/utility-engine.ts +103 -0
- package/src/economy/utility-forecaster.ts +88 -0
- package/src/errors/organism.errors.ts +47 -0
- package/src/events/organism-events.ts +122 -0
- package/src/evolution/evolution-engine.ts +167 -0
- package/src/evolution/mutation-engine.ts +110 -0
- package/src/genealogy/genealogy-manager.ts +70 -0
- package/src/governance/constitution.ts +120 -0
- package/src/host/organism-host-registry.ts +72 -0
- package/src/host/organism-host.ts +103 -0
- package/src/index.ts +78 -0
- package/src/integration/create-ops-organism.ts +80 -0
- package/src/lifecycle/birth-engine.ts +225 -0
- package/src/lifecycle/decision-engine.ts +158 -0
- package/src/lifecycle/reproduction-engine.ts +289 -0
- package/src/lifecycle/survival-engine.ts +95 -0
- package/src/observability/metrics.ts +120 -0
- package/src/perception/perception-engine.ts +140 -0
- package/src/persistence/in-memory-organism-repository.ts +1 -0
- package/src/persistence/organism-repository.ts +134 -0
- package/src/types/organism.types.ts +591 -0
- package/tsconfig.jest.json +8 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentOutcomeReport,
|
|
3
|
+
AgentReputation,
|
|
4
|
+
RuntimeAgentRecord,
|
|
5
|
+
UtilityScore,
|
|
6
|
+
UtilityWeights,
|
|
7
|
+
} from '../types/organism.types';
|
|
8
|
+
import { DEFAULT_UTILITY_WEIGHTS as WEIGHTS } from '../types/organism.types';
|
|
9
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
10
|
+
|
|
11
|
+
export class UtilityEngine {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly events: OrganismEventEmitter,
|
|
14
|
+
private readonly organismId: string,
|
|
15
|
+
private readonly weights: UtilityWeights = WEIGHTS
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
evaluate(agent: RuntimeAgentRecord, report?: AgentOutcomeReport): UtilityScore {
|
|
19
|
+
const valueGenerated = report?.metrics?.valueGenerated ?? agent.valueGenerated;
|
|
20
|
+
const cost = report?.metrics?.cost ?? agent.costConsumed;
|
|
21
|
+
const riskPenalty = report?.metrics?.riskPenalty ?? 0;
|
|
22
|
+
const confidence = report?.metrics?.confidence ?? agent.utility.confidence;
|
|
23
|
+
|
|
24
|
+
const missionContribution = clamp(
|
|
25
|
+
valueGenerated / Math.max(1, valueGenerated + cost * 10),
|
|
26
|
+
0,
|
|
27
|
+
1
|
|
28
|
+
);
|
|
29
|
+
const efficiency = cost <= 0 ? 1 : clamp(valueGenerated / (valueGenerated + cost * 50), 0, 1);
|
|
30
|
+
const reliability = clamp(agent.reputation.dimensions.reliability, 0, 1);
|
|
31
|
+
const policyCompliance = clamp(agent.reputation.dimensions.policyCompliance, 0, 1);
|
|
32
|
+
const collaboration = clamp(agent.reputation.dimensions.collaboration, 0, 1);
|
|
33
|
+
|
|
34
|
+
const score =
|
|
35
|
+
missionContribution * this.weights.missionContribution +
|
|
36
|
+
reliability * this.weights.reliability +
|
|
37
|
+
efficiency * this.weights.efficiency +
|
|
38
|
+
policyCompliance * this.weights.policyCompliance +
|
|
39
|
+
collaboration * this.weights.collaboration -
|
|
40
|
+
riskPenalty;
|
|
41
|
+
|
|
42
|
+
const utility: UtilityScore = {
|
|
43
|
+
score: clamp(score, 0, 1),
|
|
44
|
+
valueGenerated,
|
|
45
|
+
cost,
|
|
46
|
+
riskPenalty,
|
|
47
|
+
confidence: clamp(confidence, 0, 1),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
void this.events.emit(OrganismEventType.ORGANISM_UTILITY_CALCULATED, this.organismId, {
|
|
51
|
+
agentId: agent.id,
|
|
52
|
+
utility,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return utility;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class ReputationEngine {
|
|
60
|
+
constructor(
|
|
61
|
+
private readonly events: OrganismEventEmitter,
|
|
62
|
+
private readonly organismId: string
|
|
63
|
+
) {}
|
|
64
|
+
|
|
65
|
+
update(agent: RuntimeAgentRecord, report: AgentOutcomeReport): AgentReputation {
|
|
66
|
+
const value = report.metrics?.valueGenerated ?? 0;
|
|
67
|
+
const confidence = report.metrics?.confidence ?? 0.5;
|
|
68
|
+
const cost = report.metrics?.cost ?? 0;
|
|
69
|
+
|
|
70
|
+
const usefulnessDelta = clamp(value / 1000, -0.1, 0.15);
|
|
71
|
+
const efficiencyDelta = cost > value * 0.1 ? -0.05 : 0.03;
|
|
72
|
+
const reliabilityDelta = confidence >= 0.7 ? 0.04 : -0.03;
|
|
73
|
+
|
|
74
|
+
const dims = { ...agent.reputation.dimensions };
|
|
75
|
+
dims.usefulness = clamp(dims.usefulness + usefulnessDelta, 0, 1);
|
|
76
|
+
dims.efficiency = clamp(dims.efficiency + efficiencyDelta, 0, 1);
|
|
77
|
+
dims.reliability = clamp(dims.reliability + reliabilityDelta, 0, 1);
|
|
78
|
+
|
|
79
|
+
const score =
|
|
80
|
+
dims.usefulness * 0.3 +
|
|
81
|
+
dims.reliability * 0.25 +
|
|
82
|
+
dims.efficiency * 0.2 +
|
|
83
|
+
dims.policyCompliance * 0.15 +
|
|
84
|
+
dims.collaboration * 0.1;
|
|
85
|
+
|
|
86
|
+
const reputation: AgentReputation = {
|
|
87
|
+
agentId: agent.id,
|
|
88
|
+
score: clamp(score, 0, 1),
|
|
89
|
+
dimensions: dims,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
void this.events.emit(OrganismEventType.ORGANISM_REPUTATION_CHANGED, this.organismId, {
|
|
93
|
+
agentId: agent.id,
|
|
94
|
+
reputation,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return reputation;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function clamp(n: number, min: number, max: number): number {
|
|
102
|
+
return Math.max(min, Math.min(max, n));
|
|
103
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4 — utility forecasting and opportunity-cost estimates.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
OpportunityCostEstimate,
|
|
7
|
+
OrganismRecord,
|
|
8
|
+
ResourceRequest,
|
|
9
|
+
RuntimeAgentRecord,
|
|
10
|
+
UtilityForecast,
|
|
11
|
+
} from '../types/organism.types';
|
|
12
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
13
|
+
|
|
14
|
+
export class UtilityForecaster {
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly events: OrganismEventEmitter,
|
|
17
|
+
private readonly organismId: string
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Deterministic forecast: expected value × confidence − opportunity cost,
|
|
22
|
+
* adjusted by reputation and scarcity.
|
|
23
|
+
*/
|
|
24
|
+
forecast(input: {
|
|
25
|
+
agent: RuntimeAgentRecord;
|
|
26
|
+
record: OrganismRecord;
|
|
27
|
+
requested: ResourceRequest;
|
|
28
|
+
expectedValue: number;
|
|
29
|
+
confidence: number;
|
|
30
|
+
urgency?: number;
|
|
31
|
+
}): UtilityForecast {
|
|
32
|
+
const { agent, record, requested, expectedValue, confidence } = input;
|
|
33
|
+
const urgency = input.urgency ?? 0.5;
|
|
34
|
+
const opportunity = this.opportunityCost(record, requested);
|
|
35
|
+
const reputationBoost = 0.5 + agent.reputation.score * 0.5;
|
|
36
|
+
const adjustedValue = expectedValue * clamp(confidence, 0, 1) * reputationBoost;
|
|
37
|
+
const estimatedCost =
|
|
38
|
+
(requested.tokens ?? 0) * 0.00001 + (requested.money ?? 0) + opportunity.total * 0.1;
|
|
39
|
+
const netExpectedValue = adjustedValue - opportunity.total - estimatedCost;
|
|
40
|
+
const expectedUtility = clamp(
|
|
41
|
+
(netExpectedValue / Math.max(1, expectedValue + opportunity.total)) * (0.5 + urgency * 0.5),
|
|
42
|
+
0,
|
|
43
|
+
1
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const forecast: UtilityForecast = {
|
|
47
|
+
agentId: agent.id,
|
|
48
|
+
expectedUtility,
|
|
49
|
+
expectedValue,
|
|
50
|
+
estimatedCost,
|
|
51
|
+
opportunityCost: opportunity.total,
|
|
52
|
+
netExpectedValue,
|
|
53
|
+
confidence: clamp(confidence, 0, 1),
|
|
54
|
+
reasoningSummary: `net=${netExpectedValue.toFixed(2)} (value=${adjustedValue.toFixed(2)} - opp=${opportunity.total.toFixed(2)} - cost=${estimatedCost.toFixed(2)}); scarcity=${opportunity.scarcityMultiplier.toFixed(2)}`,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
void this.events.emit(OrganismEventType.ORGANISM_UTILITY_FORECAST, this.organismId, forecast);
|
|
58
|
+
return forecast;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
opportunityCost(record: OrganismRecord, requested: ResourceRequest): OpportunityCostEstimate {
|
|
62
|
+
const tokenBudget = Math.max(
|
|
63
|
+
1,
|
|
64
|
+
(record.resources.tokenBudget ?? record.pool.tokensRemaining) || 1
|
|
65
|
+
);
|
|
66
|
+
const moneyBudget = Math.max(
|
|
67
|
+
1,
|
|
68
|
+
(record.resources.monthlyBudget?.amount ?? record.pool.moneyRemaining.amount) || 1
|
|
69
|
+
);
|
|
70
|
+
const tokenUtil = 1 - record.pool.tokensRemaining / tokenBudget;
|
|
71
|
+
const moneyUtil = 1 - record.pool.moneyRemaining.amount / moneyBudget;
|
|
72
|
+
const utilization = Math.max(tokenUtil, moneyUtil);
|
|
73
|
+
const scarcityMultiplier = utilization < 0.7 ? 1 : 1 + (utilization - 0.7) * 5;
|
|
74
|
+
|
|
75
|
+
const tokens = (requested.tokens ?? 0) * 0.00002 * scarcityMultiplier;
|
|
76
|
+
const money = (requested.money ?? 0) * scarcityMultiplier;
|
|
77
|
+
return {
|
|
78
|
+
tokens,
|
|
79
|
+
money,
|
|
80
|
+
scarcityMultiplier,
|
|
81
|
+
total: tokens + money,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function clamp(n: number, min: number, max: number): number {
|
|
87
|
+
return Math.max(min, Math.min(max, n));
|
|
88
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hazeljs/organism errors
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class OrganismError extends Error {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
constructor(message: string, code = 'ORGANISM_ERROR') {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'OrganismError';
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class OrganismLimitError extends OrganismError {
|
|
15
|
+
constructor(message: string) {
|
|
16
|
+
super(message, 'ORGANISM_LIMIT');
|
|
17
|
+
this.name = 'OrganismLimitError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class OrganismResourceDeniedError extends OrganismError {
|
|
22
|
+
constructor(message: string) {
|
|
23
|
+
super(message, 'ORGANISM_RESOURCE_DENIED');
|
|
24
|
+
this.name = 'OrganismResourceDeniedError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class OrganismConstitutionError extends OrganismError {
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(message, 'ORGANISM_CONSTITUTION');
|
|
31
|
+
this.name = 'OrganismConstitutionError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class OrganismStateError extends OrganismError {
|
|
36
|
+
constructor(message: string) {
|
|
37
|
+
super(message, 'ORGANISM_STATE');
|
|
38
|
+
this.name = 'OrganismStateError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class OrganismNotFoundError extends OrganismError {
|
|
43
|
+
constructor(message: string) {
|
|
44
|
+
super(message, 'ORGANISM_NOT_FOUND');
|
|
45
|
+
this.name = 'OrganismNotFoundError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Organism domain events (namespaced; separate from AgentEventType).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export enum OrganismEventType {
|
|
6
|
+
ORGANISM_CREATED = 'organism.created',
|
|
7
|
+
ORGANISM_RESTORED = 'organism.restored',
|
|
8
|
+
ORGANISM_STARTED = 'organism.started',
|
|
9
|
+
ORGANISM_PAUSED = 'organism.paused',
|
|
10
|
+
ORGANISM_RESUMED = 'organism.resumed',
|
|
11
|
+
ORGANISM_COMPLETED = 'organism.completed',
|
|
12
|
+
ORGANISM_TERMINATED = 'organism.terminated',
|
|
13
|
+
ORGANISM_EMERGENCY_STOP = 'organism.emergency_stop',
|
|
14
|
+
|
|
15
|
+
ENVIRONMENT_SIGNAL_RECEIVED = 'environment.signal.received',
|
|
16
|
+
ORGANISM_NEED_DETECTED = 'organism.need.detected',
|
|
17
|
+
|
|
18
|
+
ORGANISM_AGENT_BORN = 'organism.agent.born',
|
|
19
|
+
ORGANISM_AGENT_SPECIALIZED = 'organism.agent.specialized',
|
|
20
|
+
ORGANISM_AGENT_REPRODUCED = 'organism.agent.reproduced',
|
|
21
|
+
ORGANISM_AGENT_MUTATED = 'organism.agent.mutated',
|
|
22
|
+
ORGANISM_AGENT_SUSPENDED = 'organism.agent.suspended',
|
|
23
|
+
ORGANISM_AGENT_TERMINATED = 'organism.agent.terminated',
|
|
24
|
+
|
|
25
|
+
ORGANISM_RESOURCE_ALLOCATED = 'organism.resource.allocated',
|
|
26
|
+
ORGANISM_RESOURCE_DENIED = 'organism.resource.denied',
|
|
27
|
+
ORGANISM_RESOURCE_BID = 'organism.resource.bid',
|
|
28
|
+
ORGANISM_MARKET_CLEARED = 'organism.market.cleared',
|
|
29
|
+
ORGANISM_NEGOTIATION = 'organism.negotiation',
|
|
30
|
+
ORGANISM_UTILITY_FORECAST = 'organism.utility.forecast',
|
|
31
|
+
|
|
32
|
+
ORGANISM_REPUTATION_CHANGED = 'organism.reputation.changed',
|
|
33
|
+
ORGANISM_UTILITY_CALCULATED = 'organism.utility.calculated',
|
|
34
|
+
|
|
35
|
+
ORGANISM_GENERATION_CREATED = 'organism.generation.created',
|
|
36
|
+
ORGANISM_STRATEGY_PROMOTED = 'organism.strategy.promoted',
|
|
37
|
+
|
|
38
|
+
ORGANISM_CONSTITUTION_VIOLATION = 'organism.constitution.violation',
|
|
39
|
+
ORGANISM_MISSION_PROGRESS = 'organism.mission.progress',
|
|
40
|
+
ORGANISM_MISSION_COMPLETED = 'organism.mission.completed',
|
|
41
|
+
|
|
42
|
+
ORGANISM_DECISION = 'organism.decision',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface OrganismEvent<T = unknown> {
|
|
46
|
+
type: OrganismEventType;
|
|
47
|
+
organismId: string;
|
|
48
|
+
timestamp: Date;
|
|
49
|
+
data: T;
|
|
50
|
+
metadata?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type EventHandler<T = unknown> = (event: OrganismEvent<T>) => void | Promise<void>;
|
|
54
|
+
|
|
55
|
+
export class OrganismEventEmitter {
|
|
56
|
+
private handlers = new Map<OrganismEventType, Set<EventHandler>>();
|
|
57
|
+
private wildcardHandlers = new Set<EventHandler>();
|
|
58
|
+
private history: OrganismEvent[] = [];
|
|
59
|
+
private maxHistory: number;
|
|
60
|
+
|
|
61
|
+
constructor(options: { maxHistory?: number } = {}) {
|
|
62
|
+
this.maxHistory = options.maxHistory ?? 500;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
on<T = unknown>(type: OrganismEventType, handler: EventHandler<T>): void {
|
|
66
|
+
if (!this.handlers.has(type)) {
|
|
67
|
+
this.handlers.set(type, new Set());
|
|
68
|
+
}
|
|
69
|
+
this.handlers.get(type)!.add(handler as EventHandler);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
onAny(handler: EventHandler): void {
|
|
73
|
+
this.wildcardHandlers.add(handler);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
off(type: OrganismEventType, handler: EventHandler): void {
|
|
77
|
+
this.handlers.get(type)?.delete(handler);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
offAny(handler: EventHandler): void {
|
|
81
|
+
this.wildcardHandlers.delete(handler);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async emit<T = unknown>(
|
|
85
|
+
type: OrganismEventType,
|
|
86
|
+
organismId: string,
|
|
87
|
+
data: T,
|
|
88
|
+
metadata?: Record<string, unknown>
|
|
89
|
+
): Promise<OrganismEvent<T>> {
|
|
90
|
+
const event: OrganismEvent<T> = {
|
|
91
|
+
type,
|
|
92
|
+
organismId,
|
|
93
|
+
timestamp: new Date(),
|
|
94
|
+
data,
|
|
95
|
+
metadata,
|
|
96
|
+
};
|
|
97
|
+
this.history.push(event as OrganismEvent);
|
|
98
|
+
if (this.history.length > this.maxHistory) {
|
|
99
|
+
this.history.splice(0, this.history.length - this.maxHistory);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const typed = this.handlers.get(type);
|
|
103
|
+
if (typed) {
|
|
104
|
+
for (const handler of typed) {
|
|
105
|
+
await handler(event);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const handler of this.wildcardHandlers) {
|
|
109
|
+
await handler(event as OrganismEvent);
|
|
110
|
+
}
|
|
111
|
+
return event;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getHistory(limit?: number): OrganismEvent[] {
|
|
115
|
+
if (limit == null) return [...this.history];
|
|
116
|
+
return this.history.slice(-limit);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
clearHistory(): void {
|
|
120
|
+
this.history = [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 — generation evaluation and strategy promotion.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
EvolutionaryHistoryEntry,
|
|
7
|
+
GenerationEvaluationResult,
|
|
8
|
+
GenerationMemberScore,
|
|
9
|
+
RuntimeAgentRecord,
|
|
10
|
+
UtilityWeights,
|
|
11
|
+
} from '../types/organism.types';
|
|
12
|
+
import { DEFAULT_UTILITY_WEIGHTS } from '../types/organism.types';
|
|
13
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
14
|
+
import type { OrganismRepository } from '../persistence/organism-repository';
|
|
15
|
+
import { MutationEngine, type MutationRequest } from './mutation-engine';
|
|
16
|
+
import type { AgentGeneDefinition } from '../types/organism.types';
|
|
17
|
+
|
|
18
|
+
export class GenerationManager {
|
|
19
|
+
private history: EvolutionaryHistoryEntry[] = [];
|
|
20
|
+
private populations = new Map<string, string[]>();
|
|
21
|
+
|
|
22
|
+
registerPopulation(populationId: string, agentIds: string[]): void {
|
|
23
|
+
this.populations.set(populationId, [...agentIds]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getPopulation(populationId: string): string[] {
|
|
27
|
+
return [...(this.populations.get(populationId) ?? [])];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
record(entry: EvolutionaryHistoryEntry): void {
|
|
31
|
+
this.history.push(entry);
|
|
32
|
+
if (this.history.length > 200) {
|
|
33
|
+
this.history.splice(0, this.history.length - 200);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getHistory(limit = 50): EvolutionaryHistoryEntry[] {
|
|
38
|
+
return this.history.slice(-limit);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class EvolutionEngine {
|
|
43
|
+
constructor(
|
|
44
|
+
private readonly events: OrganismEventEmitter,
|
|
45
|
+
private readonly repo: OrganismRepository,
|
|
46
|
+
private readonly generations: GenerationManager,
|
|
47
|
+
private readonly mutationEngine: MutationEngine,
|
|
48
|
+
private readonly organismId: string,
|
|
49
|
+
private readonly weights: UtilityWeights = DEFAULT_UTILITY_WEIGHTS,
|
|
50
|
+
private readonly now: () => Date = () => new Date()
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
scoreAgent(agent: RuntimeAgentRecord): number {
|
|
54
|
+
const missionContribution = clamp(agent.utility.score, 0, 1);
|
|
55
|
+
const reliability = agent.reputation.dimensions.reliability;
|
|
56
|
+
const efficiency = agent.reputation.dimensions.efficiency;
|
|
57
|
+
const policyCompliance = agent.reputation.dimensions.policyCompliance;
|
|
58
|
+
const collaboration = agent.reputation.dimensions.collaboration;
|
|
59
|
+
return (
|
|
60
|
+
missionContribution * this.weights.missionContribution +
|
|
61
|
+
reliability * this.weights.reliability +
|
|
62
|
+
efficiency * this.weights.efficiency +
|
|
63
|
+
policyCompliance * this.weights.policyCompliance +
|
|
64
|
+
collaboration * this.weights.collaboration
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async evaluateGeneration(input: {
|
|
69
|
+
population: string[];
|
|
70
|
+
populationId?: string;
|
|
71
|
+
}): Promise<GenerationEvaluationResult> {
|
|
72
|
+
const populationId = input.populationId ?? `pop_${Date.now().toString(36)}`;
|
|
73
|
+
this.generations.registerPopulation(populationId, input.population);
|
|
74
|
+
|
|
75
|
+
const members: GenerationMemberScore[] = [];
|
|
76
|
+
const scores: Record<string, number> = {};
|
|
77
|
+
|
|
78
|
+
for (const agentId of input.population) {
|
|
79
|
+
const agent = await this.repo.getAgent(this.organismId, agentId);
|
|
80
|
+
if (!agent || agent.status === 'terminated') continue;
|
|
81
|
+
const score = this.scoreAgent(agent);
|
|
82
|
+
scores[agentId] = score;
|
|
83
|
+
members.push({
|
|
84
|
+
agentId,
|
|
85
|
+
score,
|
|
86
|
+
utility: agent.utility.score,
|
|
87
|
+
reputation: agent.reputation.score,
|
|
88
|
+
cost: agent.costConsumed,
|
|
89
|
+
policyCompliance: agent.reputation.dimensions.policyCompliance,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!members.length) {
|
|
94
|
+
return {
|
|
95
|
+
populationId,
|
|
96
|
+
winner: input.population[0] ?? '',
|
|
97
|
+
scores,
|
|
98
|
+
members,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
members.sort((a, b) => b.score - a.score);
|
|
103
|
+
const winner = members[0].agentId;
|
|
104
|
+
const winnerAgent = await this.repo.getAgent(this.organismId, winner);
|
|
105
|
+
const promotedStrategyId = winnerAgent?.strategyId;
|
|
106
|
+
|
|
107
|
+
const result: GenerationEvaluationResult = {
|
|
108
|
+
populationId,
|
|
109
|
+
winner,
|
|
110
|
+
scores,
|
|
111
|
+
members,
|
|
112
|
+
promotedStrategyId,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this.generations.record({
|
|
116
|
+
at: this.now(),
|
|
117
|
+
populationId,
|
|
118
|
+
winnerAgentId: winner,
|
|
119
|
+
scores: { ...scores },
|
|
120
|
+
promotedStrategyId,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await this.events.emit(OrganismEventType.ORGANISM_GENERATION_CREATED, this.organismId, {
|
|
124
|
+
populationId,
|
|
125
|
+
population: input.population,
|
|
126
|
+
});
|
|
127
|
+
await this.events.emit(OrganismEventType.ORGANISM_STRATEGY_PROMOTED, this.organismId, {
|
|
128
|
+
winner,
|
|
129
|
+
promotedStrategyId,
|
|
130
|
+
scores,
|
|
131
|
+
populationId,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Promote winner strategy into a loser via controlled mutation (safe config only).
|
|
139
|
+
*/
|
|
140
|
+
async promoteStrategy(input: {
|
|
141
|
+
winnerId: string;
|
|
142
|
+
targetId: string;
|
|
143
|
+
gene: AgentGeneDefinition;
|
|
144
|
+
reason?: string;
|
|
145
|
+
}): Promise<RuntimeAgentRecord> {
|
|
146
|
+
const winner = await this.repo.getAgent(this.organismId, input.winnerId);
|
|
147
|
+
const target = await this.repo.getAgent(this.organismId, input.targetId);
|
|
148
|
+
if (!winner || !target) {
|
|
149
|
+
throw new Error('Winner or target agent not found');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const request: MutationRequest = {
|
|
153
|
+
reason: input.reason ?? `Promote strategy from ${winner.id}`,
|
|
154
|
+
mutation: {
|
|
155
|
+
strategyConfig: { ...winner.strategyConfig, promotedFrom: winner.id },
|
|
156
|
+
modelConfig: { ...winner.modelConfig },
|
|
157
|
+
promptChanges: [`Adopt winning strategy patterns from ${winner.name}#${winner.id}`],
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return this.mutationEngine.mutate(target, request, input.gene);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function clamp(n: number, min: number, max: number): number {
|
|
166
|
+
return Math.max(min, Math.min(max, n));
|
|
167
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 — constrained, auditable mutation (no arbitrary source rewrite).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AgentGeneDefinition,
|
|
7
|
+
AgentMutation,
|
|
8
|
+
AgentMutationRecord,
|
|
9
|
+
RuntimeAgentRecord,
|
|
10
|
+
} from '../types/organism.types';
|
|
11
|
+
import { OrganismLimitError, OrganismStateError } from '../errors/organism.errors';
|
|
12
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
13
|
+
import type { OrganismRepository } from '../persistence/organism-repository';
|
|
14
|
+
|
|
15
|
+
export interface MutationRequest {
|
|
16
|
+
reason: string;
|
|
17
|
+
mutation: AgentMutation;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEFAULT_ALLOWED = [
|
|
21
|
+
'promptChanges',
|
|
22
|
+
'addedCapabilities',
|
|
23
|
+
'removedCapabilities',
|
|
24
|
+
'modelConfig',
|
|
25
|
+
'strategyConfig',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export class MutationEngine {
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly events: OrganismEventEmitter,
|
|
31
|
+
private readonly repo: OrganismRepository,
|
|
32
|
+
private readonly organismId: string,
|
|
33
|
+
private readonly now: () => Date = () => new Date()
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
assertAllowed(gene: AgentGeneDefinition, mutation: AgentMutation): void {
|
|
37
|
+
if (gene.mutation && gene.mutation.enabled === false) {
|
|
38
|
+
throw new OrganismLimitError(`Gene ${gene.id} has mutation disabled`);
|
|
39
|
+
}
|
|
40
|
+
const allowed = new Set(gene.mutation?.allowedProperties ?? DEFAULT_ALLOWED);
|
|
41
|
+
for (const key of Object.keys(mutation) as Array<keyof AgentMutation>) {
|
|
42
|
+
if (mutation[key] !== undefined && !allowed.has(key)) {
|
|
43
|
+
throw new OrganismLimitError(
|
|
44
|
+
`Mutation property "${key}" is not allowed for gene ${gene.id}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
apply(
|
|
51
|
+
agent: RuntimeAgentRecord,
|
|
52
|
+
request: MutationRequest,
|
|
53
|
+
gene: AgentGeneDefinition
|
|
54
|
+
): RuntimeAgentRecord {
|
|
55
|
+
if (agent.status === 'terminated' || agent.status === 'failed') {
|
|
56
|
+
throw new OrganismStateError(`Cannot mutate agent ${agent.id} in status ${agent.status}`);
|
|
57
|
+
}
|
|
58
|
+
this.assertAllowed(gene, request.mutation);
|
|
59
|
+
|
|
60
|
+
const m = request.mutation;
|
|
61
|
+
if (m.promptChanges?.length) {
|
|
62
|
+
const appendix = m.promptChanges.map((c) => `- ${c}`).join('\n');
|
|
63
|
+
agent.systemPrompt = `${agent.systemPrompt ?? ''}\n## Mutation\n${appendix}`.trim();
|
|
64
|
+
}
|
|
65
|
+
if (m.addedCapabilities?.length) {
|
|
66
|
+
agent.capabilities = Array.from(new Set([...agent.capabilities, ...m.addedCapabilities]));
|
|
67
|
+
}
|
|
68
|
+
if (m.removedCapabilities?.length) {
|
|
69
|
+
const remove = new Set(m.removedCapabilities.map((c) => c.toLowerCase()));
|
|
70
|
+
agent.capabilities = agent.capabilities.filter((c) => !remove.has(c.toLowerCase()));
|
|
71
|
+
}
|
|
72
|
+
if (m.modelConfig) {
|
|
73
|
+
agent.modelConfig = { ...agent.modelConfig, ...m.modelConfig };
|
|
74
|
+
}
|
|
75
|
+
if (m.strategyConfig) {
|
|
76
|
+
agent.strategyConfig = { ...agent.strategyConfig, ...m.strategyConfig };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const resultingStrategyId = `${agent.strategyId ?? agent.geneId}:m${agent.mutations.length + 1}`;
|
|
80
|
+
const record: AgentMutationRecord = {
|
|
81
|
+
id: `mut_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
|
|
82
|
+
at: this.now(),
|
|
83
|
+
mutation: { ...m },
|
|
84
|
+
reason: request.reason,
|
|
85
|
+
parentStrategyId: agent.strategyId,
|
|
86
|
+
resultingStrategyId,
|
|
87
|
+
};
|
|
88
|
+
agent.mutations = [...agent.mutations, record];
|
|
89
|
+
agent.strategyId = resultingStrategyId;
|
|
90
|
+
agent.lastMutationAt = record.at;
|
|
91
|
+
|
|
92
|
+
return agent;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async mutate(
|
|
96
|
+
agent: RuntimeAgentRecord,
|
|
97
|
+
request: MutationRequest,
|
|
98
|
+
gene: AgentGeneDefinition
|
|
99
|
+
): Promise<RuntimeAgentRecord> {
|
|
100
|
+
const updated = this.apply({ ...agent, mutations: [...agent.mutations] }, request, gene);
|
|
101
|
+
await this.repo.saveAgent(this.organismId, updated);
|
|
102
|
+
await this.events.emit(OrganismEventType.ORGANISM_AGENT_MUTATED, this.organismId, {
|
|
103
|
+
agentId: updated.id,
|
|
104
|
+
reason: request.reason,
|
|
105
|
+
mutation: request.mutation,
|
|
106
|
+
strategyId: updated.strategyId,
|
|
107
|
+
});
|
|
108
|
+
return updated;
|
|
109
|
+
}
|
|
110
|
+
}
|