@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,225 @@
|
|
|
1
|
+
import { createAgentClassFromDna, exportAgentDna, type AgentRuntime } from '@hazeljs/agent';
|
|
2
|
+
import type {
|
|
3
|
+
AgentBirthProposal,
|
|
4
|
+
AgentGeneDefinition,
|
|
5
|
+
AgentGenealogy,
|
|
6
|
+
AgentReputation,
|
|
7
|
+
RuntimeAgentRecord,
|
|
8
|
+
UtilityScore,
|
|
9
|
+
} from '../types/organism.types';
|
|
10
|
+
import { createEmptyWallet } from '../economy/resource-allocator';
|
|
11
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
12
|
+
import type { OrganismRepository } from '../persistence/organism-repository';
|
|
13
|
+
import { CapabilityRegistry } from '../core/capability-registry';
|
|
14
|
+
import type { AgentResourceWallet } from '../types/organism.types';
|
|
15
|
+
|
|
16
|
+
let agentSeq = 0;
|
|
17
|
+
|
|
18
|
+
function nextAgentId(): string {
|
|
19
|
+
agentSeq += 1;
|
|
20
|
+
return `A${agentSeq}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resetAgentSeqForTests(): void {
|
|
24
|
+
agentSeq = 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function defaultReputation(agentId: string): AgentReputation {
|
|
28
|
+
return {
|
|
29
|
+
agentId,
|
|
30
|
+
score: 0.5,
|
|
31
|
+
dimensions: {
|
|
32
|
+
usefulness: 0.5,
|
|
33
|
+
reliability: 0.5,
|
|
34
|
+
efficiency: 0.5,
|
|
35
|
+
policyCompliance: 1,
|
|
36
|
+
collaboration: 0.5,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function defaultUtility(): UtilityScore {
|
|
42
|
+
return {
|
|
43
|
+
score: 0.5,
|
|
44
|
+
valueGenerated: 0,
|
|
45
|
+
cost: 0,
|
|
46
|
+
riskPenalty: 0,
|
|
47
|
+
confidence: 0.5,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Birth engine — compiles gene + proposal into DNA and registers with AgentRuntime.
|
|
53
|
+
*/
|
|
54
|
+
export class BirthEngine {
|
|
55
|
+
constructor(
|
|
56
|
+
private readonly events: OrganismEventEmitter,
|
|
57
|
+
private readonly repo: OrganismRepository,
|
|
58
|
+
private readonly capabilities: CapabilityRegistry,
|
|
59
|
+
private readonly organismId: string,
|
|
60
|
+
private readonly agentRuntime?: AgentRuntime
|
|
61
|
+
) {}
|
|
62
|
+
|
|
63
|
+
async spawn(input: {
|
|
64
|
+
proposal: AgentBirthProposal;
|
|
65
|
+
gene: AgentGeneDefinition;
|
|
66
|
+
wallet: AgentResourceWallet;
|
|
67
|
+
generation?: number;
|
|
68
|
+
parentAgentId?: string;
|
|
69
|
+
nameHint?: string;
|
|
70
|
+
}): Promise<RuntimeAgentRecord> {
|
|
71
|
+
const id = nextAgentId();
|
|
72
|
+
const generation = input.generation ?? 1;
|
|
73
|
+
const specialize = input.proposal.specialize ?? [];
|
|
74
|
+
const capabilities = Array.from(
|
|
75
|
+
new Set([...input.gene.capabilities, ...specialize, ...input.proposal.requiredCapabilities])
|
|
76
|
+
);
|
|
77
|
+
const nameHint =
|
|
78
|
+
input.nameHint ??
|
|
79
|
+
`${input.proposal.needId
|
|
80
|
+
.split(/[-_]/)
|
|
81
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
82
|
+
.join('')}Agent`;
|
|
83
|
+
const dnaAgentName = `${nameHint}_${id}`;
|
|
84
|
+
const objective = input.proposal.expectedOutcome;
|
|
85
|
+
|
|
86
|
+
const dna = exportAgentDna({
|
|
87
|
+
name: dnaAgentName,
|
|
88
|
+
description: input.gene.description ?? input.proposal.reason,
|
|
89
|
+
systemPrompt:
|
|
90
|
+
input.gene.initialPrompt ??
|
|
91
|
+
`You are ${dnaAgentName}. Objective: ${objective}. Capabilities: ${capabilities.join(', ')}.`,
|
|
92
|
+
tools: [],
|
|
93
|
+
policies: input.gene.policies,
|
|
94
|
+
mission: { goal: objective },
|
|
95
|
+
metadata: {
|
|
96
|
+
capabilities,
|
|
97
|
+
organismId: this.organismId,
|
|
98
|
+
geneId: input.gene.id,
|
|
99
|
+
needId: input.proposal.needId,
|
|
100
|
+
terminationCriteria: input.proposal.terminationCriteria,
|
|
101
|
+
},
|
|
102
|
+
version: '1.0.0',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (this.agentRuntime) {
|
|
106
|
+
const AgentClass = createAgentClassFromDna(dna);
|
|
107
|
+
this.agentRuntime.registerAgent(AgentClass);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const agent: RuntimeAgentRecord = {
|
|
111
|
+
id,
|
|
112
|
+
name: nameHint,
|
|
113
|
+
objective,
|
|
114
|
+
capabilities,
|
|
115
|
+
geneId: input.gene.id,
|
|
116
|
+
parentAgentId: input.parentAgentId ?? input.proposal.parentAgentId,
|
|
117
|
+
generation,
|
|
118
|
+
status: 'active',
|
|
119
|
+
reputation: defaultReputation(id),
|
|
120
|
+
utility: defaultUtility(),
|
|
121
|
+
wallet: { ...input.wallet },
|
|
122
|
+
birthProposal: input.proposal,
|
|
123
|
+
terminationCriteria: input.proposal.terminationCriteria ?? [],
|
|
124
|
+
createdAt: new Date(),
|
|
125
|
+
evaluationCount: 0,
|
|
126
|
+
criticalResponsibility: false,
|
|
127
|
+
costConsumed: 0,
|
|
128
|
+
tokensConsumed: 0,
|
|
129
|
+
valueGenerated: 0,
|
|
130
|
+
specialize,
|
|
131
|
+
dnaAgentName,
|
|
132
|
+
systemPrompt: dna.systemPrompt,
|
|
133
|
+
permissions: [...capabilities],
|
|
134
|
+
strategyConfig: {},
|
|
135
|
+
modelConfig: {},
|
|
136
|
+
mutations: [],
|
|
137
|
+
strategyId: `${input.gene.id}:g${generation}`,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const genealogy: AgentGenealogy = {
|
|
141
|
+
agentId: id,
|
|
142
|
+
parentAgentId: agent.parentAgentId,
|
|
143
|
+
rootGeneId: input.gene.id,
|
|
144
|
+
generation,
|
|
145
|
+
children: [],
|
|
146
|
+
createdAt: agent.createdAt,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
await this.repo.saveAgent(this.organismId, agent);
|
|
150
|
+
await this.repo.saveGenealogy(this.organismId, genealogy);
|
|
151
|
+
|
|
152
|
+
if (agent.parentAgentId) {
|
|
153
|
+
const parentGene = (await this.repo.listGenealogy(this.organismId)).find(
|
|
154
|
+
(g) => g.agentId === agent.parentAgentId
|
|
155
|
+
);
|
|
156
|
+
if (parentGene) {
|
|
157
|
+
parentGene.children = [...parentGene.children, id];
|
|
158
|
+
await this.repo.saveGenealogy(this.organismId, parentGene);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.capabilities.register(id, capabilities);
|
|
163
|
+
|
|
164
|
+
await this.events.emit(OrganismEventType.ORGANISM_AGENT_BORN, this.organismId, {
|
|
165
|
+
organismId: this.organismId,
|
|
166
|
+
agentId: id,
|
|
167
|
+
parentAgentId: agent.parentAgentId,
|
|
168
|
+
reason: input.proposal.reason,
|
|
169
|
+
generation,
|
|
170
|
+
geneId: input.gene.id,
|
|
171
|
+
needId: input.proposal.needId,
|
|
172
|
+
name: nameHint,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return agent;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Termination engine — release resources, retain genealogy, emit death event.
|
|
181
|
+
*/
|
|
182
|
+
export class TerminationEngine {
|
|
183
|
+
constructor(
|
|
184
|
+
private readonly events: OrganismEventEmitter,
|
|
185
|
+
private readonly repo: OrganismRepository,
|
|
186
|
+
private readonly capabilities: CapabilityRegistry,
|
|
187
|
+
private readonly organismId: string,
|
|
188
|
+
private readonly releaseWallet: (agent: RuntimeAgentRecord) => void
|
|
189
|
+
) {}
|
|
190
|
+
|
|
191
|
+
async terminate(agentId: string, reason: string): Promise<RuntimeAgentRecord | undefined> {
|
|
192
|
+
const agent = await this.repo.getAgent(this.organismId, agentId);
|
|
193
|
+
if (!agent || agent.status === 'terminated') return agent;
|
|
194
|
+
|
|
195
|
+
agent.status = 'termination-pending';
|
|
196
|
+
await this.repo.saveAgent(this.organismId, agent);
|
|
197
|
+
|
|
198
|
+
this.releaseWallet(agent);
|
|
199
|
+
this.capabilities.unregister(agentId);
|
|
200
|
+
|
|
201
|
+
agent.status = 'terminated';
|
|
202
|
+
agent.terminatedAt = new Date();
|
|
203
|
+
await this.repo.saveAgent(this.organismId, agent);
|
|
204
|
+
|
|
205
|
+
const genealogy = (await this.repo.listGenealogy(this.organismId)).find(
|
|
206
|
+
(g) => g.agentId === agentId
|
|
207
|
+
);
|
|
208
|
+
if (genealogy) {
|
|
209
|
+
genealogy.terminatedAt = agent.terminatedAt;
|
|
210
|
+
await this.repo.saveGenealogy(this.organismId, genealogy);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
await this.events.emit(OrganismEventType.ORGANISM_AGENT_TERMINATED, this.organismId, {
|
|
214
|
+
agentId,
|
|
215
|
+
reason,
|
|
216
|
+
finalScore: agent.utility.score,
|
|
217
|
+
totalCost: agent.costConsumed,
|
|
218
|
+
totalValue: agent.valueGenerated,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return agent;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { createEmptyWallet };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentBirthProposal,
|
|
3
|
+
DetectedNeed,
|
|
4
|
+
OrganismDecision,
|
|
5
|
+
RuntimeAgentRecord,
|
|
6
|
+
} from '../types/organism.types';
|
|
7
|
+
import { CapabilityRegistry } from '../core/capability-registry';
|
|
8
|
+
import type { AgentGeneDefinition } from '../types/organism.types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Deterministic decision engine: reuse → specialize → spawn → observe.
|
|
12
|
+
*/
|
|
13
|
+
export class DecisionEngine {
|
|
14
|
+
constructor(private readonly capabilities: CapabilityRegistry) {}
|
|
15
|
+
|
|
16
|
+
decide(input: {
|
|
17
|
+
need: DetectedNeed;
|
|
18
|
+
liveAgents: RuntimeAgentRecord[];
|
|
19
|
+
genes: AgentGeneDefinition[];
|
|
20
|
+
canSpawn: boolean;
|
|
21
|
+
spawnBlockReason?: string;
|
|
22
|
+
}): OrganismDecision {
|
|
23
|
+
const { need, liveAgents, genes, canSpawn, spawnBlockReason } = input;
|
|
24
|
+
|
|
25
|
+
const capable = this.capabilities.findCapableAgents(need.requiredCapabilities, liveAgents);
|
|
26
|
+
if (capable.length > 0) {
|
|
27
|
+
const best = capable.sort((a, b) => b.reputation.score - a.reputation.score)[0];
|
|
28
|
+
return {
|
|
29
|
+
action: 'delegate',
|
|
30
|
+
reasoningSummary: `Capable agent ${best.id} already covers ${need.requiredCapabilities.join(', ')}`,
|
|
31
|
+
confidence: Math.min(0.95, need.confidence + 0.05),
|
|
32
|
+
targetAgentId: best.id,
|
|
33
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
34
|
+
needId: need.need,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const partial = this.capabilities.findPartialAgents(need.requiredCapabilities, liveAgents);
|
|
39
|
+
const gene = this.selectGene(need.requiredCapabilities, genes);
|
|
40
|
+
|
|
41
|
+
if (partial.length > 0 && gene) {
|
|
42
|
+
const parent = partial[0];
|
|
43
|
+
const missing = need.requiredCapabilities.filter(
|
|
44
|
+
(c) => !parent.capabilities.map((x) => x.toLowerCase()).includes(c.toLowerCase())
|
|
45
|
+
);
|
|
46
|
+
if (!canSpawn) {
|
|
47
|
+
return {
|
|
48
|
+
action: 'observe',
|
|
49
|
+
reasoningSummary: `Would specialize for ${need.need} but spawn blocked: ${spawnBlockReason}`,
|
|
50
|
+
confidence: 0.4,
|
|
51
|
+
needId: need.need,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const proposal: AgentBirthProposal = {
|
|
55
|
+
reason: need.reason,
|
|
56
|
+
needId: need.need,
|
|
57
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
58
|
+
expectedOutcome: `Address need ${need.need}`,
|
|
59
|
+
estimatedCost: 1,
|
|
60
|
+
expectedUtility: need.urgency,
|
|
61
|
+
terminationCriteria: [
|
|
62
|
+
`Need ${need.need} resolved`,
|
|
63
|
+
'Utility score below 0.2 after 5 evaluations',
|
|
64
|
+
'No related tasks for evaluation window',
|
|
65
|
+
],
|
|
66
|
+
confidence: need.confidence,
|
|
67
|
+
geneId: gene.id,
|
|
68
|
+
specialize: missing,
|
|
69
|
+
parentAgentId: parent.id,
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
action: 'specialize',
|
|
73
|
+
reasoningSummary: `Partial agent ${parent.id} exists; spawn specialized agent for missing ${missing.join(', ')}`,
|
|
74
|
+
confidence: need.confidence,
|
|
75
|
+
targetAgentId: parent.id,
|
|
76
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
77
|
+
resourceRequest: { tokens: 50_000 },
|
|
78
|
+
birthProposal: proposal,
|
|
79
|
+
needId: need.need,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!gene) {
|
|
84
|
+
return {
|
|
85
|
+
action: 'observe',
|
|
86
|
+
reasoningSummary: `No gene covers required capabilities ${need.requiredCapabilities.join(', ')}`,
|
|
87
|
+
confidence: 0.5,
|
|
88
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
89
|
+
needId: need.need,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!canSpawn) {
|
|
94
|
+
return {
|
|
95
|
+
action: 'observe',
|
|
96
|
+
reasoningSummary: `Need ${need.need} requires spawn but blocked: ${spawnBlockReason}`,
|
|
97
|
+
confidence: 0.4,
|
|
98
|
+
needId: need.need,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const proposal: AgentBirthProposal = {
|
|
103
|
+
reason: need.reason,
|
|
104
|
+
needId: need.need,
|
|
105
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
106
|
+
expectedOutcome: `Address need ${need.need}`,
|
|
107
|
+
estimatedCost: 1,
|
|
108
|
+
expectedUtility: need.urgency,
|
|
109
|
+
terminationCriteria: [
|
|
110
|
+
`Need ${need.need} resolved`,
|
|
111
|
+
'Utility score below 0.2 after 5 evaluations',
|
|
112
|
+
'No related tasks for evaluation window',
|
|
113
|
+
],
|
|
114
|
+
confidence: need.confidence,
|
|
115
|
+
geneId: gene.id,
|
|
116
|
+
specialize: need.requiredCapabilities.filter(
|
|
117
|
+
(c) => !gene.capabilities.map((g) => g.toLowerCase()).includes(c.toLowerCase())
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
action: 'spawn',
|
|
123
|
+
reasoningSummary: `No capable agent for ${need.need}; spawning from gene ${gene.id}`,
|
|
124
|
+
confidence: need.confidence,
|
|
125
|
+
requiredCapabilities: need.requiredCapabilities,
|
|
126
|
+
resourceRequest: { tokens: 50_000 },
|
|
127
|
+
birthProposal: proposal,
|
|
128
|
+
needId: need.need,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
selectGene(required: string[], genes: AgentGeneDefinition[]): AgentGeneDefinition | undefined {
|
|
133
|
+
if (!genes.length) return undefined;
|
|
134
|
+
const needed = required.map((c) => c.toLowerCase());
|
|
135
|
+
let best: AgentGeneDefinition | undefined;
|
|
136
|
+
let bestScore = -1;
|
|
137
|
+
let bestRatio = -1;
|
|
138
|
+
let bestPrimaryHit = false;
|
|
139
|
+
const primary = needed[0];
|
|
140
|
+
for (const gene of genes) {
|
|
141
|
+
const caps = new Set(gene.capabilities.map((c) => c.toLowerCase()));
|
|
142
|
+
const score = needed.filter((n) => caps.has(n)).length;
|
|
143
|
+
const ratio = score / Math.max(1, gene.capabilities.length);
|
|
144
|
+
const primaryHit = primary ? caps.has(primary) : false;
|
|
145
|
+
const better =
|
|
146
|
+
score > bestScore ||
|
|
147
|
+
(score === bestScore && ratio > bestRatio) ||
|
|
148
|
+
(score === bestScore && ratio === bestRatio && primaryHit && !bestPrimaryHit);
|
|
149
|
+
if (better) {
|
|
150
|
+
bestScore = score;
|
|
151
|
+
bestRatio = ratio;
|
|
152
|
+
bestPrimaryHit = primaryHit;
|
|
153
|
+
best = gene;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return best ?? genes[0];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2 — parent→child reproduction with explicit inheritance policy.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AgentBirthProposal,
|
|
7
|
+
AgentGeneDefinition,
|
|
8
|
+
AgentResourceWallet,
|
|
9
|
+
InheritancePolicy,
|
|
10
|
+
OrganismLimits,
|
|
11
|
+
ReproduceRequest,
|
|
12
|
+
RuntimeAgentRecord,
|
|
13
|
+
} from '../types/organism.types';
|
|
14
|
+
import { DEFAULT_INHERITANCE_POLICY } from '../types/organism.types';
|
|
15
|
+
import { OrganismLimitError, OrganismStateError } from '../errors/organism.errors';
|
|
16
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
17
|
+
import type { OrganismRepository } from '../persistence/organism-repository';
|
|
18
|
+
import { ConstitutionEnforcer } from '../governance/constitution';
|
|
19
|
+
import { BirthEngine } from '../lifecycle/birth-engine';
|
|
20
|
+
import { GenealogyManager } from '../genealogy/genealogy-manager';
|
|
21
|
+
|
|
22
|
+
export interface ReproductionConfig {
|
|
23
|
+
cooldownMs: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_REPRODUCTION_CONFIG: ReproductionConfig = {
|
|
27
|
+
cooldownMs: 30_000,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class ReproductionEngine {
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly events: OrganismEventEmitter,
|
|
33
|
+
private readonly repo: OrganismRepository,
|
|
34
|
+
private readonly birth: BirthEngine,
|
|
35
|
+
private readonly genealogy: GenealogyManager,
|
|
36
|
+
private readonly constitution: ConstitutionEnforcer,
|
|
37
|
+
private readonly organismId: string,
|
|
38
|
+
private readonly now: () => Date = () => new Date(),
|
|
39
|
+
private readonly config: ReproductionConfig = DEFAULT_REPRODUCTION_CONFIG
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
resolveInheritance(partial?: InheritancePolicy): InheritancePolicy {
|
|
43
|
+
return {
|
|
44
|
+
...DEFAULT_INHERITANCE_POLICY,
|
|
45
|
+
...partial,
|
|
46
|
+
memory: {
|
|
47
|
+
strategy:
|
|
48
|
+
partial?.memory?.strategy ??
|
|
49
|
+
DEFAULT_INHERITANCE_POLICY.memory?.strategy ??
|
|
50
|
+
'relevant-only',
|
|
51
|
+
maxItems: partial?.memory?.maxItems ?? DEFAULT_INHERITANCE_POLICY.memory?.maxItems,
|
|
52
|
+
},
|
|
53
|
+
resources: {
|
|
54
|
+
transferFraction:
|
|
55
|
+
partial?.resources?.transferFraction ??
|
|
56
|
+
DEFAULT_INHERITANCE_POLICY.resources?.transferFraction,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
assertCanReproduce(input: {
|
|
62
|
+
parent: RuntimeAgentRecord;
|
|
63
|
+
gene: AgentGeneDefinition;
|
|
64
|
+
limits: OrganismLimits;
|
|
65
|
+
liveAgents: RuntimeAgentRecord[];
|
|
66
|
+
liveChildCount: number;
|
|
67
|
+
}): void {
|
|
68
|
+
const { parent, gene, limits, liveAgents, liveChildCount } = input;
|
|
69
|
+
|
|
70
|
+
if (parent.status === 'terminated' || parent.status === 'failed') {
|
|
71
|
+
throw new OrganismStateError(`Parent ${parent.id} is not active`);
|
|
72
|
+
}
|
|
73
|
+
if (gene.reproduction && gene.reproduction.enabled === false) {
|
|
74
|
+
throw new OrganismLimitError(`Gene ${gene.id} has reproduction disabled`);
|
|
75
|
+
}
|
|
76
|
+
if (parent.generation >= limits.maxGenerationDepth) {
|
|
77
|
+
throw new OrganismLimitError(
|
|
78
|
+
`maxGenerationDepth (${limits.maxGenerationDepth}) reached by parent`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const geneMax = gene.reproduction?.maxChildren;
|
|
82
|
+
const maxChildren =
|
|
83
|
+
geneMax != null ? Math.min(geneMax, limits.maxChildrenPerAgent) : limits.maxChildrenPerAgent;
|
|
84
|
+
if (liveChildCount >= maxChildren) {
|
|
85
|
+
throw new OrganismLimitError(
|
|
86
|
+
`maxChildrenPerAgent (${maxChildren}) reached for parent ${parent.id}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (liveAgents.length >= limits.maxAgents) {
|
|
90
|
+
throw new OrganismLimitError(`maxAgents (${limits.maxAgents}) reached`);
|
|
91
|
+
}
|
|
92
|
+
if (parent.lastReproductionAt) {
|
|
93
|
+
const elapsed = this.now().getTime() - parent.lastReproductionAt.getTime();
|
|
94
|
+
if (elapsed < this.config.cooldownMs) {
|
|
95
|
+
throw new OrganismLimitError(
|
|
96
|
+
`Reproduction cooldown active (${elapsed}ms < ${this.config.cooldownMs}ms)`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Build child capabilities: parent caps + specialization.
|
|
104
|
+
* Permissions must remain a subset of the parent's permission grants.
|
|
105
|
+
*/
|
|
106
|
+
resolveChildCapabilities(
|
|
107
|
+
parent: RuntimeAgentRecord,
|
|
108
|
+
specialization: string[],
|
|
109
|
+
policy: InheritancePolicy
|
|
110
|
+
): { capabilities: string[]; permissions: string[] } {
|
|
111
|
+
const capabilities = Array.from(new Set([...parent.capabilities, ...specialization]));
|
|
112
|
+
let permissions: string[];
|
|
113
|
+
if (policy.permissions === 'none') {
|
|
114
|
+
permissions = [];
|
|
115
|
+
} else {
|
|
116
|
+
// copy and subset both enforce ⊆ parent (never escalate)
|
|
117
|
+
permissions = [...parent.permissions];
|
|
118
|
+
}
|
|
119
|
+
return { capabilities, permissions };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
transferResources(
|
|
123
|
+
parent: RuntimeAgentRecord,
|
|
124
|
+
policy: InheritancePolicy,
|
|
125
|
+
requestedTokens?: number
|
|
126
|
+
): { childWallet: AgentResourceWallet; parentWallet: AgentResourceWallet } {
|
|
127
|
+
const fraction = policy.resources?.transferFraction ?? 0.25;
|
|
128
|
+
const parentTokens = parent.wallet.tokensRemaining ?? 0;
|
|
129
|
+
const parentMoney = parent.wallet.moneyRemaining?.amount ?? 0;
|
|
130
|
+
const currency = parent.wallet.moneyRemaining?.currency ?? 'USD';
|
|
131
|
+
|
|
132
|
+
let tokens = Math.floor(parentTokens * fraction);
|
|
133
|
+
if (requestedTokens != null) {
|
|
134
|
+
tokens = Math.min(tokens, requestedTokens, parentTokens);
|
|
135
|
+
}
|
|
136
|
+
const money = Math.floor(parentMoney * fraction);
|
|
137
|
+
|
|
138
|
+
const childWallet: AgentResourceWallet = {
|
|
139
|
+
tokensRemaining: tokens,
|
|
140
|
+
moneyRemaining: { amount: money, currency },
|
|
141
|
+
computeUnitsRemaining: Math.floor((parent.wallet.computeUnitsRemaining ?? 0) * fraction),
|
|
142
|
+
toolCallsRemaining: Math.floor((parent.wallet.toolCallsRemaining ?? 0) * fraction),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const parentWallet: AgentResourceWallet = {
|
|
146
|
+
tokensRemaining: parentTokens - tokens,
|
|
147
|
+
moneyRemaining: { amount: parentMoney - money, currency },
|
|
148
|
+
computeUnitsRemaining:
|
|
149
|
+
(parent.wallet.computeUnitsRemaining ?? 0) - (childWallet.computeUnitsRemaining ?? 0),
|
|
150
|
+
toolCallsRemaining:
|
|
151
|
+
(parent.wallet.toolCallsRemaining ?? 0) - (childWallet.toolCallsRemaining ?? 0),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return { childWallet, parentWallet };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
buildProposal(
|
|
158
|
+
parent: RuntimeAgentRecord,
|
|
159
|
+
request: ReproduceRequest,
|
|
160
|
+
capabilities: string[]
|
|
161
|
+
): AgentBirthProposal {
|
|
162
|
+
const specialization = request.specialization ?? [];
|
|
163
|
+
const needId = request.needId ?? `specialize-${specialization.join('-') || parent.id}`;
|
|
164
|
+
return {
|
|
165
|
+
reason: request.reason,
|
|
166
|
+
needId,
|
|
167
|
+
requiredCapabilities: capabilities,
|
|
168
|
+
expectedOutcome:
|
|
169
|
+
request.objective ??
|
|
170
|
+
`Specialized descendant of ${parent.name} for ${specialization.join(', ') || 'focused work'}`,
|
|
171
|
+
estimatedCost: 1,
|
|
172
|
+
expectedUtility: parent.utility.score,
|
|
173
|
+
terminationCriteria: request.terminationCriteria ?? [
|
|
174
|
+
`Specialization ${specialization.join(', ') || needId} complete`,
|
|
175
|
+
'Utility score below 0.2 after 5 evaluations',
|
|
176
|
+
'No related tasks for evaluation window',
|
|
177
|
+
],
|
|
178
|
+
confidence: request.confidence ?? 0.8,
|
|
179
|
+
geneId: parent.geneId,
|
|
180
|
+
specialize: specialization,
|
|
181
|
+
parentAgentId: parent.id,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async reproduce(input: {
|
|
186
|
+
parent: RuntimeAgentRecord;
|
|
187
|
+
gene: AgentGeneDefinition;
|
|
188
|
+
request: ReproduceRequest;
|
|
189
|
+
limits: OrganismLimits;
|
|
190
|
+
liveAgents: RuntimeAgentRecord[];
|
|
191
|
+
/** When true, child wallet comes from organism pool allocation instead of parent. */
|
|
192
|
+
poolWallet?: AgentResourceWallet;
|
|
193
|
+
}): Promise<{ child: RuntimeAgentRecord; parent: RuntimeAgentRecord }> {
|
|
194
|
+
const policy = this.resolveInheritance(input.request.inheritance);
|
|
195
|
+
const liveChildCount = await this.genealogy.countLiveChildren(
|
|
196
|
+
input.parent.id,
|
|
197
|
+
input.liveAgents
|
|
198
|
+
);
|
|
199
|
+
this.assertCanReproduce({
|
|
200
|
+
parent: input.parent,
|
|
201
|
+
gene: input.gene,
|
|
202
|
+
limits: input.limits,
|
|
203
|
+
liveAgents: input.liveAgents,
|
|
204
|
+
liveChildCount,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
this.constitution.assertAllows('reproduce', {});
|
|
208
|
+
|
|
209
|
+
const specialization = input.request.specialization ?? [];
|
|
210
|
+
const { capabilities, permissions } = this.resolveChildCapabilities(
|
|
211
|
+
input.parent,
|
|
212
|
+
specialization,
|
|
213
|
+
policy
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
let childWallet: AgentResourceWallet;
|
|
217
|
+
let parentWallet = input.parent.wallet;
|
|
218
|
+
if (input.poolWallet) {
|
|
219
|
+
childWallet = input.poolWallet;
|
|
220
|
+
} else {
|
|
221
|
+
const transfer = this.transferResources(input.parent, policy, input.request.tokens);
|
|
222
|
+
childWallet = transfer.childWallet;
|
|
223
|
+
parentWallet = transfer.parentWallet;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const proposal = this.buildProposal(input.parent, input.request, capabilities);
|
|
227
|
+
const generation = input.parent.generation + 1;
|
|
228
|
+
|
|
229
|
+
input.parent.status = 'reproducing';
|
|
230
|
+
await this.repo.saveAgent(this.organismId, input.parent);
|
|
231
|
+
|
|
232
|
+
const child = await this.birth.spawn({
|
|
233
|
+
proposal,
|
|
234
|
+
gene: input.gene,
|
|
235
|
+
wallet: childWallet,
|
|
236
|
+
generation,
|
|
237
|
+
parentAgentId: input.parent.id,
|
|
238
|
+
nameHint: specialization.length
|
|
239
|
+
? `${specialization
|
|
240
|
+
.map((s) =>
|
|
241
|
+
s
|
|
242
|
+
.split(/[-_]/)
|
|
243
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
244
|
+
.join('')
|
|
245
|
+
)
|
|
246
|
+
.join('')}Agent`
|
|
247
|
+
: `${input.parent.name}Child`,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Apply inheritance overlays
|
|
251
|
+
child.permissions = permissions;
|
|
252
|
+
child.systemPrompt =
|
|
253
|
+
policy.strategies !== false
|
|
254
|
+
? [
|
|
255
|
+
input.parent.systemPrompt ?? input.gene.initialPrompt ?? '',
|
|
256
|
+
specialization.length ? `Specialization: ${specialization.join(', ')}` : '',
|
|
257
|
+
]
|
|
258
|
+
.filter(Boolean)
|
|
259
|
+
.join('\n')
|
|
260
|
+
: input.gene.initialPrompt;
|
|
261
|
+
if (policy.strategies !== false) {
|
|
262
|
+
child.strategyConfig = { ...input.parent.strategyConfig };
|
|
263
|
+
}
|
|
264
|
+
if (policy.modelSettings !== false) {
|
|
265
|
+
child.modelConfig = { ...input.parent.modelConfig };
|
|
266
|
+
}
|
|
267
|
+
child.strategyId = input.parent.strategyId
|
|
268
|
+
? `${input.parent.strategyId}:g${generation}`
|
|
269
|
+
: `${input.parent.geneId}:g${generation}`;
|
|
270
|
+
|
|
271
|
+
await this.repo.saveAgent(this.organismId, child);
|
|
272
|
+
|
|
273
|
+
input.parent.wallet = parentWallet;
|
|
274
|
+
input.parent.status = 'active';
|
|
275
|
+
input.parent.lastReproductionAt = this.now();
|
|
276
|
+
await this.repo.saveAgent(this.organismId, input.parent);
|
|
277
|
+
|
|
278
|
+
await this.events.emit(OrganismEventType.ORGANISM_AGENT_REPRODUCED, this.organismId, {
|
|
279
|
+
parentAgentId: input.parent.id,
|
|
280
|
+
childAgentId: child.id,
|
|
281
|
+
generation,
|
|
282
|
+
specialization,
|
|
283
|
+
reason: input.request.reason,
|
|
284
|
+
inheritance: policy,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return { child, parent: input.parent };
|
|
288
|
+
}
|
|
289
|
+
}
|