@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,1369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OrganismRuntime — mission-defined self-organizing agent society control plane.
|
|
3
|
+
* Composes AgentRuntime; does not replace the Agent OS kernel.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
AgentRuntime,
|
|
8
|
+
PolicyEngine,
|
|
9
|
+
createAgentClassFromDna,
|
|
10
|
+
createMockLlmProvider,
|
|
11
|
+
exportAgentDna,
|
|
12
|
+
type LLMProvider,
|
|
13
|
+
} from '@hazeljs/agent';
|
|
14
|
+
import type {
|
|
15
|
+
AgentGeneDefinition,
|
|
16
|
+
AgentGenealogy,
|
|
17
|
+
AgentMutation,
|
|
18
|
+
AgentOutcomeReport,
|
|
19
|
+
ConstitutionDefinition,
|
|
20
|
+
EnvironmentDefinition,
|
|
21
|
+
EnvironmentSignal,
|
|
22
|
+
GenerationEvaluationResult,
|
|
23
|
+
MarketClearingResult,
|
|
24
|
+
MarketConfig,
|
|
25
|
+
MissionDefinition,
|
|
26
|
+
NegotiationOffer,
|
|
27
|
+
NegotiationResult,
|
|
28
|
+
OrganismCycleConfig,
|
|
29
|
+
OrganismDecision,
|
|
30
|
+
OrganismGraph,
|
|
31
|
+
OrganismInspectState,
|
|
32
|
+
OrganismLimits,
|
|
33
|
+
OrganismRecord,
|
|
34
|
+
ReproduceRequest,
|
|
35
|
+
ResourceBid,
|
|
36
|
+
ResourceDefinition,
|
|
37
|
+
ResourceRequest,
|
|
38
|
+
RuntimeAgentRecord,
|
|
39
|
+
SignalNeedMapping,
|
|
40
|
+
SurvivalConfig,
|
|
41
|
+
UtilityForecast,
|
|
42
|
+
UtilityWeights,
|
|
43
|
+
} from '../types/organism.types';
|
|
44
|
+
import {
|
|
45
|
+
DEFAULT_ORGANISM_LIMITS,
|
|
46
|
+
DEFAULT_SURVIVAL_CONFIG,
|
|
47
|
+
DEFAULT_UTILITY_WEIGHTS,
|
|
48
|
+
} from '../types/organism.types';
|
|
49
|
+
import { OrganismStateError } from '../errors/organism.errors';
|
|
50
|
+
import { OrganismEventEmitter, OrganismEventType } from '../events/organism-events';
|
|
51
|
+
import {
|
|
52
|
+
InMemoryOrganismRepository,
|
|
53
|
+
type OrganismRepository,
|
|
54
|
+
} from '../persistence/organism-repository';
|
|
55
|
+
import {
|
|
56
|
+
resolveConstitution,
|
|
57
|
+
resolveEnvironment,
|
|
58
|
+
resolveGene,
|
|
59
|
+
resolveMission,
|
|
60
|
+
resolveResource,
|
|
61
|
+
type OrganismDecoratorOptions,
|
|
62
|
+
} from '../decorators';
|
|
63
|
+
import { ConstitutionEnforcer } from '../governance/constitution';
|
|
64
|
+
import { ResourceAllocator } from '../economy/resource-allocator';
|
|
65
|
+
import { UtilityEngine, ReputationEngine } from '../economy/utility-engine';
|
|
66
|
+
import { UtilityForecaster } from '../economy/utility-forecaster';
|
|
67
|
+
import { MarketEngine } from '../economy/market-engine';
|
|
68
|
+
import { CapabilityRegistry } from './capability-registry';
|
|
69
|
+
import { NeedDetector, PerceptionEngine } from '../perception/perception-engine';
|
|
70
|
+
import { DecisionEngine } from '../lifecycle/decision-engine';
|
|
71
|
+
import { BirthEngine, TerminationEngine } from '../lifecycle/birth-engine';
|
|
72
|
+
import { SurvivalEngine } from '../lifecycle/survival-engine';
|
|
73
|
+
import {
|
|
74
|
+
ReproductionEngine,
|
|
75
|
+
DEFAULT_REPRODUCTION_CONFIG,
|
|
76
|
+
type ReproductionConfig,
|
|
77
|
+
} from '../lifecycle/reproduction-engine';
|
|
78
|
+
import { GenealogyManager } from '../genealogy/genealogy-manager';
|
|
79
|
+
import { MutationEngine } from '../evolution/mutation-engine';
|
|
80
|
+
import { EvolutionEngine, GenerationManager } from '../evolution/evolution-engine';
|
|
81
|
+
import { applyMissionMetricUpdates, createMissionProgress } from './mission';
|
|
82
|
+
import { OrganismAgentContext } from './organism-context';
|
|
83
|
+
import { OrganismClock, parseDurationMs } from './clock';
|
|
84
|
+
import { OrganismMetrics } from '../observability/metrics';
|
|
85
|
+
|
|
86
|
+
type Newable = new (...args: unknown[]) => unknown;
|
|
87
|
+
|
|
88
|
+
export interface CreateOrganismOptions {
|
|
89
|
+
id?: string;
|
|
90
|
+
mission: MissionDefinition | Newable;
|
|
91
|
+
genes?: Array<AgentGeneDefinition | Newable>;
|
|
92
|
+
environment?: EnvironmentDefinition | Newable;
|
|
93
|
+
constitution?: ConstitutionDefinition | Newable;
|
|
94
|
+
resources?: ResourceDefinition | Newable;
|
|
95
|
+
limits?: Partial<OrganismLimits>;
|
|
96
|
+
cycles?: OrganismCycleConfig;
|
|
97
|
+
signalNeedMappings?: SignalNeedMapping[];
|
|
98
|
+
survival?: Partial<SurvivalConfig>;
|
|
99
|
+
utilityWeights?: Partial<UtilityWeights>;
|
|
100
|
+
reproduction?: Partial<ReproductionConfig>;
|
|
101
|
+
market?: Partial<MarketConfig>;
|
|
102
|
+
repository?: OrganismRepository;
|
|
103
|
+
agentRuntime?: AgentRuntime;
|
|
104
|
+
llmProvider?: LLMProvider;
|
|
105
|
+
debug?: boolean;
|
|
106
|
+
simulation?: boolean;
|
|
107
|
+
/** Relevance threshold for perception (0-1). */
|
|
108
|
+
relevanceThreshold?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface SimulateOptions {
|
|
112
|
+
duration: string | number;
|
|
113
|
+
environment?: { clock?: 'accelerated' | 'real' };
|
|
114
|
+
signals?: Array<Partial<EnvironmentSignal> & Pick<EnvironmentSignal, 'type' | 'source'>>;
|
|
115
|
+
stepMs?: number;
|
|
116
|
+
onEvent?: (line: string) => void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class OrganismRuntime {
|
|
120
|
+
readonly id: string;
|
|
121
|
+
readonly events: OrganismEventEmitter;
|
|
122
|
+
readonly metrics = new OrganismMetrics();
|
|
123
|
+
readonly capabilities = new CapabilityRegistry();
|
|
124
|
+
readonly clock = new OrganismClock();
|
|
125
|
+
|
|
126
|
+
private record!: OrganismRecord;
|
|
127
|
+
private readonly repo: OrganismRepository;
|
|
128
|
+
private readonly perception: PerceptionEngine;
|
|
129
|
+
private readonly needDetector: NeedDetector;
|
|
130
|
+
private readonly decisionEngine: DecisionEngine;
|
|
131
|
+
private readonly allocator: ResourceAllocator;
|
|
132
|
+
private readonly utilityEngine: UtilityEngine;
|
|
133
|
+
private readonly reputationEngine: ReputationEngine;
|
|
134
|
+
private readonly survivalEngine: SurvivalEngine;
|
|
135
|
+
private readonly forecaster: UtilityForecaster;
|
|
136
|
+
private marketEngine!: MarketEngine;
|
|
137
|
+
private birthEngine!: BirthEngine;
|
|
138
|
+
private terminationEngine!: TerminationEngine;
|
|
139
|
+
private reproductionEngine!: ReproductionEngine;
|
|
140
|
+
private genealogyManager!: GenealogyManager;
|
|
141
|
+
private mutationEngine!: MutationEngine;
|
|
142
|
+
private evolutionEngine!: EvolutionEngine;
|
|
143
|
+
private generationManager!: GenerationManager;
|
|
144
|
+
private constitutionEnforcer!: ConstitutionEnforcer;
|
|
145
|
+
private agentRuntime: AgentRuntime;
|
|
146
|
+
private cycleTimers: Array<ReturnType<typeof setInterval>> = [];
|
|
147
|
+
private pendingApprovals = new Map<string, boolean>();
|
|
148
|
+
private agentCache = new Map<string, RuntimeAgentRecord>();
|
|
149
|
+
private reproductionConfig: ReproductionConfig = DEFAULT_REPRODUCTION_CONFIG;
|
|
150
|
+
private debug: boolean;
|
|
151
|
+
private log: (line: string) => void;
|
|
152
|
+
|
|
153
|
+
private constructor(options: CreateOrganismOptions) {
|
|
154
|
+
this.id = options.id ?? `org_${Date.now().toString(36)}`;
|
|
155
|
+
this.events = new OrganismEventEmitter();
|
|
156
|
+
this.repo = options.repository ?? new InMemoryOrganismRepository();
|
|
157
|
+
this.debug = options.debug ?? false;
|
|
158
|
+
this.log = (line: string): void => {
|
|
159
|
+
if (this.debug || options.simulation) {
|
|
160
|
+
// eslint-disable-next-line no-console
|
|
161
|
+
console.log(line);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
this.perception = new PerceptionEngine(options.relevanceThreshold ?? 0.3);
|
|
166
|
+
this.needDetector = new NeedDetector(options.signalNeedMappings ?? []);
|
|
167
|
+
this.decisionEngine = new DecisionEngine(this.capabilities);
|
|
168
|
+
this.allocator = new ResourceAllocator(this.events, this.id, () => this.clock.now());
|
|
169
|
+
this.forecaster = new UtilityForecaster(this.events, this.id);
|
|
170
|
+
this.utilityEngine = new UtilityEngine(this.events, this.id, {
|
|
171
|
+
...DEFAULT_UTILITY_WEIGHTS,
|
|
172
|
+
...options.utilityWeights,
|
|
173
|
+
});
|
|
174
|
+
this.reputationEngine = new ReputationEngine(this.events, this.id);
|
|
175
|
+
this.survivalEngine = new SurvivalEngine(
|
|
176
|
+
{ ...DEFAULT_SURVIVAL_CONFIG, ...options.survival },
|
|
177
|
+
() => this.clock.now()
|
|
178
|
+
);
|
|
179
|
+
this.reproductionConfig = {
|
|
180
|
+
...DEFAULT_REPRODUCTION_CONFIG,
|
|
181
|
+
...options.reproduction,
|
|
182
|
+
};
|
|
183
|
+
this.marketEngine = new MarketEngine(
|
|
184
|
+
this.events,
|
|
185
|
+
this.allocator,
|
|
186
|
+
this.forecaster,
|
|
187
|
+
this.id,
|
|
188
|
+
() => this.clock.now(),
|
|
189
|
+
options.market
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
this.agentRuntime =
|
|
193
|
+
options.agentRuntime ??
|
|
194
|
+
new AgentRuntime({
|
|
195
|
+
llmProvider: options.llmProvider ?? createMockLlmProvider(),
|
|
196
|
+
enableMetrics: true,
|
|
197
|
+
enableRetry: false,
|
|
198
|
+
enableCircuitBreaker: false,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static async create(options: CreateOrganismOptions): Promise<OrganismRuntime> {
|
|
203
|
+
const runtime = new OrganismRuntime(options);
|
|
204
|
+
await runtime.initialize(options);
|
|
205
|
+
return runtime;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Resolve decorator-decorated organism class into create options. */
|
|
209
|
+
static async fromClass(
|
|
210
|
+
target: Newable,
|
|
211
|
+
overrides: Partial<CreateOrganismOptions> = {}
|
|
212
|
+
): Promise<OrganismRuntime> {
|
|
213
|
+
const meta = (await import('../decorators')).getOrganismMetadata(target);
|
|
214
|
+
if (!meta) {
|
|
215
|
+
throw new OrganismStateError(`Class ${target.name} is not decorated with @Organism`);
|
|
216
|
+
}
|
|
217
|
+
const opts = OrganismRuntime.mergeDecoratorOptions(meta, overrides);
|
|
218
|
+
return OrganismRuntime.create(opts);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private static mergeDecoratorOptions(
|
|
222
|
+
meta: OrganismDecoratorOptions,
|
|
223
|
+
overrides: Partial<CreateOrganismOptions>
|
|
224
|
+
): CreateOrganismOptions {
|
|
225
|
+
if (!meta.mission && !overrides.mission) {
|
|
226
|
+
throw new OrganismStateError('@Organism requires a mission');
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
mission: overrides.mission ?? (meta.mission as MissionDefinition | Newable),
|
|
230
|
+
genes: overrides.genes ?? meta.genes,
|
|
231
|
+
environment: overrides.environment ?? meta.environment,
|
|
232
|
+
constitution: overrides.constitution ?? meta.constitution,
|
|
233
|
+
resources: overrides.resources ?? meta.resources,
|
|
234
|
+
id: overrides.id ?? meta.id,
|
|
235
|
+
...overrides,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async initialize(options: CreateOrganismOptions): Promise<void> {
|
|
240
|
+
const existing = options.id ? await this.repo.getOrganism(this.id) : undefined;
|
|
241
|
+
|
|
242
|
+
if (existing) {
|
|
243
|
+
this.record = normalizeOrganismRecord(existing);
|
|
244
|
+
this.wireEngines(this.record.constitution, options);
|
|
245
|
+
await this.hydrateAgentsFromRepository();
|
|
246
|
+
await this.events.emit(OrganismEventType.ORGANISM_RESTORED, this.id, {
|
|
247
|
+
missionId: this.record.mission.id,
|
|
248
|
+
objective: this.record.mission.objective,
|
|
249
|
+
agentCount: this.agentCache.size,
|
|
250
|
+
});
|
|
251
|
+
this.log(`[ORGANISM] Restored ${this.id} with ${this.agentCache.size} agents`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const mission = resolveMission(options.mission);
|
|
256
|
+
const genes = (options.genes ?? []).map(resolveGene);
|
|
257
|
+
const environment = options.environment ? resolveEnvironment(options.environment) : undefined;
|
|
258
|
+
const constitution = options.constitution
|
|
259
|
+
? resolveConstitution(options.constitution)
|
|
260
|
+
: undefined;
|
|
261
|
+
const resources = options.resources
|
|
262
|
+
? resolveResource(options.resources)
|
|
263
|
+
: ({
|
|
264
|
+
tokenBudget: 5_000_000,
|
|
265
|
+
monthlyBudget: { amount: 1000, currency: 'USD' },
|
|
266
|
+
} as ResourceDefinition);
|
|
267
|
+
|
|
268
|
+
const limits: OrganismLimits = { ...DEFAULT_ORGANISM_LIMITS, ...options.limits };
|
|
269
|
+
const now = this.clock.now();
|
|
270
|
+
|
|
271
|
+
this.record = {
|
|
272
|
+
id: this.id,
|
|
273
|
+
status: 'created',
|
|
274
|
+
mission,
|
|
275
|
+
constitution,
|
|
276
|
+
environment,
|
|
277
|
+
genes,
|
|
278
|
+
resources,
|
|
279
|
+
limits,
|
|
280
|
+
cycles: options.cycles ?? {
|
|
281
|
+
perception: '5s',
|
|
282
|
+
evaluation: '1m',
|
|
283
|
+
survival: '10m',
|
|
284
|
+
missionEvaluation: '1m',
|
|
285
|
+
},
|
|
286
|
+
missionProgress: createMissionProgress(mission),
|
|
287
|
+
pool: {
|
|
288
|
+
tokensRemaining: resources.tokenBudget ?? 5_000_000,
|
|
289
|
+
moneyRemaining: {
|
|
290
|
+
amount: resources.monthlyBudget?.amount ?? 1000,
|
|
291
|
+
currency: resources.monthlyBudget?.currency ?? 'USD',
|
|
292
|
+
},
|
|
293
|
+
costSpentThisHour: 0,
|
|
294
|
+
},
|
|
295
|
+
debug: this.debug,
|
|
296
|
+
simulation: options.simulation ?? false,
|
|
297
|
+
createdAt: now,
|
|
298
|
+
updatedAt: now,
|
|
299
|
+
emergencyStopped: false,
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
this.wireEngines(constitution, options);
|
|
303
|
+
|
|
304
|
+
await this.repo.saveOrganism(this.record);
|
|
305
|
+
await this.events.emit(OrganismEventType.ORGANISM_CREATED, this.id, {
|
|
306
|
+
missionId: mission.id,
|
|
307
|
+
objective: mission.objective,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private wireEngines(
|
|
312
|
+
constitution: ConstitutionDefinition | undefined,
|
|
313
|
+
options: CreateOrganismOptions
|
|
314
|
+
): void {
|
|
315
|
+
this.constitutionEnforcer = new ConstitutionEnforcer(constitution, this.events, this.id);
|
|
316
|
+
const policyRules = this.constitutionEnforcer.toPolicyRules();
|
|
317
|
+
if (policyRules.length) {
|
|
318
|
+
this.agentRuntime = new AgentRuntime({
|
|
319
|
+
llmProvider: options.llmProvider ?? createMockLlmProvider(),
|
|
320
|
+
enableMetrics: true,
|
|
321
|
+
enableRetry: false,
|
|
322
|
+
enableCircuitBreaker: false,
|
|
323
|
+
policyEngine: new PolicyEngine(policyRules),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.birthEngine = new BirthEngine(
|
|
328
|
+
this.events,
|
|
329
|
+
this.repo,
|
|
330
|
+
this.capabilities,
|
|
331
|
+
this.id,
|
|
332
|
+
this.agentRuntime
|
|
333
|
+
);
|
|
334
|
+
this.terminationEngine = new TerminationEngine(
|
|
335
|
+
this.events,
|
|
336
|
+
this.repo,
|
|
337
|
+
this.capabilities,
|
|
338
|
+
this.id,
|
|
339
|
+
(agent) => this.allocator.release(this.record, agent)
|
|
340
|
+
);
|
|
341
|
+
this.genealogyManager = new GenealogyManager(this.repo, this.id);
|
|
342
|
+
this.reproductionEngine = new ReproductionEngine(
|
|
343
|
+
this.events,
|
|
344
|
+
this.repo,
|
|
345
|
+
this.birthEngine,
|
|
346
|
+
this.genealogyManager,
|
|
347
|
+
this.constitutionEnforcer,
|
|
348
|
+
this.id,
|
|
349
|
+
() => this.clock.now(),
|
|
350
|
+
this.reproductionConfig
|
|
351
|
+
);
|
|
352
|
+
this.mutationEngine = new MutationEngine(this.events, this.repo, this.id, () =>
|
|
353
|
+
this.clock.now()
|
|
354
|
+
);
|
|
355
|
+
this.generationManager = new GenerationManager();
|
|
356
|
+
this.evolutionEngine = new EvolutionEngine(
|
|
357
|
+
this.events,
|
|
358
|
+
this.repo,
|
|
359
|
+
this.generationManager,
|
|
360
|
+
this.mutationEngine,
|
|
361
|
+
this.id,
|
|
362
|
+
{ ...DEFAULT_UTILITY_WEIGHTS },
|
|
363
|
+
() => this.clock.now()
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Rebuild agent cache + capability index from durable repository (multi-replica hydrate). */
|
|
368
|
+
private async hydrateAgentsFromRepository(): Promise<void> {
|
|
369
|
+
const agents = await this.repo.listAgents(this.id);
|
|
370
|
+
for (const raw of agents) {
|
|
371
|
+
const agent = normalizeAgentRecord(raw);
|
|
372
|
+
this.agentCache.set(agent.id, agent);
|
|
373
|
+
this.capabilities.register(agent.id, agent.capabilities);
|
|
374
|
+
this.reregisterAgentDna(agent);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private reregisterAgentDna(agent: RuntimeAgentRecord): void {
|
|
379
|
+
if (!this.agentRuntime || !agent.dnaAgentName) return;
|
|
380
|
+
try {
|
|
381
|
+
const dna = exportAgentDna({
|
|
382
|
+
name: agent.dnaAgentName,
|
|
383
|
+
description: agent.objective,
|
|
384
|
+
systemPrompt:
|
|
385
|
+
agent.systemPrompt ??
|
|
386
|
+
`You are ${agent.dnaAgentName}. Objective: ${agent.objective}. Capabilities: ${agent.capabilities.join(', ')}.`,
|
|
387
|
+
tools: [],
|
|
388
|
+
mission: { goal: agent.objective },
|
|
389
|
+
metadata: {
|
|
390
|
+
capabilities: agent.capabilities,
|
|
391
|
+
organismId: this.id,
|
|
392
|
+
geneId: agent.geneId,
|
|
393
|
+
needId: agent.birthProposal?.needId,
|
|
394
|
+
terminationCriteria: agent.terminationCriteria,
|
|
395
|
+
},
|
|
396
|
+
version: '1.0.0',
|
|
397
|
+
});
|
|
398
|
+
const AgentClass = createAgentClassFromDna(dna);
|
|
399
|
+
this.agentRuntime.registerAgent(AgentClass);
|
|
400
|
+
} catch {
|
|
401
|
+
// DNA re-registration is best-effort; observe/spawn still work via capabilities.
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
get status(): OrganismRecord['status'] {
|
|
406
|
+
return this.record.status;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
getMission(): MissionDefinition {
|
|
410
|
+
return this.record.mission;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async start(): Promise<void> {
|
|
414
|
+
if (this.record.emergencyStopped) {
|
|
415
|
+
throw new OrganismStateError('Cannot start after emergency stop');
|
|
416
|
+
}
|
|
417
|
+
this.record.status = 'initializing';
|
|
418
|
+
await this.persist();
|
|
419
|
+
this.record.status = 'operating';
|
|
420
|
+
await this.persist();
|
|
421
|
+
this.startCycles();
|
|
422
|
+
await this.events.emit(OrganismEventType.ORGANISM_STARTED, this.id, {
|
|
423
|
+
status: this.record.status,
|
|
424
|
+
});
|
|
425
|
+
this.log(`[ORGANISM] Mission started: ${this.record.mission.objective}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async pause(): Promise<void> {
|
|
429
|
+
this.stopCycles();
|
|
430
|
+
this.record.status = 'paused';
|
|
431
|
+
await this.persist();
|
|
432
|
+
await this.events.emit(OrganismEventType.ORGANISM_PAUSED, this.id, {});
|
|
433
|
+
this.log('[ORGANISM] Paused');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async resume(): Promise<void> {
|
|
437
|
+
if (this.record.emergencyStopped) {
|
|
438
|
+
throw new OrganismStateError('Cannot resume after emergency stop');
|
|
439
|
+
}
|
|
440
|
+
this.record.status = 'operating';
|
|
441
|
+
await this.persist();
|
|
442
|
+
this.startCycles();
|
|
443
|
+
await this.events.emit(OrganismEventType.ORGANISM_RESUMED, this.id, {});
|
|
444
|
+
this.log('[ORGANISM] Resumed');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async terminate(): Promise<void> {
|
|
448
|
+
this.stopCycles();
|
|
449
|
+
const agents = await this.liveAgents();
|
|
450
|
+
for (const agent of agents) {
|
|
451
|
+
await this.terminationEngine.terminate(agent.id, 'manual');
|
|
452
|
+
this.metrics.recordTermination();
|
|
453
|
+
}
|
|
454
|
+
this.record.status = 'terminated';
|
|
455
|
+
await this.persist();
|
|
456
|
+
await this.events.emit(OrganismEventType.ORGANISM_TERMINATED, this.id, {});
|
|
457
|
+
this.log('[ORGANISM] Terminated');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async emergencyStop(): Promise<void> {
|
|
461
|
+
this.record.emergencyStopped = true;
|
|
462
|
+
this.stopCycles();
|
|
463
|
+
this.record.status = 'terminated';
|
|
464
|
+
await this.persist();
|
|
465
|
+
await this.events.emit(OrganismEventType.ORGANISM_EMERGENCY_STOP, this.id, {});
|
|
466
|
+
this.log('[ORGANISM] Emergency stop');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async observe(
|
|
470
|
+
raw: Partial<EnvironmentSignal> & Pick<EnvironmentSignal, 'type' | 'source'>
|
|
471
|
+
): Promise<OrganismDecision | undefined> {
|
|
472
|
+
if (this.record.status === 'paused' || this.record.emergencyStopped) {
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
this.record.status = 'observing';
|
|
476
|
+
const signal = this.perception.normalize({
|
|
477
|
+
...raw,
|
|
478
|
+
timestamp: raw.timestamp ?? this.clock.now(),
|
|
479
|
+
});
|
|
480
|
+
const filtered = this.perception.filter(signal, this.record.mission);
|
|
481
|
+
await this.repo.appendSignal(this.id, filtered.signal);
|
|
482
|
+
await this.events.emit(OrganismEventType.ENVIRONMENT_SIGNAL_RECEIVED, this.id, filtered.signal);
|
|
483
|
+
this.log(`[ENVIRONMENT] ${signal.type} ${filtered.accepted ? 'accepted' : 'ignored'}`);
|
|
484
|
+
|
|
485
|
+
if (!filtered.accepted) {
|
|
486
|
+
this.record.status = 'operating';
|
|
487
|
+
await this.persist();
|
|
488
|
+
return {
|
|
489
|
+
action: 'observe',
|
|
490
|
+
reasoningSummary: filtered.reason,
|
|
491
|
+
confidence: 1,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const need = this.needDetector.detect(filtered.signal, this.record.mission);
|
|
496
|
+
if (!need) {
|
|
497
|
+
this.record.status = 'operating';
|
|
498
|
+
await this.persist();
|
|
499
|
+
return {
|
|
500
|
+
action: 'observe',
|
|
501
|
+
reasoningSummary: `No need detected for ${signal.type}`,
|
|
502
|
+
confidence: 0.7,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
await this.events.emit(OrganismEventType.ORGANISM_NEED_DETECTED, this.id, need);
|
|
507
|
+
this.log(`[NEED] ${need.need} — ${need.reason}`);
|
|
508
|
+
|
|
509
|
+
this.record.status = 'planning';
|
|
510
|
+
const live = await this.liveAgents();
|
|
511
|
+
const spawnCheck = this.allocator.canSpawn(this.record, live.length);
|
|
512
|
+
const decision = this.decisionEngine.decide({
|
|
513
|
+
need,
|
|
514
|
+
liveAgents: live,
|
|
515
|
+
genes: this.record.genes,
|
|
516
|
+
canSpawn: spawnCheck.ok,
|
|
517
|
+
spawnBlockReason: spawnCheck.reason,
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
await this.repo.appendDecision(this.id, decision);
|
|
521
|
+
await this.events.emit(OrganismEventType.ORGANISM_DECISION, this.id, decision);
|
|
522
|
+
if (this.debug) {
|
|
523
|
+
this.log(
|
|
524
|
+
`[DECISION] ${decision.action}: ${decision.reasoningSummary} (confidence=${decision.confidence})`
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await this.applyDecision(decision);
|
|
529
|
+
this.record.status = 'operating';
|
|
530
|
+
await this.persist();
|
|
531
|
+
return decision;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
private async applyDecision(decision: OrganismDecision): Promise<void> {
|
|
535
|
+
if (decision.action === 'delegate' && decision.targetAgentId) {
|
|
536
|
+
await this.delegateInternal(decision.targetAgentId, decision.needId ?? 'task');
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (
|
|
541
|
+
(decision.action === 'spawn' || decision.action === 'specialize') &&
|
|
542
|
+
decision.birthProposal
|
|
543
|
+
) {
|
|
544
|
+
await this.spawnFromProposal(decision);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (decision.action === 'reproduce' && decision.birthProposal?.parentAgentId) {
|
|
549
|
+
await this.reproduceAgent(decision.birthProposal.parentAgentId, {
|
|
550
|
+
reason: decision.birthProposal.reason,
|
|
551
|
+
specialization: decision.birthProposal.specialize,
|
|
552
|
+
objective: decision.birthProposal.expectedOutcome,
|
|
553
|
+
needId: decision.birthProposal.needId,
|
|
554
|
+
confidence: decision.confidence,
|
|
555
|
+
tokens: decision.resourceRequest?.tokens,
|
|
556
|
+
terminationCriteria: decision.birthProposal.terminationCriteria,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private async spawnFromProposal(decision: OrganismDecision): Promise<RuntimeAgentRecord> {
|
|
562
|
+
const proposal = decision.birthProposal!;
|
|
563
|
+
this.constitutionEnforcer.assertAllows('spawn', {});
|
|
564
|
+
|
|
565
|
+
const live = await this.liveAgents();
|
|
566
|
+
const spawnCheck = this.allocator.canSpawn(this.record, live.length);
|
|
567
|
+
if (!spawnCheck.ok) {
|
|
568
|
+
throw new OrganismStateError(spawnCheck.reason);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const geneId = proposal.geneId ?? this.record.genes[0]?.id;
|
|
572
|
+
const gene = this.record.genes.find((g) => g.id === geneId);
|
|
573
|
+
if (!gene) {
|
|
574
|
+
throw new OrganismStateError(`Gene not found: ${geneId}`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const agentId = `pending_${Date.now()}`;
|
|
578
|
+
const allocation = this.allocator.allocate(this.record, {
|
|
579
|
+
agentId,
|
|
580
|
+
requestedResources: decision.resourceRequest ?? { tokens: 50_000 },
|
|
581
|
+
expectedUtility: proposal.expectedUtility ?? 0.5,
|
|
582
|
+
confidence: proposal.confidence,
|
|
583
|
+
urgency: 0.7,
|
|
584
|
+
});
|
|
585
|
+
if (!allocation.approved) {
|
|
586
|
+
await this.events.emit(OrganismEventType.ORGANISM_RESOURCE_DENIED, this.id, allocation);
|
|
587
|
+
throw new OrganismStateError(allocation.reason);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
this.allocator.recordSpawn();
|
|
591
|
+
const parentGeneration = proposal.parentAgentId
|
|
592
|
+
? ((await this.repo.getAgent(this.id, proposal.parentAgentId))?.generation ?? 0)
|
|
593
|
+
: 0;
|
|
594
|
+
const generation = parentGeneration + 1;
|
|
595
|
+
if (generation > this.record.limits.maxGenerationDepth) {
|
|
596
|
+
this.record.pool.tokensRemaining += allocation.wallet.tokensRemaining ?? 0;
|
|
597
|
+
this.record.pool.moneyRemaining.amount += allocation.wallet.moneyRemaining?.amount ?? 0;
|
|
598
|
+
throw new OrganismStateError(
|
|
599
|
+
`maxGenerationDepth (${this.record.limits.maxGenerationDepth}) exceeded`
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const agent = await this.birthEngine.spawn({
|
|
604
|
+
proposal,
|
|
605
|
+
gene,
|
|
606
|
+
wallet: allocation.wallet,
|
|
607
|
+
generation,
|
|
608
|
+
parentAgentId: proposal.parentAgentId,
|
|
609
|
+
});
|
|
610
|
+
this.agentCache.set(agent.id, agent);
|
|
611
|
+
|
|
612
|
+
this.metrics.recordSpawn();
|
|
613
|
+
this.metrics.recordTokens(allocation.wallet.tokensRemaining ?? 0);
|
|
614
|
+
this.log(
|
|
615
|
+
`[BIRTH] ${agent.name}#${agent.id} born parent: ${agent.parentAgentId ?? agent.geneId} generation: ${agent.generation}`
|
|
616
|
+
);
|
|
617
|
+
this.log(
|
|
618
|
+
`[RESOURCE] allocated ${(allocation.wallet.tokensRemaining ?? 0).toLocaleString()} tokens`
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
if (decision.action === 'specialize') {
|
|
622
|
+
await this.events.emit(OrganismEventType.ORGANISM_AGENT_SPECIALIZED, this.id, {
|
|
623
|
+
agentId: agent.id,
|
|
624
|
+
specialize: agent.specialize,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
await this.persist();
|
|
629
|
+
return agent;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** Public spawn API for explicit births (still requires structured proposal). */
|
|
633
|
+
async spawnAgent(input: {
|
|
634
|
+
reason: string;
|
|
635
|
+
objective: string;
|
|
636
|
+
needId: string;
|
|
637
|
+
requiredCapabilities: string[];
|
|
638
|
+
parentAgentId?: string;
|
|
639
|
+
specialize?: string[];
|
|
640
|
+
geneId?: string;
|
|
641
|
+
confidence?: number;
|
|
642
|
+
terminationCriteria?: string[];
|
|
643
|
+
tokens?: number;
|
|
644
|
+
}): Promise<RuntimeAgentRecord> {
|
|
645
|
+
const gene =
|
|
646
|
+
this.record.genes.find((g) => g.id === input.geneId) ??
|
|
647
|
+
this.decisionEngine.selectGene(input.requiredCapabilities, this.record.genes);
|
|
648
|
+
if (!gene) throw new OrganismStateError('No gene available for spawn');
|
|
649
|
+
|
|
650
|
+
const decision: OrganismDecision = {
|
|
651
|
+
action: input.specialize?.length ? 'specialize' : 'spawn',
|
|
652
|
+
reasoningSummary: input.reason,
|
|
653
|
+
confidence: input.confidence ?? 0.8,
|
|
654
|
+
resourceRequest: { tokens: input.tokens ?? 50_000 },
|
|
655
|
+
birthProposal: {
|
|
656
|
+
reason: input.reason,
|
|
657
|
+
needId: input.needId,
|
|
658
|
+
requiredCapabilities: input.requiredCapabilities,
|
|
659
|
+
expectedOutcome: input.objective,
|
|
660
|
+
confidence: input.confidence ?? 0.8,
|
|
661
|
+
geneId: gene.id,
|
|
662
|
+
specialize: input.specialize,
|
|
663
|
+
parentAgentId: input.parentAgentId,
|
|
664
|
+
terminationCriteria: input.terminationCriteria,
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
await this.repo.appendDecision(this.id, decision);
|
|
668
|
+
return this.spawnFromProposal(decision);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
async terminateAgent(
|
|
672
|
+
agentId: string,
|
|
673
|
+
opts: { reason: string } = { reason: 'manual' }
|
|
674
|
+
): Promise<void> {
|
|
675
|
+
const agent = await this.terminationEngine.terminate(agentId, opts.reason);
|
|
676
|
+
if (agent) {
|
|
677
|
+
this.metrics.recordTermination();
|
|
678
|
+
this.log(`[DEATH] ${agent.name}#${agent.id} terminated (${opts.reason})`);
|
|
679
|
+
if ((agent.wallet.tokensRemaining ?? 0) === 0) {
|
|
680
|
+
this.log(`[RESOURCE] unused tokens returned to pool`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
await this.persist();
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async reportOutcome(agentId: string, report: AgentOutcomeReport): Promise<void> {
|
|
687
|
+
const agent = await this.repo.getAgent(this.id, agentId);
|
|
688
|
+
if (!agent) throw new OrganismStateError(`Agent not found: ${agentId}`);
|
|
689
|
+
|
|
690
|
+
agent.valueGenerated += report.metrics?.valueGenerated ?? 0;
|
|
691
|
+
agent.costConsumed += report.metrics?.cost ?? 0;
|
|
692
|
+
agent.tokensConsumed += report.metrics?.cost
|
|
693
|
+
? Math.floor((report.metrics.cost ?? 0) * 1000)
|
|
694
|
+
: 0;
|
|
695
|
+
agent.reputation = this.reputationEngine.update(agent, report);
|
|
696
|
+
agent.utility = this.utilityEngine.evaluate(agent, report);
|
|
697
|
+
agent.evaluationCount += 1;
|
|
698
|
+
agent.lastEvaluatedAt = this.clock.now();
|
|
699
|
+
await this.repo.saveAgent(this.id, agent);
|
|
700
|
+
this.agentCache.set(agent.id, agent);
|
|
701
|
+
|
|
702
|
+
if (report.missionMetricUpdates) {
|
|
703
|
+
this.record.missionProgress = applyMissionMetricUpdates(
|
|
704
|
+
this.record.missionProgress,
|
|
705
|
+
this.record.mission,
|
|
706
|
+
report.missionMetricUpdates
|
|
707
|
+
);
|
|
708
|
+
await this.events.emit(
|
|
709
|
+
OrganismEventType.ORGANISM_MISSION_PROGRESS,
|
|
710
|
+
this.id,
|
|
711
|
+
this.record.missionProgress
|
|
712
|
+
);
|
|
713
|
+
if (this.record.missionProgress.completed) {
|
|
714
|
+
this.record.status = 'completed';
|
|
715
|
+
await this.events.emit(
|
|
716
|
+
OrganismEventType.ORGANISM_MISSION_COMPLETED,
|
|
717
|
+
this.id,
|
|
718
|
+
this.record.missionProgress
|
|
719
|
+
);
|
|
720
|
+
this.log('[MISSION] Mission completed');
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
this.log(`[RESULT] ${report.result}`);
|
|
725
|
+
await this.persist();
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async runSurvivalCycle(): Promise<SurvivalReport[]> {
|
|
729
|
+
const agents = await this.liveAgents();
|
|
730
|
+
const reports: SurvivalReport[] = [];
|
|
731
|
+
for (const agent of agents) {
|
|
732
|
+
const verdict = this.survivalEngine.evaluate(agent);
|
|
733
|
+
reports.push(verdict);
|
|
734
|
+
if (verdict.shouldTerminate) {
|
|
735
|
+
this.log(`[SURVIVAL] ${agent.name}#${agent.id} ${verdict.reason}`);
|
|
736
|
+
await this.terminateAgent(agent.id, { reason: 'low_value' });
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return reports;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
async inspect(): Promise<OrganismInspectState> {
|
|
743
|
+
const agents = await this.repo.listAgents(this.id);
|
|
744
|
+
return {
|
|
745
|
+
mission: this.record.mission,
|
|
746
|
+
missionProgress: this.record.missionProgress,
|
|
747
|
+
status: this.record.status,
|
|
748
|
+
agents: agents.map((a) => ({
|
|
749
|
+
id: a.id,
|
|
750
|
+
objective: a.objective,
|
|
751
|
+
capabilities: a.capabilities,
|
|
752
|
+
parentId: a.parentAgentId,
|
|
753
|
+
generation: a.generation,
|
|
754
|
+
status: a.status,
|
|
755
|
+
reputation: a.reputation,
|
|
756
|
+
utility: a.utility,
|
|
757
|
+
resourceConsumption: { tokens: a.tokensConsumed, cost: a.costConsumed },
|
|
758
|
+
})),
|
|
759
|
+
resources: {
|
|
760
|
+
pool: this.record.pool,
|
|
761
|
+
limits: this.record.limits,
|
|
762
|
+
},
|
|
763
|
+
recentSignals: await this.repo.listSignals(this.id, 20),
|
|
764
|
+
recentDecisions: await this.repo.listDecisions(this.id, 20),
|
|
765
|
+
genealogy: await this.repo.listGenealogy(this.id),
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
async getGenealogy(): Promise<AgentGenealogy[]> {
|
|
770
|
+
return this.repo.listGenealogy(this.id);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
async formatGenealogy(): Promise<string> {
|
|
774
|
+
const [agents, genealogy] = await Promise.all([
|
|
775
|
+
this.repo.listAgents(this.id),
|
|
776
|
+
this.genealogyManager.list(),
|
|
777
|
+
]);
|
|
778
|
+
return this.genealogyManager.formatTree(agents, genealogy);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Phase 2 — create a specialized descendant from an existing agent.
|
|
783
|
+
*/
|
|
784
|
+
async reproduceAgent(
|
|
785
|
+
parentAgentId: string,
|
|
786
|
+
request: ReproduceRequest
|
|
787
|
+
): Promise<RuntimeAgentRecord> {
|
|
788
|
+
const parent = await this.repo.getAgent(this.id, parentAgentId);
|
|
789
|
+
if (!parent) throw new OrganismStateError(`Parent agent not found: ${parentAgentId}`);
|
|
790
|
+
|
|
791
|
+
const gene = this.record.genes.find((g) => g.id === parent.geneId);
|
|
792
|
+
if (!gene) throw new OrganismStateError(`Gene not found for parent: ${parent.geneId}`);
|
|
793
|
+
|
|
794
|
+
const live = await this.liveAgents();
|
|
795
|
+
const spawnCheck = this.allocator.canSpawn(this.record, live.length);
|
|
796
|
+
if (!spawnCheck.ok) {
|
|
797
|
+
throw new OrganismStateError(spawnCheck.reason);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Prefer parent wallet transfer; optionally top up from pool if parent has no tokens
|
|
801
|
+
const parentTokens = parent.wallet.tokensRemaining ?? 0;
|
|
802
|
+
let poolWallet: import('../types/organism.types').AgentResourceWallet | undefined;
|
|
803
|
+
if (parentTokens <= 0 || request.tokens) {
|
|
804
|
+
const allocation = this.allocator.allocate(this.record, {
|
|
805
|
+
agentId: `repro_${parentAgentId}`,
|
|
806
|
+
requestedResources: { tokens: request.tokens ?? 25_000 },
|
|
807
|
+
expectedUtility: parent.utility.score,
|
|
808
|
+
confidence: request.confidence ?? 0.8,
|
|
809
|
+
urgency: 0.6,
|
|
810
|
+
reputationScore: parent.reputation.score,
|
|
811
|
+
});
|
|
812
|
+
if (!allocation.approved) {
|
|
813
|
+
throw new OrganismStateError(allocation.reason);
|
|
814
|
+
}
|
|
815
|
+
poolWallet = allocation.wallet;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
this.allocator.recordSpawn();
|
|
819
|
+
const { child, parent: updatedParent } = await this.reproductionEngine.reproduce({
|
|
820
|
+
parent,
|
|
821
|
+
gene,
|
|
822
|
+
request,
|
|
823
|
+
limits: this.record.limits,
|
|
824
|
+
liveAgents: live,
|
|
825
|
+
poolWallet,
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
this.agentCache.set(child.id, child);
|
|
829
|
+
this.agentCache.set(updatedParent.id, updatedParent);
|
|
830
|
+
this.capabilities.register(child.id, child.capabilities);
|
|
831
|
+
this.metrics.recordReproduction();
|
|
832
|
+
this.metrics.recordTokens(child.wallet.tokensRemaining ?? 0);
|
|
833
|
+
|
|
834
|
+
this.log(
|
|
835
|
+
`[REPRODUCTION] ${child.name}#${child.id} born parent: ${parent.name}#${parent.id} generation: ${child.generation}`
|
|
836
|
+
);
|
|
837
|
+
await this.persist();
|
|
838
|
+
return child;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Phase 3 — apply a constrained, auditable mutation.
|
|
843
|
+
*/
|
|
844
|
+
async mutateAgent(
|
|
845
|
+
agentId: string,
|
|
846
|
+
input: { reason: string; mutation: AgentMutation }
|
|
847
|
+
): Promise<RuntimeAgentRecord> {
|
|
848
|
+
const agent = await this.repo.getAgent(this.id, agentId);
|
|
849
|
+
if (!agent) throw new OrganismStateError(`Agent not found: ${agentId}`);
|
|
850
|
+
const gene = this.record.genes.find((g) => g.id === agent.geneId);
|
|
851
|
+
if (!gene) throw new OrganismStateError(`Gene not found: ${agent.geneId}`);
|
|
852
|
+
|
|
853
|
+
this.constitutionEnforcer.assertAllows('mutate', {});
|
|
854
|
+
const updated = await this.mutationEngine.mutate(agent, input, gene);
|
|
855
|
+
this.capabilities.register(updated.id, updated.capabilities);
|
|
856
|
+
this.agentCache.set(updated.id, updated);
|
|
857
|
+
this.metrics.recordMutation();
|
|
858
|
+
this.log(`[MUTATION] ${updated.name}#${updated.id}: ${input.reason}`);
|
|
859
|
+
await this.persist();
|
|
860
|
+
return updated;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Phase 3 — evaluate a competing population and promote the winner's strategy id.
|
|
865
|
+
*/
|
|
866
|
+
async evaluateGeneration(input: {
|
|
867
|
+
population: string[];
|
|
868
|
+
populationId?: string;
|
|
869
|
+
promoteToLosers?: boolean;
|
|
870
|
+
}): Promise<GenerationEvaluationResult> {
|
|
871
|
+
const result = await this.evolutionEngine.evaluateGeneration(input);
|
|
872
|
+
this.log(
|
|
873
|
+
`[EVOLUTION] population=${result.populationId} winner=${result.winner} scores=${JSON.stringify(result.scores)}`
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
if (input.promoteToLosers && result.winner) {
|
|
877
|
+
const winner = await this.repo.getAgent(this.id, result.winner);
|
|
878
|
+
const gene = winner ? this.record.genes.find((g) => g.id === winner.geneId) : undefined;
|
|
879
|
+
if (winner && gene) {
|
|
880
|
+
for (const member of result.members) {
|
|
881
|
+
if (member.agentId === result.winner) continue;
|
|
882
|
+
await this.evolutionEngine.promoteStrategy({
|
|
883
|
+
winnerId: result.winner,
|
|
884
|
+
targetId: member.agentId,
|
|
885
|
+
gene,
|
|
886
|
+
});
|
|
887
|
+
this.metrics.recordMutation();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
await this.persist();
|
|
893
|
+
return result;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
getEvolutionaryHistory(limit = 50): import('../types/organism.types').EvolutionaryHistoryEntry[] {
|
|
897
|
+
return this.generationManager.getHistory(limit);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** Phase 4 — forecast expected utility / opportunity cost for a resource request. */
|
|
901
|
+
async forecastUtility(input: {
|
|
902
|
+
agentId: string;
|
|
903
|
+
requested: ResourceRequest;
|
|
904
|
+
expectedValue: number;
|
|
905
|
+
confidence: number;
|
|
906
|
+
urgency?: number;
|
|
907
|
+
}): Promise<UtilityForecast> {
|
|
908
|
+
const agent = await this.repo.getAgent(this.id, input.agentId);
|
|
909
|
+
if (!agent) throw new OrganismStateError(`Agent not found: ${input.agentId}`);
|
|
910
|
+
return this.forecaster.forecast({
|
|
911
|
+
agent,
|
|
912
|
+
record: this.record,
|
|
913
|
+
requested: input.requested,
|
|
914
|
+
expectedValue: input.expectedValue,
|
|
915
|
+
confidence: input.confidence,
|
|
916
|
+
urgency: input.urgency,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/** Phase 4 — place a resource bid into the internal market. */
|
|
921
|
+
placeBid(input: {
|
|
922
|
+
agentId: string;
|
|
923
|
+
reason: string;
|
|
924
|
+
requested: ResourceRequest;
|
|
925
|
+
expectedValue: number;
|
|
926
|
+
confidence: number;
|
|
927
|
+
urgency?: number;
|
|
928
|
+
bidPrice?: number;
|
|
929
|
+
ttlMs?: number;
|
|
930
|
+
}): ResourceBid {
|
|
931
|
+
const bid = this.marketEngine.placeBid(input);
|
|
932
|
+
this.log(
|
|
933
|
+
`[BID] ${bid.id} agent=${bid.agentId} value=${bid.expectedValue} tokens=${bid.requested.tokens ?? 0}`
|
|
934
|
+
);
|
|
935
|
+
return bid;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
listOpenBids(): ResourceBid[] {
|
|
939
|
+
return this.marketEngine.listOpenBids();
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/** Phase 4 — clear open bids against the organism pool. */
|
|
943
|
+
async clearMarket(): Promise<MarketClearingResult> {
|
|
944
|
+
const agents = await this.repo.listAgents(this.id);
|
|
945
|
+
const map = new Map(agents.map((a) => [a.id, a]));
|
|
946
|
+
const result = this.marketEngine.clearMarket({ record: this.record, agents: map });
|
|
947
|
+
|
|
948
|
+
for (const win of result.awarded) {
|
|
949
|
+
const agent = map.get(win.agentId);
|
|
950
|
+
if (!agent) continue;
|
|
951
|
+
agent.wallet = {
|
|
952
|
+
tokensRemaining: (agent.wallet.tokensRemaining ?? 0) + (win.wallet.tokensRemaining ?? 0),
|
|
953
|
+
moneyRemaining: {
|
|
954
|
+
amount:
|
|
955
|
+
(agent.wallet.moneyRemaining?.amount ?? 0) + (win.wallet.moneyRemaining?.amount ?? 0),
|
|
956
|
+
currency: this.record.pool.moneyRemaining.currency,
|
|
957
|
+
},
|
|
958
|
+
};
|
|
959
|
+
await this.repo.saveAgent(this.id, agent);
|
|
960
|
+
this.agentCache.set(agent.id, agent);
|
|
961
|
+
this.metrics.recordTokens(win.wallet.tokensRemaining ?? 0);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Persist any bid-price charges on losers/winners wallets
|
|
965
|
+
for (const agent of map.values()) {
|
|
966
|
+
await this.repo.saveAgent(this.id, agent);
|
|
967
|
+
this.agentCache.set(agent.id, agent);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
this.log(
|
|
971
|
+
`[MARKET] cleared ${result.roundId}: awarded=${result.awarded.length} denied=${result.denied.length}`
|
|
972
|
+
);
|
|
973
|
+
await this.persist();
|
|
974
|
+
return result;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/** Phase 4 — peer resource negotiation between two agents. */
|
|
978
|
+
async negotiate(offer: NegotiationOffer): Promise<NegotiationResult> {
|
|
979
|
+
const from = await this.repo.getAgent(this.id, offer.fromAgentId);
|
|
980
|
+
const to = await this.repo.getAgent(this.id, offer.toAgentId);
|
|
981
|
+
if (!from || !to) {
|
|
982
|
+
return { approved: false, reason: 'Agent not found' };
|
|
983
|
+
}
|
|
984
|
+
const result = this.marketEngine.negotiate({
|
|
985
|
+
offer,
|
|
986
|
+
from,
|
|
987
|
+
to,
|
|
988
|
+
record: this.record,
|
|
989
|
+
});
|
|
990
|
+
if (result.approved) {
|
|
991
|
+
await this.repo.saveAgent(this.id, from);
|
|
992
|
+
await this.repo.saveAgent(this.id, to);
|
|
993
|
+
this.agentCache.set(from.id, from);
|
|
994
|
+
this.agentCache.set(to.id, to);
|
|
995
|
+
this.log(
|
|
996
|
+
`[NEGOTIATION] ${from.id} → ${to.id} tokens=${offer.transfer.tokens ?? 0} money=${offer.transfer.money ?? 0}`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
await this.persist();
|
|
1000
|
+
return result;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Phase 4 — negotiate-aware resource request (forecast first, then allocate).
|
|
1005
|
+
*/
|
|
1006
|
+
async requestResourcesForAgent(
|
|
1007
|
+
agentId: string,
|
|
1008
|
+
input: {
|
|
1009
|
+
reason: string;
|
|
1010
|
+
requested: ResourceRequest;
|
|
1011
|
+
expectedValue?: number;
|
|
1012
|
+
confidence?: number;
|
|
1013
|
+
urgency?: number;
|
|
1014
|
+
useMarket?: boolean;
|
|
1015
|
+
}
|
|
1016
|
+
): Promise<{
|
|
1017
|
+
approved: boolean;
|
|
1018
|
+
reason: string;
|
|
1019
|
+
wallet?: import('../types/organism.types').AgentResourceWallet;
|
|
1020
|
+
forecast?: UtilityForecast;
|
|
1021
|
+
}> {
|
|
1022
|
+
const agent = await this.repo.getAgent(this.id, agentId);
|
|
1023
|
+
if (!agent) return { approved: false, reason: `Agent not found: ${agentId}` };
|
|
1024
|
+
|
|
1025
|
+
const expectedValue = input.expectedValue ?? 100;
|
|
1026
|
+
const confidence = input.confidence ?? 0.5;
|
|
1027
|
+
const forecast = this.forecaster.forecast({
|
|
1028
|
+
agent,
|
|
1029
|
+
record: this.record,
|
|
1030
|
+
requested: input.requested,
|
|
1031
|
+
expectedValue,
|
|
1032
|
+
confidence,
|
|
1033
|
+
urgency: input.urgency,
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
if (input.useMarket) {
|
|
1037
|
+
this.placeBid({
|
|
1038
|
+
agentId,
|
|
1039
|
+
reason: input.reason,
|
|
1040
|
+
requested: input.requested,
|
|
1041
|
+
expectedValue,
|
|
1042
|
+
confidence,
|
|
1043
|
+
urgency: input.urgency,
|
|
1044
|
+
});
|
|
1045
|
+
const cleared = await this.clearMarket();
|
|
1046
|
+
const win = cleared.awarded.find((a) => a.agentId === agentId);
|
|
1047
|
+
if (win) {
|
|
1048
|
+
return {
|
|
1049
|
+
approved: true,
|
|
1050
|
+
reason: 'Won market clearing',
|
|
1051
|
+
wallet: win.wallet,
|
|
1052
|
+
forecast,
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
const denial = cleared.denied.find((d) => d.agentId === agentId);
|
|
1056
|
+
return {
|
|
1057
|
+
approved: false,
|
|
1058
|
+
reason: denial?.reason ?? 'Lost market clearing',
|
|
1059
|
+
forecast,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (forecast.netExpectedValue < 0 && expectedValue > 0) {
|
|
1064
|
+
return {
|
|
1065
|
+
approved: false,
|
|
1066
|
+
reason: `Forecast net value negative (${forecast.netExpectedValue.toFixed(2)})`,
|
|
1067
|
+
forecast,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const allocation = this.allocator.allocate(this.record, {
|
|
1072
|
+
agentId,
|
|
1073
|
+
requestedResources: input.requested,
|
|
1074
|
+
expectedUtility: forecast.expectedUtility,
|
|
1075
|
+
confidence,
|
|
1076
|
+
urgency: input.urgency ?? 0.5,
|
|
1077
|
+
reputationScore: agent.reputation.score,
|
|
1078
|
+
});
|
|
1079
|
+
if (allocation.approved) {
|
|
1080
|
+
agent.wallet = {
|
|
1081
|
+
tokensRemaining:
|
|
1082
|
+
(agent.wallet.tokensRemaining ?? 0) + (allocation.wallet.tokensRemaining ?? 0),
|
|
1083
|
+
moneyRemaining: {
|
|
1084
|
+
amount:
|
|
1085
|
+
(agent.wallet.moneyRemaining?.amount ?? 0) +
|
|
1086
|
+
(allocation.wallet.moneyRemaining?.amount ?? 0),
|
|
1087
|
+
currency: this.record.pool.moneyRemaining.currency,
|
|
1088
|
+
},
|
|
1089
|
+
};
|
|
1090
|
+
await this.repo.saveAgent(this.id, agent);
|
|
1091
|
+
this.agentCache.set(agent.id, agent);
|
|
1092
|
+
await this.persist();
|
|
1093
|
+
}
|
|
1094
|
+
return {
|
|
1095
|
+
approved: allocation.approved,
|
|
1096
|
+
reason: allocation.reason,
|
|
1097
|
+
wallet: allocation.wallet,
|
|
1098
|
+
forecast,
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
getMarketHistory(limit = 20): MarketClearingResult[] {
|
|
1103
|
+
return this.marketEngine.getHistory(limit);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
async getGraph(): Promise<OrganismGraph> {
|
|
1107
|
+
const agents = await this.repo.listAgents(this.id);
|
|
1108
|
+
const nodes = agents.map((a) => ({
|
|
1109
|
+
id: a.id,
|
|
1110
|
+
label: a.name,
|
|
1111
|
+
status: a.status,
|
|
1112
|
+
generation: a.generation,
|
|
1113
|
+
utility: a.utility.score,
|
|
1114
|
+
reputation: a.reputation.score,
|
|
1115
|
+
cost: a.costConsumed,
|
|
1116
|
+
tokens: a.tokensConsumed,
|
|
1117
|
+
objective: a.objective,
|
|
1118
|
+
geneId: a.geneId,
|
|
1119
|
+
parentId: a.parentAgentId,
|
|
1120
|
+
}));
|
|
1121
|
+
const edges = agents
|
|
1122
|
+
.filter((a) => a.parentAgentId)
|
|
1123
|
+
.map((a) => ({
|
|
1124
|
+
from: a.parentAgentId!,
|
|
1125
|
+
to: a.id,
|
|
1126
|
+
kind: 'parent' as const,
|
|
1127
|
+
}));
|
|
1128
|
+
return { nodes, edges };
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
createAgentContext(agentId: string): OrganismAgentContext {
|
|
1132
|
+
return new OrganismAgentContext({
|
|
1133
|
+
getMission: (): MissionDefinition => this.record.mission,
|
|
1134
|
+
getConstitution: (): ConstitutionDefinition | undefined => this.record.constitution,
|
|
1135
|
+
getWallet: (): import('../types/organism.types').AgentResourceWallet => {
|
|
1136
|
+
const agent = this.syncGetAgent(agentId);
|
|
1137
|
+
return agent?.wallet ?? {};
|
|
1138
|
+
},
|
|
1139
|
+
findAgents: async (query: { capabilities?: string[] }): Promise<RuntimeAgentRecord[]> => {
|
|
1140
|
+
const all = await this.liveAgents();
|
|
1141
|
+
if (!query.capabilities?.length) return all;
|
|
1142
|
+
return this.capabilities.findCapableAgents(query.capabilities, all);
|
|
1143
|
+
},
|
|
1144
|
+
delegate: async (input: {
|
|
1145
|
+
to: string;
|
|
1146
|
+
task: string;
|
|
1147
|
+
}): Promise<{ ok: boolean; result?: string }> => this.delegateInternal(input.to, input.task),
|
|
1148
|
+
requestResources: async (input: {
|
|
1149
|
+
reason: string;
|
|
1150
|
+
requested: import('../types/organism.types').ResourceRequest;
|
|
1151
|
+
expectedValue?: number;
|
|
1152
|
+
confidence?: number;
|
|
1153
|
+
urgency?: number;
|
|
1154
|
+
useMarket?: boolean;
|
|
1155
|
+
}): Promise<{
|
|
1156
|
+
approved: boolean;
|
|
1157
|
+
reason: string;
|
|
1158
|
+
wallet?: import('../types/organism.types').AgentResourceWallet;
|
|
1159
|
+
forecast?: UtilityForecast;
|
|
1160
|
+
}> => this.requestResourcesForAgent(agentId, input),
|
|
1161
|
+
placeBid: (input: {
|
|
1162
|
+
reason: string;
|
|
1163
|
+
requested: ResourceRequest;
|
|
1164
|
+
expectedValue: number;
|
|
1165
|
+
confidence: number;
|
|
1166
|
+
urgency?: number;
|
|
1167
|
+
bidPrice?: number;
|
|
1168
|
+
}): ResourceBid =>
|
|
1169
|
+
this.placeBid({
|
|
1170
|
+
agentId,
|
|
1171
|
+
...input,
|
|
1172
|
+
}),
|
|
1173
|
+
negotiate: async (input: {
|
|
1174
|
+
toAgentId: string;
|
|
1175
|
+
reason: string;
|
|
1176
|
+
transfer: ResourceRequest;
|
|
1177
|
+
expectedValue?: number;
|
|
1178
|
+
confidence?: number;
|
|
1179
|
+
}): Promise<NegotiationResult> =>
|
|
1180
|
+
this.negotiate({
|
|
1181
|
+
fromAgentId: agentId,
|
|
1182
|
+
toAgentId: input.toAgentId,
|
|
1183
|
+
reason: input.reason,
|
|
1184
|
+
transfer: input.transfer,
|
|
1185
|
+
expectedValue: input.expectedValue,
|
|
1186
|
+
confidence: input.confidence,
|
|
1187
|
+
}),
|
|
1188
|
+
reportOutcome: async (report: AgentOutcomeReport): Promise<void> =>
|
|
1189
|
+
this.reportOutcome(agentId, report),
|
|
1190
|
+
requestApproval: async (input: {
|
|
1191
|
+
action: string;
|
|
1192
|
+
reason: string;
|
|
1193
|
+
risk?: number;
|
|
1194
|
+
expectedValue?: number;
|
|
1195
|
+
}): Promise<{ approved: boolean; reason: string }> => {
|
|
1196
|
+
const key = `${input.action}:${input.reason}`;
|
|
1197
|
+
if (this.pendingApprovals.has(key)) {
|
|
1198
|
+
return {
|
|
1199
|
+
approved: this.pendingApprovals.get(key)!,
|
|
1200
|
+
reason: 'Cached approval decision',
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
const approved = (input.risk ?? 0) < 0.5;
|
|
1204
|
+
this.pendingApprovals.set(key, approved);
|
|
1205
|
+
return {
|
|
1206
|
+
approved,
|
|
1207
|
+
reason: approved ? 'Auto-approved low risk' : 'Requires human approval',
|
|
1208
|
+
};
|
|
1209
|
+
},
|
|
1210
|
+
spawn: async (input): Promise<RuntimeAgentRecord> =>
|
|
1211
|
+
this.spawnAgent({
|
|
1212
|
+
...input,
|
|
1213
|
+
parentAgentId: agentId,
|
|
1214
|
+
}),
|
|
1215
|
+
reproduce: async (request: ReproduceRequest): Promise<RuntimeAgentRecord> =>
|
|
1216
|
+
this.reproduceAgent(agentId, request),
|
|
1217
|
+
mutate: async (input: {
|
|
1218
|
+
reason: string;
|
|
1219
|
+
mutation: AgentMutation;
|
|
1220
|
+
}): Promise<RuntimeAgentRecord> => this.mutateAgent(agentId, input),
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/** Grant/deny a pending human approval key (for tests / HITL bridge). */
|
|
1225
|
+
setApproval(action: string, reason: string, approved: boolean): void {
|
|
1226
|
+
this.pendingApprovals.set(`${action}:${reason}`, approved);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
async simulate(options: SimulateOptions): Promise<OrganismInspectState> {
|
|
1230
|
+
this.record.simulation = true;
|
|
1231
|
+
if (options.environment?.clock !== 'real') {
|
|
1232
|
+
this.clock.useAccelerated(this.clock.now().getTime());
|
|
1233
|
+
}
|
|
1234
|
+
const log = options.onEvent ?? ((line: string): void => this.log(line));
|
|
1235
|
+
const prevLog = this.log;
|
|
1236
|
+
this.log = log;
|
|
1237
|
+
|
|
1238
|
+
if (this.record.status === 'created') {
|
|
1239
|
+
await this.start();
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
const durationMs = parseDurationMs(options.duration, 86_400_000);
|
|
1243
|
+
const stepMs = options.stepMs ?? Math.max(1000, Math.floor(durationMs / 20));
|
|
1244
|
+
const signals = options.signals ?? [];
|
|
1245
|
+
let elapsed = 0;
|
|
1246
|
+
let signalIndex = 0;
|
|
1247
|
+
|
|
1248
|
+
while (elapsed < durationMs && this.record.status !== 'terminated') {
|
|
1249
|
+
this.clock.advance(stepMs);
|
|
1250
|
+
elapsed += stepMs;
|
|
1251
|
+
|
|
1252
|
+
if (signalIndex < signals.length) {
|
|
1253
|
+
const at = (signalIndex + 1) * (durationMs / (signals.length + 1));
|
|
1254
|
+
if (elapsed >= at) {
|
|
1255
|
+
await this.observe(signals[signalIndex]);
|
|
1256
|
+
signalIndex += 1;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
if (elapsed % parseDurationMs(this.record.cycles.survival, 600_000) < stepMs) {
|
|
1261
|
+
await this.runSurvivalCycle();
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
this.log = prevLog;
|
|
1266
|
+
return this.inspect();
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
getAgentRuntime(): AgentRuntime {
|
|
1270
|
+
return this.agentRuntime;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
getRecord(): OrganismRecord {
|
|
1274
|
+
return { ...this.record };
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
async listAgents(): Promise<RuntimeAgentRecord[]> {
|
|
1278
|
+
return this.repo.listAgents(this.id);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
async listEvents(limit = 50): Promise<import('../events/organism-events').OrganismEvent[]> {
|
|
1282
|
+
return this.events.getHistory(limit);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
private syncGetAgent(agentId: string): RuntimeAgentRecord | undefined {
|
|
1286
|
+
return this.agentCache.get(agentId);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
private async liveAgents(): Promise<RuntimeAgentRecord[]> {
|
|
1290
|
+
const all = await this.repo.listAgents(this.id);
|
|
1291
|
+
for (const a of all) this.agentCache.set(a.id, a);
|
|
1292
|
+
return all.filter((a) => a.status !== 'terminated' && a.status !== 'failed');
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
private async delegateInternal(
|
|
1296
|
+
to: string,
|
|
1297
|
+
task: string
|
|
1298
|
+
): Promise<{ ok: boolean; result?: string }> {
|
|
1299
|
+
const agent = await this.repo.getAgent(this.id, to);
|
|
1300
|
+
if (!agent || agent.status === 'terminated') {
|
|
1301
|
+
return { ok: false, result: 'Agent not available' };
|
|
1302
|
+
}
|
|
1303
|
+
agent.status = 'active';
|
|
1304
|
+
await this.repo.saveAgent(this.id, agent);
|
|
1305
|
+
this.agentCache.set(agent.id, agent);
|
|
1306
|
+
this.log(`[DELEGATE] task to ${agent.name}#${agent.id}: ${task}`);
|
|
1307
|
+
try {
|
|
1308
|
+
if (this.agentRuntime.getAgentMetadata(agent.dnaAgentName)) {
|
|
1309
|
+
await this.agentRuntime.execute(agent.dnaAgentName, task);
|
|
1310
|
+
}
|
|
1311
|
+
} catch {
|
|
1312
|
+
// mock / missing agent — still count as delegated assignment
|
|
1313
|
+
}
|
|
1314
|
+
return { ok: true, result: `Assigned: ${task}` };
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
private startCycles(): void {
|
|
1318
|
+
this.stopCycles();
|
|
1319
|
+
if (this.record.simulation || this.clock.isAccelerated) {
|
|
1320
|
+
// simulation drives cycles explicitly
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const survivalMs = parseDurationMs(this.record.cycles.survival, 600_000);
|
|
1324
|
+
const timer = setInterval(() => {
|
|
1325
|
+
void this.runSurvivalCycle();
|
|
1326
|
+
}, survivalMs);
|
|
1327
|
+
if (typeof timer.unref === 'function') timer.unref();
|
|
1328
|
+
this.cycleTimers.push(timer);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
private stopCycles(): void {
|
|
1332
|
+
for (const t of this.cycleTimers) clearInterval(t);
|
|
1333
|
+
this.cycleTimers = [];
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
private async persist(): Promise<void> {
|
|
1337
|
+
this.record.updatedAt = this.clock.now();
|
|
1338
|
+
await this.repo.saveOrganism(this.record);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
function asDate(value: Date | string): Date {
|
|
1343
|
+
return value instanceof Date ? value : new Date(value);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
function normalizeOrganismRecord(record: OrganismRecord): OrganismRecord {
|
|
1347
|
+
return {
|
|
1348
|
+
...record,
|
|
1349
|
+
createdAt: asDate(record.createdAt),
|
|
1350
|
+
updatedAt: asDate(record.updatedAt),
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function normalizeAgentRecord(agent: RuntimeAgentRecord): RuntimeAgentRecord {
|
|
1355
|
+
return {
|
|
1356
|
+
...agent,
|
|
1357
|
+
createdAt: asDate(agent.createdAt),
|
|
1358
|
+
terminatedAt: agent.terminatedAt ? asDate(agent.terminatedAt) : undefined,
|
|
1359
|
+
lastEvaluatedAt: agent.lastEvaluatedAt ? asDate(agent.lastEvaluatedAt) : undefined,
|
|
1360
|
+
lastReproductionAt: agent.lastReproductionAt ? asDate(agent.lastReproductionAt) : undefined,
|
|
1361
|
+
lastMutationAt: agent.lastMutationAt ? asDate(agent.lastMutationAt) : undefined,
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
export type SurvivalReport = import('../lifecycle/survival-engine').SurvivalVerdict;
|
|
1366
|
+
|
|
1367
|
+
export async function createOrganism(options: CreateOrganismOptions): Promise<OrganismRuntime> {
|
|
1368
|
+
return OrganismRuntime.create(options);
|
|
1369
|
+
}
|