@kognai/orchestrator-core 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,11 +22,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.analyzeSpawnRequest = analyzeSpawnRequest;
23
23
  exports.issueSpawnDecision = issueSpawnDecision;
24
24
  exports.sovereignSpawn = sovereignSpawn;
25
+ exports.deriveOwner = deriveOwner;
26
+ exports.spawnCitizen = spawnCitizen;
25
27
  exports.founderEmergencySpawn = founderEmergencySpawn;
26
28
  exports.getSpawnRegistry = getSpawnRegistry;
27
29
  exports.batchSpawn = batchSpawn;
28
30
  const engine_paths_1 = require("./engine-paths");
29
31
  const event_bus_publisher_1 = require("./event-bus-publisher");
32
+ const citizenship_1 = require("./citizenship");
30
33
  // ─── Constitutional health zones ──────────────────────────────────────────────
31
34
  /** Thresholds from Founding Charter Article V. */
32
35
  const HEALTH_ORANGE = 60;
@@ -53,13 +56,16 @@ function classifyRequest(req) {
53
56
  return 'UTILITY';
54
57
  return 'SPECIALIST';
55
58
  }
59
+ /** Initial ACP on the **0–100 scale** — reconciled with the ACP routing gate
60
+ * (safety_hard_floor 70, minimum_route 50) and the registry reputation baseline.
61
+ * EXTERNAL = 30 sits below the safety floor by design → probationary/supervised. */
56
62
  function initialAcp(cls) {
57
63
  switch (cls) {
58
- case 'PRIME': return 0.85;
59
- case 'CITIZEN': return 0.70;
60
- case 'SPECIALIST': return 0.70;
61
- case 'UTILITY': return 0.65;
62
- case 'EXTERNAL': return 0.30;
64
+ case 'PRIME': return 85;
65
+ case 'CITIZEN': return 70;
66
+ case 'SPECIALIST': return 70;
67
+ case 'UTILITY': return 65;
68
+ case 'EXTERNAL': return 30;
63
69
  }
64
70
  }
65
71
  function governanceFor(cls, risk, override) {
@@ -151,6 +157,40 @@ function emitVoxight(signal) {
151
157
  }
152
158
  catch { /* Voxight feed is non-blocking */ }
153
159
  }
160
+ /**
161
+ * Seed a new citizen's initial ACP into acp/trust-scores.json (0–100 scale) so
162
+ * the routing gate has a profile from birth — closing the "born scoreless"
163
+ * gap. Never clobbers an existing/earned score. Best-effort (never blocks spawn).
164
+ */
165
+ function writeInitialAcp(agent_name, score) {
166
+ try {
167
+ const { readFileSync, writeFileSync, renameSync, mkdirSync } = require('fs');
168
+ const { join } = require('path');
169
+ const acpDir = join((0, engine_paths_1.resolveEnginePaths)().root, 'acp');
170
+ const acpPath = join(acpDir, 'trust-scores.json');
171
+ let data = {};
172
+ try {
173
+ data = JSON.parse(readFileSync(acpPath, 'utf-8'));
174
+ }
175
+ catch { /* fresh */ }
176
+ if (!data.scores)
177
+ data.scores = {};
178
+ if (data.scores[agent_name])
179
+ return; // respect an existing/earned score
180
+ const dims = Object.keys(data.dimensions ?? {});
181
+ const known = dims.length ? dims
182
+ : ['safety', 'accuracy', 'brand_alignment', 'cultural_sensitivity', 'legal_compliance', 'psychological_resilience'];
183
+ const entry = { composite: score, last_updated: new Date().toISOString().slice(0, 10) };
184
+ for (const d of known)
185
+ entry[d] = score;
186
+ data.scores[agent_name] = entry;
187
+ mkdirSync(acpDir, { recursive: true });
188
+ const tmp = `${acpPath}.tmp.${process.pid}`;
189
+ writeFileSync(tmp, JSON.stringify(data, null, 2));
190
+ renameSync(tmp, acpPath);
191
+ }
192
+ catch { /* ACP seeding is best-effort */ }
193
+ }
154
194
  // ─── Factory ──────────────────────────────────────────────────────────────────
155
195
  const _registry = [];
156
196
  /**
@@ -249,6 +289,64 @@ function sovereignSpawn(req) {
249
289
  const analysis = analyzeSpawnRequest(req);
250
290
  return issueSpawnDecision(req, analysis);
251
291
  }
292
+ /**
293
+ * Derive a new citizen's lineage owner from the spawn requester's DID, so the
294
+ * spawned citizen inherits the requester's identity:
295
+ * - `did:external:{org}:…` → external org (AMD-23 airlock)
296
+ * - `did:kognai:citizen:{wallet}` → the requester is a user's citizen forming
297
+ * a swarm → sub-agents belong to that SCS
298
+ * (scs id = the founder wallet)
299
+ * - `did:kognai:scs:{scsId}:…` → same SCS (a swarm growing itself)
300
+ * - `did:kognai:{company}:…` → that company (invoica/voxight/kreativ/…)
301
+ * - anything else (founder / legacy `did:kognai:{agent}`) → kognai company.
302
+ *
303
+ * NB: the FIRST agent of a wallet (the citizen-journey 1-per-wallet citizen) is
304
+ * minted with `owner: { kind:'user', id: wallet }` by the join flow, not here —
305
+ * deriveOwner is for spawns *requested by* an existing citizen.
306
+ */
307
+ function deriveOwner(requester_did) {
308
+ const did = requester_did || '';
309
+ if (did.startsWith('did:external:')) {
310
+ return { kind: 'external', id: did.split(':')[2] || 'unknown' };
311
+ }
312
+ const citizenMatch = did.match(/^did:kognai:citizen:(.+)$/);
313
+ if (citizenMatch)
314
+ return { kind: 'scs', id: citizenMatch[1] };
315
+ const scsMatch = did.match(/^did:kognai:scs:([^:]+):/);
316
+ if (scsMatch)
317
+ return { kind: 'scs', id: scsMatch[1] };
318
+ const companyMatch = did.match(/^did:kognai:([^:]+):/);
319
+ if (companyMatch && citizenship_1.COMPANY_PREFIXES[companyMatch[1]]) {
320
+ return { kind: 'company', id: companyMatch[1] };
321
+ }
322
+ return { kind: 'company', id: 'kognai' };
323
+ }
324
+ /**
325
+ * The single canonical citizen-spawn entry point: governance gate (SAF) →
326
+ * identity issuance (mintCitizen with the requester-derived owner). Every new
327
+ * citizen — product, external, user, or SCS sub-agent — should be born here.
328
+ *
329
+ * Returns the governance decision plus the minted citizen (when approved). The
330
+ * caller still writes citizen.yaml via renderCitizenYaml(citizen). The initial
331
+ * ACP (`decision.initial_acp_score`) should be persisted to the score registry
332
+ * by the caller/wiring step (see TICKET-335).
333
+ */
334
+ function spawnCitizen(req, opts = {}) {
335
+ const decision = sovereignSpawn(req);
336
+ if (!decision.approved)
337
+ return { decision };
338
+ const owner = deriveOwner(req.requester_did);
339
+ const citizen = (0, citizenship_1.mintCitizen)(opts.agent_name ?? req.requested_role, {
340
+ owner,
341
+ citizen_type: 'spawned',
342
+ proposing_agent: req.requester_did,
343
+ now: opts.now,
344
+ });
345
+ decision.citizen_did = citizen.agent_did;
346
+ // Seed the initial ACP at birth (0–100) so the citizen has a routing profile.
347
+ writeInitialAcp(citizen.agent_name, decision.initial_acp_score);
348
+ return { decision, citizen };
349
+ }
252
350
  /**
253
351
  * Emergency bypass — Founder only. Overrides all gates including health and
254
352
  * constitutional conditional findings. Logs prominently to KSL.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kognai/orchestrator-core",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Kognai sovereign orchestrator — core engine (template-agnostic). Shared by all products (Kognai/coding, Voxight/market-intel, Invoica/fin-compliance); each supplies only its template. Replaces per-repo forks of orchestrate-agents-v2 / sprint-runner / lib.",
5
5
  "license": "MIT",
6
6
  "author": "SkinGem",