@buildaureon/sdk 0.1.2 → 0.1.8

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/dist/index.js CHANGED
@@ -10,11 +10,81 @@ function userAgentHeader(version) {
10
10
  return { "X-Aureon-SDK": `@buildaureon/sdk/${version}` };
11
11
  }
12
12
 
13
+ // src/constants/networks.ts
14
+ var MAINNET_CHAIN_ID = 4663;
15
+ var TESTNET_CHAIN_ID = 46630;
16
+ var MAINNET_API_BASE_URL = "http://127.0.0.1:8788";
17
+ var TESTNET_API_BASE_URL = "https://api.aureonlabs.network";
18
+ var MAINNET_EXPLORER = "https://robinhoodchain.blockscout.com";
19
+ var TESTNET_EXPLORER = "https://explorer.testnet.chain.robinhood.com";
20
+ var AUREON_NETWORKS = {
21
+ mainnet: {
22
+ network: "mainnet",
23
+ chainId: MAINNET_CHAIN_ID,
24
+ baseUrl: MAINNET_API_BASE_URL,
25
+ explorer: MAINNET_EXPLORER
26
+ },
27
+ testnet: {
28
+ network: "testnet",
29
+ chainId: TESTNET_CHAIN_ID,
30
+ baseUrl: TESTNET_API_BASE_URL,
31
+ explorer: TESTNET_EXPLORER
32
+ }
33
+ };
34
+ function parseNetwork(raw) {
35
+ const n = raw.trim().toLowerCase();
36
+ if (n === "mainnet" || n === "testnet") return n;
37
+ throw new Error(
38
+ `Unknown AUREON network "${raw}". Use "mainnet" or "testnet".`
39
+ );
40
+ }
41
+ function stripSlash(url) {
42
+ return url.replace(/\/+$/, "");
43
+ }
44
+ function inferAureonNetworkFromUrl(url) {
45
+ const u = stripSlash(url).toLowerCase();
46
+ if (u.includes("api.aureonlabs.network")) return "testnet";
47
+ if (/:(8787)(\/|$)/.test(u) || u.endsWith(":8787")) return "testnet";
48
+ if (/:(8788)(\/|$)/.test(u) || u.endsWith(":8788")) return "mainnet";
49
+ return null;
50
+ }
51
+ function mismatchMessage(network, baseUrl) {
52
+ return `baseUrl "${baseUrl}" does not match network "${network}". mainnet is ${MAINNET_API_BASE_URL} (4663). testnet is ${TESTNET_API_BASE_URL} (46630; public host is still testnet).`;
53
+ }
54
+ function resolveAureonNetwork(input = {}) {
55
+ const networkRaw = input.network?.trim();
56
+ const networkSpecified = Boolean(networkRaw);
57
+ const network = networkSpecified ? parseNetwork(networkRaw) : "mainnet";
58
+ const explicitUrl = input.baseUrl?.trim();
59
+ if (!explicitUrl) {
60
+ return { ...AUREON_NETWORKS[network] };
61
+ }
62
+ const baseUrl = stripSlash(explicitUrl);
63
+ const inferred = inferAureonNetworkFromUrl(baseUrl);
64
+ if (networkSpecified && inferred && inferred !== network) {
65
+ throw new Error(mismatchMessage(network, baseUrl));
66
+ }
67
+ const resolvedNetwork = networkSpecified ? network : inferred ?? "mainnet";
68
+ const preset = AUREON_NETWORKS[resolvedNetwork];
69
+ return {
70
+ network: resolvedNetwork,
71
+ chainId: preset.chainId,
72
+ baseUrl,
73
+ explorer: preset.explorer
74
+ };
75
+ }
76
+ function resolveAureonNetworkFromEnv(env = process.env) {
77
+ return resolveAureonNetwork({
78
+ network: env.AUREON_NETWORK,
79
+ baseUrl: env.AUREON_API_URL
80
+ });
81
+ }
82
+
13
83
  // src/constants/defaults.ts
14
- var DEFAULT_API_BASE_URL = "https://api.aureonlabs.network";
15
- var LOCAL_API_BASE_URL = "http://127.0.0.1:8787";
84
+ var DEFAULT_API_BASE_URL = TESTNET_API_BASE_URL;
85
+ var LOCAL_API_BASE_URL = MAINNET_API_BASE_URL;
16
86
  var DEFAULT_TIMEOUT_MS = 3e4;
17
- var SDK_VERSION = "0.1.2";
87
+ var SDK_VERSION = "0.1.7";
18
88
  var SDK_NAME = "@buildaureon/sdk";
19
89
  var PRODUCT_NAME = "AUREON";
20
90
  var PRODUCT_TAGLINE = "Financial Compass for Robinhood Chain";
@@ -45,7 +115,8 @@ var ENDPOINTS = {
45
115
  authDevLogin: "/auth/dev-login",
46
116
  authMe: "/auth/me",
47
117
  developerApiKeys: "/developer/api-keys",
48
- registryStatus: "/registry/status"
118
+ registryStatus: "/registry/status",
119
+ settlements: "/settlements"
49
120
  };
50
121
  function objectivePath(id) {
51
122
  return `${ENDPOINTS.objectives}/${encodeURIComponent(id)}`;
@@ -74,6 +145,12 @@ function registryPreparePath(id) {
74
145
  function registryConfirmPath(id) {
75
146
  return `${registryObjectivePath(id)}/confirm`;
76
147
  }
148
+ function executionSettlementPath(id) {
149
+ return `/executions/${encodeURIComponent(id)}/settlement`;
150
+ }
151
+ function executionConfirmSettlementPath(id) {
152
+ return `/executions/${encodeURIComponent(id)}/confirm-settlement`;
153
+ }
77
154
 
78
155
  // src/errors/codes.ts
79
156
  var RETRYABLE_CODES = [
@@ -168,22 +245,1110 @@ var AureonTimeoutError = class extends AureonError {
168
245
  }
169
246
  };
170
247
 
171
- // src/errors/http.ts
248
+ // src/formatting/allocation.ts
249
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["active", "validated"]);
250
+ function isOffPlan(state) {
251
+ return state === "warning" || state === "violation";
252
+ }
253
+ function buildAllocationComparison(objectives, health) {
254
+ const healthById = new Map(health.map((h) => [h.objectiveId, h]));
255
+ return objectives.filter((o) => ACTIVE_STATUSES.has(o.status)).map((objective) => {
256
+ const record = healthById.get(objective.id);
257
+ const targetWeight = objective.policy?.targetWeight ?? record?.targetMetric ?? 0;
258
+ const currentMetric = record?.currentMetric ?? 0;
259
+ return {
260
+ objectiveId: objective.id,
261
+ name: objective.name,
262
+ kind: objective.kind,
263
+ targetSymbol: objective.policy?.targetSymbol,
264
+ targetWeight,
265
+ currentMetric,
266
+ deviation: record?.deviation ?? currentMetric - targetWeight,
267
+ state: record?.state ?? "paused"
268
+ };
269
+ });
270
+ }
271
+ function detectPlanParadox(overview, health) {
272
+ const offPlan = health.filter((h) => isOffPlan(h.state));
273
+ const offPlanCount = offPlan.length;
274
+ const bookUp = overview.change24hPct != null && !overview.change24hBaselineOnly ? overview.change24hPct >= 0 : overview.attentionCount > 0 && overview.totalNotionalUsd > 0;
275
+ const detected = bookUp && offPlanCount > 0;
276
+ let message = "Portfolio and objectives are aligned.";
277
+ if (detected) {
278
+ const pct = overview.change24hPct != null && !overview.change24hBaselineOnly ? `${(overview.change24hPct * 100).toFixed(1)}%` : "recent activity";
279
+ message = `Book is up (${pct}), but ${offPlanCount} objective${offPlanCount === 1 ? "" : "s"} are off-plan.`;
280
+ } else if (offPlanCount > 0) {
281
+ message = `${offPlanCount} objective${offPlanCount === 1 ? "" : "s"} need attention.`;
282
+ }
283
+ return { detected, bookUp, offPlanCount, message };
284
+ }
285
+
286
+ // src/types/objective.ts
287
+ var OBJECTIVE_KINDS = [
288
+ "stable_allocation",
289
+ "balanced_portfolio",
290
+ "risk_ceiling",
291
+ "reward_reinvestment"
292
+ ];
293
+ var OBJECTIVE_PRIORITIES = [
294
+ "low",
295
+ "medium",
296
+ "high",
297
+ "critical"
298
+ ];
299
+ function isObjectiveKind(value) {
300
+ return OBJECTIVE_KINDS.includes(value);
301
+ }
302
+ function isObjectivePriority(value) {
303
+ return OBJECTIVE_PRIORITIES.includes(value);
304
+ }
305
+
306
+ // src/validation/objective-input.ts
307
+ function buildPolicySummary(kind, targetWeight, tolerance) {
308
+ const targetPct = (targetWeight * 100).toFixed(1);
309
+ const tolPct = (tolerance * 100).toFixed(1);
310
+ switch (kind) {
311
+ case "stable_allocation":
312
+ return `Maintain ${targetPct}% stable allocation within \xB1${tolPct}%`;
313
+ case "balanced_portfolio":
314
+ return `Hold balanced weights near ${targetPct}% primary sleeve within \xB1${tolPct}%`;
315
+ case "risk_ceiling":
316
+ return `Keep portfolio risk at or below configured ceiling with ${tolPct}% buffer`;
317
+ case "reward_reinvestment":
318
+ return `Reinvest available rewards toward ${targetPct}% target sleeve`;
319
+ default:
320
+ return `Objective policy target ${targetPct}% \xB1${tolPct}%`;
321
+ }
322
+ }
323
+ function normalizeCreateObjectiveInput(input) {
324
+ const name = input.name?.trim();
325
+ if (!name || name.length < 3) {
326
+ throw new AureonValidationError("Objective name must be at least 3 characters");
327
+ }
328
+ if (!isObjectiveKind(input.kind)) {
329
+ throw new AureonValidationError(`Unsupported objective kind: ${input.kind}`);
330
+ }
331
+ if (input.targetWeight < 0 || input.targetWeight > 1) {
332
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
333
+ }
334
+ if (input.tolerance < 0 || input.tolerance > 0.5) {
335
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
336
+ }
337
+ const priority = input.priority ?? "high";
338
+ if (!isObjectivePriority(priority)) {
339
+ throw new AureonValidationError(`Unsupported priority: ${priority}`);
340
+ }
341
+ if (input.kind === "balanced_portfolio") {
342
+ const symbol = input.targetSymbol?.trim().toUpperCase();
343
+ if (!symbol) {
344
+ throw new AureonValidationError(
345
+ "balanced_portfolio requires targetSymbol"
346
+ );
347
+ }
348
+ return {
349
+ ...input,
350
+ name,
351
+ priority,
352
+ targetSymbol: symbol,
353
+ // SDK / agent path defaults to Automatic. Explicit "manual" is reserved
354
+ // for the operator utility Approve UX; not recommended for integrations.
355
+ automationMode: input.automationMode === "manual" ? "manual" : "auto"
356
+ };
357
+ }
358
+ return {
359
+ ...input,
360
+ name,
361
+ priority,
362
+ targetSymbol: null,
363
+ automationMode: input.automationMode === "manual" ? "manual" : "auto"
364
+ };
365
+ }
366
+ function normalizeUpdateObjectiveInput(input) {
367
+ const next = { ...input };
368
+ if (next.name !== void 0) {
369
+ const name = next.name.trim();
370
+ if (name.length < 3) {
371
+ throw new AureonValidationError("Objective name must be at least 3 characters");
372
+ }
373
+ next.name = name;
374
+ }
375
+ if (next.targetWeight !== void 0) {
376
+ if (next.targetWeight < 0 || next.targetWeight > 1) {
377
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
378
+ }
379
+ }
380
+ if (next.tolerance !== void 0) {
381
+ if (next.tolerance < 0 || next.tolerance > 0.5) {
382
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
383
+ }
384
+ }
385
+ if (next.priority !== void 0 && !isObjectivePriority(next.priority)) {
386
+ throw new AureonValidationError(`Unsupported priority: ${next.priority}`);
387
+ }
388
+ if (next.automationMode !== void 0) {
389
+ throw new AureonValidationError(
390
+ "automationMode cannot be changed after create: recreate the objective instead"
391
+ );
392
+ }
393
+ if (input.targetSymbol !== void 0) {
394
+ throw new AureonValidationError(
395
+ "targetSymbol cannot be changed after create: recreate the objective instead"
396
+ );
397
+ }
398
+ return next;
399
+ }
400
+ function assertId(value, label) {
401
+ if (!value || typeof value !== "string" || value.trim().length < 8) {
402
+ throw new AureonValidationError(`Invalid ${label}`);
403
+ }
404
+ }
405
+
406
+ // src/formatting/drift-restore.ts
407
+ function isOffPlan2(state) {
408
+ return state === "warning" || state === "violation";
409
+ }
410
+ function inferDriftPhase(health) {
411
+ if (health.state === "healthy") return "aligned";
412
+ if (isOffPlan2(health.state)) return "drift_detected";
413
+ return "aligned";
414
+ }
415
+ function buildDriftRestoreFlow(input) {
416
+ const targetWeight = input.objective.policy?.targetWeight ?? input.alignedHealth.targetMetric ?? 0;
417
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
418
+ const summary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
419
+ const restored = input.restoredHealth ? {
420
+ health: input.restoredHealth,
421
+ receipt: input.receipt,
422
+ settlement: input.receipt?.settlement
423
+ } : void 0;
424
+ const currentPhase = restored ? restored.health.state === "healthy" || !isOffPlan2(restored.health.state) ? "restored" : "drift_detected" : inferDriftPhase(input.driftHealth);
425
+ let message = "Rule set and portfolio aligned. Ready to detect drift when marks move.";
426
+ if (currentPhase === "drift_detected") {
427
+ message = "We broke the rule on purpose. AUREON detected drift \u2014 allocation moved off policy.";
428
+ } else if (currentPhase === "restored") {
429
+ const settlement = input.receipt?.settlement ?? "staged";
430
+ message = `Drift detected and restore completed (${settlement} settlement). Policy is back within tolerance.`;
431
+ }
432
+ return {
433
+ objectiveId: input.objective.id,
434
+ rule: { summary, targetWeight, tolerance },
435
+ phases: {
436
+ aligned: {
437
+ health: input.alignedHealth,
438
+ allocationRow: input.alignedRow
439
+ },
440
+ drift: {
441
+ health: input.driftHealth,
442
+ allocationRow: input.driftRow,
443
+ restorePlan: input.driftPlan
444
+ },
445
+ restored
446
+ },
447
+ currentPhase,
448
+ message
449
+ };
450
+ }
451
+ function buildDriftRestoreFlowFromSnapshot(input) {
452
+ const targetWeight = input.objective.policy?.targetWeight ?? input.health.targetMetric ?? 0;
453
+ const offPlan = isOffPlan2(input.health.state);
454
+ const hasRestore = input.latestReceipt && input.latestReceipt.objectiveId === input.objective.id;
455
+ if (!offPlan && !hasRestore) {
456
+ return buildDriftRestoreFlow({
457
+ objective: input.objective,
458
+ alignedHealth: input.health,
459
+ driftHealth: input.health,
460
+ alignedRow: input.allocationRow,
461
+ driftRow: input.allocationRow
462
+ });
463
+ }
464
+ if (offPlan) {
465
+ return buildDriftRestoreFlow({
466
+ objective: input.objective,
467
+ alignedHealth: {
468
+ ...input.health,
469
+ state: "healthy",
470
+ currentMetric: targetWeight,
471
+ deviation: 0,
472
+ message: "On track \u2014 still inside your target range."
473
+ },
474
+ driftHealth: input.health,
475
+ driftPlan: input.restorePlan,
476
+ alignedRow: input.allocationRow,
477
+ driftRow: input.allocationRow
478
+ });
479
+ }
480
+ return buildDriftRestoreFlow({
481
+ objective: input.objective,
482
+ alignedHealth: {
483
+ ...input.health,
484
+ state: "healthy",
485
+ currentMetric: targetWeight,
486
+ deviation: 0,
487
+ message: "On track \u2014 still inside your target range."
488
+ },
489
+ driftHealth: {
490
+ ...input.health,
491
+ state: "warning",
492
+ message: "Prior drift detected."
493
+ },
494
+ restoredHealth: input.health,
495
+ receipt: input.latestReceipt,
496
+ alignedRow: input.allocationRow,
497
+ driftRow: input.allocationRow
498
+ });
499
+ }
500
+
501
+ // src/types/execution.ts
502
+ function shortTransactionHash(hash, head = 10, tail = 6) {
503
+ if (hash.length <= head + tail + 1) return hash;
504
+ return `${hash.slice(0, head)}\u2026${hash.slice(-tail)}`;
505
+ }
506
+ function sortExecutionsNewestFirst(receipts) {
507
+ return [...receipts].sort((a, b) => a.createdAt < b.createdAt ? 1 : -1);
508
+ }
509
+ function isVaultSettlement(receipt) {
510
+ return receipt.settlement === "vault";
511
+ }
512
+ function isChainVerifiedReceipt(receipt) {
513
+ return receipt.verifiedOnChain === true;
514
+ }
515
+ function formatReceiptSummary(receipt) {
516
+ const settlementLabel = receipt.verifiedOnChain ? "vault settlement (chain-verified)" : receipt.settlement === "vault" ? "vault settlement (unverified on-chain)" : "staged settlement (capital book)";
517
+ const parts = [receipt.action, settlementLabel, receipt.status];
518
+ if (receipt.explorerUrl) parts.push(receipt.explorerUrl);
519
+ if (receipt.registryRef) {
520
+ parts.push(
521
+ `registry ${shortTransactionHash(receipt.registryRef.objectiveKey, 8, 4)}`
522
+ );
523
+ }
524
+ return parts.join(" \xB7 ");
525
+ }
526
+ function findTimelineEventsForReceipt(events, receipt) {
527
+ return events.filter((event) => {
528
+ if (event.type !== "execution_started" && event.type !== "execution_completed") {
529
+ return false;
530
+ }
531
+ const executionId = event.payload?.executionId;
532
+ return typeof executionId === "string" && executionId === receipt.id;
533
+ });
534
+ }
535
+
536
+ // src/validation/receipt-validator.ts
537
+ var EXECUTION_STATUSES = /* @__PURE__ */ new Set([
538
+ "pending",
539
+ "submitted",
540
+ "confirmed",
541
+ "failed"
542
+ ]);
543
+ var SETTLEMENTS = /* @__PURE__ */ new Set(["vault", "staged"]);
544
+ var TX_HASH_0X = /^0x[a-fA-F0-9]{64}$/;
545
+ var ADDRESS_0X = /^0x[a-fA-F0-9]{40}$/;
546
+ var BYTES32_0X = /^0x[a-fA-F0-9]{64}$/;
547
+ var ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T/;
548
+ function issue(code, message, path) {
549
+ return path ? { code, message, path } : { code, message };
550
+ }
172
551
  function isRecord(value) {
173
552
  return typeof value === "object" && value !== null && !Array.isArray(value);
174
553
  }
554
+ function isNonEmptyString(value) {
555
+ return typeof value === "string" && value.trim().length > 0;
556
+ }
557
+ function isVaultTransactionHash(hash) {
558
+ return hash.startsWith("pending_vault_") || TX_HASH_0X.test(hash);
559
+ }
560
+ function isRealVaultTxHash(hash) {
561
+ return TX_HASH_0X.test(hash);
562
+ }
563
+ function isStagedTransactionHash(hash) {
564
+ return !isVaultTransactionHash(hash);
565
+ }
566
+ function validateSettlementRecord(input, options = {}) {
567
+ const issues = [];
568
+ if (!isRecord(input)) {
569
+ return {
570
+ valid: false,
571
+ issues: [issue("INVALID_INPUT", "Settlement record must be an object")]
572
+ };
573
+ }
574
+ const requiredStrings = [
575
+ ["id", "id"],
576
+ ["walletAddress", "walletAddress"],
577
+ ["transactionHash", "transactionHash"],
578
+ ["vaultAddress", "vaultAddress"],
579
+ ["tokenSell", "tokenSell"],
580
+ ["tokenBuy", "tokenBuy"],
581
+ ["amountIn", "amountIn"],
582
+ ["amountOut", "amountOut"],
583
+ ["explorerUrl", "explorerUrl"],
584
+ ["verifiedAt", "verifiedAt"],
585
+ ["status", "status"]
586
+ ];
587
+ for (const [key, path] of requiredStrings) {
588
+ if (!isNonEmptyString(input[key])) {
589
+ issues.push(issue("MISSING_FIELD", `Missing ${key}`, path));
590
+ }
591
+ }
592
+ if (input.settlement !== "vault") {
593
+ issues.push(
594
+ issue(
595
+ "INVALID_SETTLEMENT_RECORD",
596
+ 'settlement must be "vault" on chain records',
597
+ "settlement"
598
+ )
599
+ );
600
+ }
601
+ if (typeof input.blockNumber !== "number" || !Number.isFinite(input.blockNumber) || input.blockNumber < 0) {
602
+ issues.push(
603
+ issue("INVALID_SETTLEMENT_RECORD", "blockNumber must be a non-negative number", "blockNumber")
604
+ );
605
+ }
606
+ if (typeof input.logIndex !== "number" || !Number.isInteger(input.logIndex) || input.logIndex < 0) {
607
+ issues.push(
608
+ issue("INVALID_SETTLEMENT_RECORD", "logIndex must be a non-negative integer", "logIndex")
609
+ );
610
+ }
611
+ const tx = String(input.transactionHash ?? "");
612
+ if (tx && !TX_HASH_0X.test(tx)) {
613
+ issues.push(
614
+ issue("INVALID_SETTLEMENT_RECORD", "transactionHash must be a 32-byte hex hash", "transactionHash")
615
+ );
616
+ }
617
+ if (input.status !== "confirmed" && input.status !== "orphan") {
618
+ issues.push(
619
+ issue(
620
+ "INVALID_SETTLEMENT_RECORD",
621
+ 'status must be "confirmed" or "orphan"',
622
+ "status"
623
+ )
624
+ );
625
+ }
626
+ if (options.executionId && input.executionId != null && input.executionId !== options.executionId) {
627
+ issues.push(
628
+ issue(
629
+ "VERIFIED_RECORD_MISMATCH",
630
+ "settlementRecord.executionId must match receipt.id",
631
+ "executionId"
632
+ )
633
+ );
634
+ }
635
+ if (isRecord(input.registryRef)) {
636
+ const refIssues = validateRegistryRef(input.registryRef, "registryRef");
637
+ issues.push(...refIssues);
638
+ }
639
+ return { valid: issues.length === 0, issues };
640
+ }
641
+ function validateRegistryRef(ref, basePath) {
642
+ const issues = [];
643
+ const objectiveKey = ref.objectiveKey;
644
+ const contractAddress = ref.contractAddress;
645
+ if (!isNonEmptyString(objectiveKey) || !BYTES32_0X.test(objectiveKey)) {
646
+ issues.push(
647
+ issue(
648
+ "INVALID_REGISTRY_REF",
649
+ "objectiveKey must be a 32-byte hex string",
650
+ `${basePath}.objectiveKey`
651
+ )
652
+ );
653
+ }
654
+ if (!isNonEmptyString(contractAddress) || !ADDRESS_0X.test(contractAddress)) {
655
+ issues.push(
656
+ issue(
657
+ "INVALID_REGISTRY_REF",
658
+ "contractAddress must be a 20-byte hex address",
659
+ `${basePath}.contractAddress`
660
+ )
661
+ );
662
+ }
663
+ return issues;
664
+ }
665
+ function validateExecutionReceipt(input) {
666
+ const issues = [];
667
+ if (!isRecord(input)) {
668
+ return {
669
+ valid: false,
670
+ issues: [issue("INVALID_INPUT", "Receipt must be an object")]
671
+ };
672
+ }
673
+ const required = [
674
+ ["id", "id"],
675
+ ["objectiveId", "objectiveId"],
676
+ ["action", "action"],
677
+ ["status", "status"],
678
+ ["transactionHash", "transactionHash"],
679
+ ["result", "result"],
680
+ ["createdAt", "createdAt"],
681
+ ["settlement", "settlement"]
682
+ ];
683
+ for (const [key, path] of required) {
684
+ if (!isNonEmptyString(input[key])) {
685
+ issues.push(issue("MISSING_FIELD", `Missing ${key}`, path));
686
+ }
687
+ }
688
+ const settlement = input.settlement;
689
+ if (settlement != null && !SETTLEMENTS.has(String(settlement))) {
690
+ issues.push(
691
+ issue(
692
+ "INVALID_SETTLEMENT",
693
+ 'settlement must be "vault" or "staged"',
694
+ "settlement"
695
+ )
696
+ );
697
+ }
698
+ const status = input.status;
699
+ if (status != null && !EXECUTION_STATUSES.has(String(status))) {
700
+ issues.push(
701
+ issue(
702
+ "INVALID_STATUS",
703
+ "status must be pending, submitted, confirmed, or failed",
704
+ "status"
705
+ )
706
+ );
707
+ }
708
+ if (status === "confirmed" && !isNonEmptyString(input.confirmedAt)) {
709
+ issues.push(
710
+ issue("MISSING_CONFIRMED_AT", "confirmed status requires confirmedAt", "confirmedAt")
711
+ );
712
+ }
713
+ if (isNonEmptyString(input.createdAt) && !ISO_TIMESTAMP.test(input.createdAt)) {
714
+ issues.push(
715
+ issue("MISSING_FIELD", "createdAt must be an ISO-8601 timestamp", "createdAt")
716
+ );
717
+ }
718
+ const txHash = String(input.transactionHash ?? "");
719
+ const settlementStr = String(settlement ?? "");
720
+ if (settlementStr === "staged") {
721
+ if (input.explorerUrl != null && input.explorerUrl !== "") {
722
+ issues.push(
723
+ issue(
724
+ "STAGED_WITH_EXPLORER",
725
+ "staged receipts must not include explorerUrl",
726
+ "explorerUrl"
727
+ )
728
+ );
729
+ }
730
+ if (input.verifiedOnChain === true) {
731
+ issues.push(
732
+ issue(
733
+ "STAGED_VERIFIED_ON_CHAIN",
734
+ "staged receipts cannot be verifiedOnChain",
735
+ "verifiedOnChain"
736
+ )
737
+ );
738
+ }
739
+ if (input.settlementRecord != null) {
740
+ issues.push(
741
+ issue(
742
+ "STAGED_WITH_SETTLEMENT_RECORD",
743
+ "staged receipts must not include settlementRecord",
744
+ "settlementRecord"
745
+ )
746
+ );
747
+ }
748
+ if (txHash && !isStagedTransactionHash(txHash)) {
749
+ issues.push(
750
+ issue(
751
+ "INVALID_VAULT_HASH",
752
+ "staged settlement must not use a vault transaction hash",
753
+ "transactionHash"
754
+ )
755
+ );
756
+ }
757
+ }
758
+ if (settlementStr === "vault") {
759
+ if (txHash && !isVaultTransactionHash(txHash)) {
760
+ issues.push(
761
+ issue(
762
+ "INVALID_VAULT_HASH",
763
+ "vault settlement requires 0x\u2026 hash or pending_vault_* prefix",
764
+ "transactionHash"
765
+ )
766
+ );
767
+ }
768
+ if (isRealVaultTxHash(txHash)) {
769
+ if (input.explorerUrl == null || input.explorerUrl === "") {
770
+ issues.push(
771
+ issue(
772
+ "VAULT_MISSING_EXPLORER",
773
+ "confirmed vault tx must include explorerUrl",
774
+ "explorerUrl"
775
+ )
776
+ );
777
+ }
778
+ }
779
+ }
780
+ if (input.verifiedOnChain === true) {
781
+ if (settlementStr !== "vault") {
782
+ issues.push(
783
+ issue(
784
+ "VERIFIED_WITHOUT_RECORD",
785
+ "verifiedOnChain requires vault settlement",
786
+ "verifiedOnChain"
787
+ )
788
+ );
789
+ }
790
+ if (input.settlementRecord == null) {
791
+ issues.push(
792
+ issue(
793
+ "VERIFIED_WITHOUT_RECORD",
794
+ "verifiedOnChain requires settlementRecord",
795
+ "verifiedOnChain"
796
+ )
797
+ );
798
+ } else {
799
+ const nested = validateSettlementRecord(input.settlementRecord, {
800
+ executionId: isNonEmptyString(input.id) ? input.id : void 0
801
+ });
802
+ for (const nestedIssue of nested.issues) {
803
+ issues.push({
804
+ ...nestedIssue,
805
+ path: nestedIssue.path ? `settlementRecord.${nestedIssue.path}` : "settlementRecord"
806
+ });
807
+ }
808
+ }
809
+ } else if (input.settlementRecord != null && input.verifiedOnChain !== false) {
810
+ if (input.verifiedOnChain !== true) {
811
+ issues.push(
812
+ issue(
813
+ "VERIFIED_RECORD_MISMATCH",
814
+ "settlementRecord present but verifiedOnChain is not true",
815
+ "verifiedOnChain"
816
+ )
817
+ );
818
+ }
819
+ }
820
+ if (isRecord(input.registryRef)) {
821
+ issues.push(...validateRegistryRef(input.registryRef, "registryRef"));
822
+ }
823
+ return { valid: issues.length === 0, issues };
824
+ }
825
+ function isValidExecutionReceipt(input) {
826
+ return validateExecutionReceipt(input).valid;
827
+ }
828
+ function assertValidExecutionReceipt(receipt) {
829
+ const result = validateExecutionReceipt(receipt);
830
+ if (!result.valid) {
831
+ throw new AureonValidationError("Invalid execution receipt", {
832
+ issues: result.issues
833
+ });
834
+ }
835
+ }
836
+
837
+ // src/formatting/receipt-verification.ts
838
+ function inferProofTier(receipt, validation, settlement) {
839
+ if (!validation.valid) return "claim_only";
840
+ if (settlement?.verifiedOnChain === true || isChainVerifiedReceipt(receipt) || receipt.verifiedOnChain === true) {
841
+ return "chain_verified";
842
+ }
843
+ return "schema_valid";
844
+ }
845
+ function inferCurrentPhase(validation, proofTier) {
846
+ if (!validation.valid) return "validation_failed";
847
+ if (proofTier === "chain_verified") return "chain_verified";
848
+ return "validated";
849
+ }
850
+ function buildReceiptVerificationFlow(input) {
851
+ const validation = input.validation ?? validateExecutionReceipt(input.receipt);
852
+ const proofTier = inferProofTier(
853
+ input.receipt,
854
+ validation,
855
+ input.settlement
856
+ );
857
+ const currentPhase = inferCurrentPhase(validation, proofTier);
858
+ let message = "An AI saying 'transaction successful' is only a claim \u2014 validate the receipt before trusting it.";
859
+ if (currentPhase === "validation_failed") {
860
+ message = "Receipt failed validation \u2014 do not summarize as proof. Fix honesty issues before reporting success.";
861
+ } else if (currentPhase === "chain_verified") {
862
+ message = "Receipt passes validation and has independent on-chain settlement proof.";
863
+ } else if (proofTier === "schema_valid") {
864
+ const label = input.receipt.settlement === "staged" ? "staged (capital book)" : "vault (not yet chain-observed)";
865
+ message = `Receipt passes validation (${label}). Schema-valid does not mean chain-verified \u2014 check settlement lookup for vault proof.`;
866
+ }
867
+ return {
868
+ executionId: input.receipt.id,
869
+ receipt: input.receipt,
870
+ phases: {
871
+ claimed: {
872
+ summary: formatReceiptSummary(input.receipt),
873
+ status: input.receipt.status,
874
+ settlement: input.receipt.settlement,
875
+ result: input.receipt.result
876
+ },
877
+ validation,
878
+ settlement: input.settlement,
879
+ timelineEvents: input.timelineEvents
880
+ },
881
+ proofTier,
882
+ currentPhase,
883
+ message
884
+ };
885
+ }
886
+
887
+ // src/formatting/portfolio-watch.ts
888
+ var DEFAULT_PORTFOLIO_WATCH_BRIEF = "Watch my portfolio while I'm away \u2014 keep about 20% in stable assets.";
889
+ function isOffPlan3(state) {
890
+ return state === "warning" || state === "violation";
891
+ }
892
+ function inferPortfolioWatchPhase(input) {
893
+ if (input.whileAway) return "return_briefing";
894
+ if (isOffPlan3(input.registerHealth.state)) return "while_away";
895
+ return "watch_registered";
896
+ }
897
+ function buildPortfolioWatchBriefingLines(input) {
898
+ const hostLabel = input.host === "cursor" ? "Cursor" : input.host === "claude" ? "Claude" : "MCP agent";
899
+ const lines = [
900
+ `You asked ${hostLabel} to watch your portfolio while away.`,
901
+ `Policy registered: ${input.objective.name} (${input.objective.automationMode ?? "auto"} mode).`
902
+ ];
903
+ if (input.whileAway) {
904
+ lines.push(
905
+ `While away: ${input.whileAway.symbol} moved ${(input.whileAway.priceChangeRatio * 100).toFixed(0)}% \u2014 health went from ${input.whileAway.healthBefore.state} to ${input.whileAway.healthAfter.state}.`
906
+ );
907
+ if (input.whileAway.autoRestored && input.whileAway.receipt) {
908
+ lines.push(
909
+ `Automatic restore ran \u2014 settlement ${input.whileAway.receipt.settlement}. Receipt id ${input.whileAway.receipt.id}.`
910
+ );
911
+ } else if (input.whileAway.autoRestored) {
912
+ lines.push("Automatic restore ran \u2014 check executions for receipt.");
913
+ } else {
914
+ lines.push("No automatic restore fired \u2014 review health and restore plan.");
915
+ }
916
+ } else if (isOffPlan3(input.briefingHealth.state)) {
917
+ lines.push(
918
+ `Portfolio is off-plan (${input.briefingHealth.state}) \u2014 agent should surface restore options.`
919
+ );
920
+ } else {
921
+ lines.push("Portfolio remains within policy \u2014 no action required.");
922
+ }
923
+ if (input.timelineEvents.length > 0) {
924
+ const types = [...new Set(input.timelineEvents.map((e) => e.type))].slice(
925
+ 0,
926
+ 4
927
+ );
928
+ lines.push(`Timeline: ${input.timelineEvents.length} recent event(s) \u2014 ${types.join(", ")}.`);
929
+ }
930
+ lines.push(`Current health: ${input.briefingHealth.state}.`);
931
+ return lines;
932
+ }
933
+ function buildPortfolioWatchFlow(input) {
934
+ const targetWeight = input.objective.policy?.targetWeight ?? input.registerHealth.targetMetric ?? 0;
935
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
936
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
937
+ const whileAway = input.whileAway ? {
938
+ marketEventName: input.whileAway.marketEvent.name,
939
+ symbol: input.whileAway.marketEvent.symbol,
940
+ priceChangeRatio: input.whileAway.marketEvent.priceChangeRatio,
941
+ healthBefore: input.whileAway.healthBefore,
942
+ healthAfter: input.whileAway.healthAfter,
943
+ autoRestored: input.whileAway.autoRestored,
944
+ receipt: input.whileAway.receipt
945
+ } : void 0;
946
+ const summaryLines = buildPortfolioWatchBriefingLines({
947
+ userBrief: input.userBrief,
948
+ host: input.host,
949
+ objective: input.objective,
950
+ registerHealth: input.registerHealth,
951
+ briefingHealth: input.briefingHealth,
952
+ whileAway,
953
+ timelineEvents: input.timelineEvents
954
+ });
955
+ const currentPhase = inferPortfolioWatchPhase({
956
+ registerHealth: input.registerHealth,
957
+ whileAway
958
+ });
959
+ let message = "Tell your agent to watch the portfolio \u2014 intent becomes an Automatic objective, then read health and timeline when you return.";
960
+ if (currentPhase === "return_briefing" && whileAway?.autoRestored) {
961
+ message = "While you were away, the market moved and Automatic mode restored policy. Review the briefing \u2014 not a blank check, a registered rule.";
962
+ } else if (currentPhase === "while_away" || isOffPlan3(input.briefingHealth.state)) {
963
+ message = "Portfolio drifted off-plan. The agent should report health and restore options \u2014 not discretionary trades.";
964
+ }
965
+ return {
966
+ objectiveId: input.objective.id,
967
+ userBrief: input.userBrief,
968
+ host: input.host,
969
+ phases: {
970
+ register: {
971
+ objectiveName: input.objective.name,
972
+ automationMode: input.objective.automationMode ?? "auto",
973
+ policySummary,
974
+ health: input.registerHealth,
975
+ allocationRow: input.registerRow
976
+ },
977
+ whileAway,
978
+ briefing: {
979
+ health: input.briefingHealth,
980
+ timelineEventCount: input.timelineEvents.length,
981
+ timelineEvents: input.timelineEvents,
982
+ summaryLines
983
+ }
984
+ },
985
+ currentPhase,
986
+ message
987
+ };
988
+ }
989
+ function buildPortfolioWatchFlowFromSnapshot(input) {
990
+ return buildPortfolioWatchFlow({
991
+ userBrief: input.userBrief,
992
+ host: input.host,
993
+ objective: input.objective,
994
+ registerHealth: input.health,
995
+ registerRow: input.allocationRow,
996
+ briefingHealth: input.health,
997
+ timelineEvents: input.timelineEvents
998
+ });
999
+ }
1000
+
1001
+ // src/formatting/full-aureon-loop.ts
1002
+ var DEFAULT_FULL_LOOP_BRIEF = "Keep about 20% in stable assets \u2014 grow the book without abandoning the plan.";
1003
+ function inferFullAureonLoopPhase(input) {
1004
+ if (input.hasRestore && input.verificationValid) return "verified";
1005
+ if (input.hasRestore) return "restored";
1006
+ return "plan_check";
1007
+ }
1008
+ function buildFullAureonLoopFlow(input) {
1009
+ const targetWeight = input.objective.policy?.targetWeight ?? input.baselineHealth.targetMetric ?? 0;
1010
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
1011
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
1012
+ const verification = input.verification ?? buildReceiptVerificationFlow({ receipt: input.receipt });
1013
+ const currentPhase = inferFullAureonLoopPhase({
1014
+ hasRestore: true,
1015
+ verificationValid: verification.phases.validation.valid
1016
+ });
1017
+ let message = "We're not building another portfolio tracker. AUREON registers intent, checks the plan, restores when off-plan, and verifies the receipt.";
1018
+ if (!verification.phases.validation.valid) {
1019
+ message = "Loop completed restore, but the receipt failed validation \u2014 do not treat success text as proof.";
1020
+ } else if (verification.proofTier === "chain_verified") {
1021
+ message = "Full loop complete: intent \u2192 plan check \u2192 restore \u2192 chain-verified receipt. Not a tracker \u2014 a Financial Compass.";
1022
+ } else {
1023
+ message = "Full loop complete: intent \u2192 plan check \u2192 restore \u2192 schema-valid receipt. We're not building another portfolio tracker.";
1024
+ }
1025
+ return {
1026
+ objectiveId: input.objective.id,
1027
+ userBrief: input.userBrief,
1028
+ phases: {
1029
+ intent: {
1030
+ objectiveName: input.objective.name,
1031
+ policySummary,
1032
+ automationMode: input.objective.automationMode ?? "auto",
1033
+ health: input.baselineHealth
1034
+ },
1035
+ planCheck: {
1036
+ baselineAligned: input.baselineHealth.state === "healthy",
1037
+ afterShock: {
1038
+ health: input.afterShockHealth,
1039
+ allocationRow: input.afterShockRow,
1040
+ paradox: input.paradox
1041
+ }
1042
+ },
1043
+ driftRestore: {
1044
+ healthBefore: input.afterShockHealth,
1045
+ healthAfter: input.restoredHealth,
1046
+ receipt: input.receipt,
1047
+ settlement: input.receipt.settlement
1048
+ },
1049
+ verification
1050
+ },
1051
+ currentPhase,
1052
+ message
1053
+ };
1054
+ }
1055
+ function buildFullAureonLoopFlowFromSnapshot(input) {
1056
+ if (!input.latestReceipt) return null;
1057
+ const verification = input.verification ?? buildReceiptVerificationFlow({ receipt: input.latestReceipt });
1058
+ return buildFullAureonLoopFlow({
1059
+ userBrief: input.userBrief,
1060
+ objective: input.objective,
1061
+ baselineHealth: input.health,
1062
+ afterShockHealth: input.health,
1063
+ afterShockRow: input.allocationRow,
1064
+ paradox: input.paradox,
1065
+ restoredHealth: input.health,
1066
+ receipt: input.latestReceipt,
1067
+ verification
1068
+ });
1069
+ }
1070
+
1071
+ // src/formatting/audit-trail.ts
1072
+ function buildFinancialAuditTrail(input) {
1073
+ const targetWeight = input.objective.policy?.targetWeight ?? 0;
1074
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
1075
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
1076
+ const registered = input.registry?.registered === true ? input.registry.record : void 0;
1077
+ const receipts = input.receipts.map((receipt) => {
1078
+ const validation = validateExecutionReceipt(receipt);
1079
+ return {
1080
+ id: receipt.id,
1081
+ action: receipt.action,
1082
+ settlement: receipt.settlement,
1083
+ status: receipt.status,
1084
+ valid: validation.valid,
1085
+ verifiedOnChain: receipt.verifiedOnChain === true,
1086
+ explorerUrl: receipt.explorerUrl ?? null,
1087
+ summary: formatReceiptSummary(receipt),
1088
+ validation
1089
+ };
1090
+ });
1091
+ const timeline = input.timeline.map((event) => ({
1092
+ id: event.id,
1093
+ type: event.type,
1094
+ message: event.message,
1095
+ createdAt: event.createdAt,
1096
+ executionId: typeof event.payload?.executionId === "string" ? event.payload.executionId : null
1097
+ }));
1098
+ const gaps = [];
1099
+ if (input.registryLookupFailed || input.settlementsLookupFailed) {
1100
+ const parts = [
1101
+ input.registryLookupFailed ? "Registry lookup failed." : null,
1102
+ input.settlementsLookupFailed ? "Settlement lookup failed." : null
1103
+ ].filter((part) => Boolean(part));
1104
+ gaps.push({
1105
+ code: "lookup_failed",
1106
+ message: `${parts.join(" ")} Do not treat this as a confirmed gap.`
1107
+ });
1108
+ }
1109
+ if (!registered && !input.registryLookupFailed) {
1110
+ gaps.push({
1111
+ code: "not_registered",
1112
+ message: "Objective is not registered on ObjectiveRegistry."
1113
+ });
1114
+ }
1115
+ if (receipts.length === 0) {
1116
+ gaps.push({
1117
+ code: "no_executions",
1118
+ message: "No execution receipts for this objective."
1119
+ });
1120
+ } else {
1121
+ if (receipts.every((row) => row.settlement === "staged")) {
1122
+ gaps.push({
1123
+ code: "staged_only",
1124
+ message: "Every receipt is staged. None are on-chain."
1125
+ });
1126
+ }
1127
+ if (receipts.some((row) => row.settlement === "vault" && !row.verifiedOnChain)) {
1128
+ gaps.push({
1129
+ code: "vault_unverified",
1130
+ message: "At least one vault receipt has no independent settlement record yet."
1131
+ });
1132
+ }
1133
+ if (receipts.some((row) => !row.valid)) {
1134
+ gaps.push({
1135
+ code: "invalid_receipt",
1136
+ message: "At least one receipt failed local validation. Do not trust it."
1137
+ });
1138
+ }
1139
+ }
1140
+ const everyReceiptStaged = receipts.length > 0 && receipts.every((row) => row.settlement === "staged");
1141
+ if (input.settlements.length === 0 && !input.settlementsLookupFailed && !everyReceiptStaged) {
1142
+ gaps.push({
1143
+ code: "no_settlements",
1144
+ message: "No chain settlement records linked to this objective."
1145
+ });
1146
+ }
1147
+ if (timeline.length === 0) {
1148
+ gaps.push({
1149
+ code: "no_timeline",
1150
+ message: "No timeline events for this objective."
1151
+ });
1152
+ }
1153
+ return {
1154
+ objectiveId: input.objective.id,
1155
+ objectiveName: input.objective.name,
1156
+ policySummary,
1157
+ healthState: input.health?.state ?? null,
1158
+ generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1159
+ registry: registered ? { present: true, record: registered } : { present: false },
1160
+ receipts,
1161
+ settlements: input.settlements,
1162
+ timeline,
1163
+ gaps,
1164
+ message: auditTrailMessage({
1165
+ registered: Boolean(registered),
1166
+ receiptCount: receipts.length,
1167
+ settlementCount: input.settlements.length,
1168
+ invalid: receipts.some((row) => !row.valid),
1169
+ chainVerified: receipts.some((row) => row.verifiedOnChain)
1170
+ })
1171
+ };
1172
+ }
1173
+ function formatAuditTrailLines(trail) {
1174
+ const lines = [
1175
+ `Objective ${trail.objectiveName} (${trail.objectiveId})`,
1176
+ `Policy ${trail.policySummary}`,
1177
+ `Health ${trail.healthState ?? "unknown"}`,
1178
+ `Registry ${trail.registry.present ? "registered on-chain" : "not registered"}`,
1179
+ `Receipts ${trail.receipts.length}`,
1180
+ `Settlements ${trail.settlements.length}`,
1181
+ `Timeline ${trail.timeline.length}`
1182
+ ];
1183
+ if (trail.gaps.length > 0) {
1184
+ lines.push("Gaps");
1185
+ for (const gap of trail.gaps) {
1186
+ lines.push(` - ${gap.message}`);
1187
+ }
1188
+ }
1189
+ lines.push(trail.message);
1190
+ return lines;
1191
+ }
1192
+ function auditTrailMessage(input) {
1193
+ if (input.invalid) {
1194
+ return "Audit trail assembled with dishonest or incomplete receipts. Do not treat success text as proof.";
1195
+ }
1196
+ if (input.receiptCount === 0) {
1197
+ return "Audit trail shows the objective only. No restore has been recorded yet.";
1198
+ }
1199
+ if (input.chainVerified && input.registered) {
1200
+ return "Audit trail complete on testnet: registered objective, receipts, and chain settlement. Not mainnet.";
1201
+ }
1202
+ if (input.chainVerified) {
1203
+ return "Receipts include chain settlement proof. Objective is not registered on-chain.";
1204
+ }
1205
+ if (input.settlementCount === 0) {
1206
+ return "Audit trail shows receipts without chain settlement records. Staged or unverified vault \u2014 not independent proof.";
1207
+ }
1208
+ return "Audit trail assembled from what exists. Gaps are labeled. Nothing missing was invented.";
1209
+ }
1210
+
1211
+ // src/formatting/intent.ts
1212
+ var DEFAULT_TOLERANCE = 0.02;
1213
+ function defaultName(intent) {
1214
+ if (intent.name?.trim()) return intent.name.trim();
1215
+ const pct = (intent.targetWeight * 100).toFixed(0);
1216
+ switch (intent.kind) {
1217
+ case "stable_allocation":
1218
+ return `Maintain ${pct}% Stable Assets`;
1219
+ case "balanced_portfolio":
1220
+ return `Maintain ${pct}% ${intent.targetSymbol ?? "Sleeve"}`;
1221
+ case "risk_ceiling":
1222
+ return `Risk ceiling policy`;
1223
+ case "reward_reinvestment":
1224
+ return `Reinvest rewards toward ${pct}%`;
1225
+ default:
1226
+ return intent.brief.slice(0, 64);
1227
+ }
1228
+ }
1229
+ function resolveObjectiveFromIntent(intent) {
1230
+ const brief = intent.brief?.trim();
1231
+ if (!brief || brief.length < 3) {
1232
+ throw new AureonValidationError("Intent brief must be at least 3 characters");
1233
+ }
1234
+ if (intent.targetWeight < 0 || intent.targetWeight > 1) {
1235
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
1236
+ }
1237
+ const tolerance = intent.tolerance ?? DEFAULT_TOLERANCE;
1238
+ if (tolerance < 0 || tolerance > 0.5) {
1239
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
1240
+ }
1241
+ const base = {
1242
+ name: defaultName(intent),
1243
+ kind: intent.kind,
1244
+ targetWeight: intent.targetWeight,
1245
+ tolerance,
1246
+ priority: intent.priority ?? "high",
1247
+ automationMode: "auto"
1248
+ };
1249
+ if (intent.kind === "balanced_portfolio") {
1250
+ const symbol = intent.targetSymbol?.trim().toUpperCase();
1251
+ if (!symbol) {
1252
+ throw new AureonValidationError(
1253
+ "balanced_portfolio intent requires targetSymbol"
1254
+ );
1255
+ }
1256
+ return { ...base, targetSymbol: symbol };
1257
+ }
1258
+ return base;
1259
+ }
1260
+ function buildObjectivePortfolioFlow(intent, objective, health, portfolio) {
1261
+ const policySummary = objective.policy?.summary ?? buildPolicySummary(intent.kind, intent.targetWeight, intent.tolerance);
1262
+ const state = health?.state ?? "paused";
1263
+ const current = health?.currentMetric;
1264
+ const target = health?.targetMetric ?? intent.targetWeight;
1265
+ let message = "Intent registered as objective. Portfolio is now scored against that policy.";
1266
+ if (health && state === "healthy") {
1267
+ message = `Portfolio aligns with intent \u2014 ${policySummary}.`;
1268
+ } else if (health && (state === "warning" || state === "violation")) {
1269
+ message = `Objective is active but portfolio is off-plan (${state}). Current ${((current ?? 0) * 100).toFixed(1)}% vs target ${(target * 100).toFixed(1)}%.`;
1270
+ }
1271
+ return {
1272
+ intent: { brief: intent.brief.trim(), policySummary },
1273
+ objective,
1274
+ health,
1275
+ portfolio: {
1276
+ totalNotionalUsd: portfolio.totalNotionalUsd,
1277
+ stableWeight: portfolio.stableWeight,
1278
+ positions: portfolio.positions
1279
+ },
1280
+ message
1281
+ };
1282
+ }
1283
+ function parseFinancialIntent(brief) {
1284
+ const text = brief.trim();
1285
+ if (!text) {
1286
+ throw new AureonValidationError("Intent brief is required");
1287
+ }
1288
+ const stableMatch = text.match(
1289
+ /(\d+(?:\.\d+)?)\s*%?\s*(?:of\s+(?:my\s+)?portfolio\s+in\s+)?stable/i
1290
+ );
1291
+ if (stableMatch) {
1292
+ const pct = Number(stableMatch[1]) / 100;
1293
+ return {
1294
+ brief: text,
1295
+ kind: "stable_allocation",
1296
+ targetWeight: pct,
1297
+ tolerance: DEFAULT_TOLERANCE
1298
+ };
1299
+ }
1300
+ const holdMatch = text.match(
1301
+ /(?:hold|keep|maintain)\s+(?:about\s+)?(\d+(?:\.\d+)?)\s*%?\s*(?:in\s+)?([A-Z]{2,10})/i
1302
+ );
1303
+ if (holdMatch) {
1304
+ const pct = Number(holdMatch[1]) / 100;
1305
+ const symbol = holdMatch[2].toUpperCase();
1306
+ if (symbol === "STABLE" || symbol === "STABLES") {
1307
+ return {
1308
+ brief: text,
1309
+ kind: "stable_allocation",
1310
+ targetWeight: pct,
1311
+ tolerance: DEFAULT_TOLERANCE
1312
+ };
1313
+ }
1314
+ return {
1315
+ brief: text,
1316
+ kind: "balanced_portfolio",
1317
+ targetWeight: pct,
1318
+ tolerance: DEFAULT_TOLERANCE,
1319
+ targetSymbol: symbol
1320
+ };
1321
+ }
1322
+ const pctOnly = text.match(/(\d+(?:\.\d+)?)\s*%/);
1323
+ if (pctOnly && /stable/i.test(text)) {
1324
+ return {
1325
+ brief: text,
1326
+ kind: "stable_allocation",
1327
+ targetWeight: Number(pctOnly[1]) / 100,
1328
+ tolerance: DEFAULT_TOLERANCE
1329
+ };
1330
+ }
1331
+ throw new AureonValidationError(
1332
+ "Could not parse intent from brief \u2014 supply structured FinancialIntent fields"
1333
+ );
1334
+ }
1335
+
1336
+ // src/errors/http.ts
1337
+ function isRecord2(value) {
1338
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1339
+ }
175
1340
  function extractMessage(body) {
176
- if (!isRecord(body)) return null;
1341
+ if (!isRecord2(body)) return null;
177
1342
  if (typeof body.message === "string") return body.message;
178
1343
  if (typeof body.error === "string") return body.error;
179
- if (isRecord(body.error) && typeof body.error.message === "string") {
1344
+ if (isRecord2(body.error) && typeof body.error.message === "string") {
180
1345
  return body.error.message;
181
1346
  }
182
1347
  return null;
183
1348
  }
184
1349
  function errorFromHttpStatus(status, body) {
185
1350
  const message = extractMessage(body) ?? defaultMessageForStatus(status);
186
- const details = isRecord(body) ? body : { body };
1351
+ const details = isRecord2(body) ? body : { body };
187
1352
  if (status === 400) return new AureonValidationError(message, details);
188
1353
  if (status === 404) return new AureonNotFoundError(message, details);
189
1354
  if (status === 409) return new AureonConflictError(message, details);
@@ -334,208 +1499,126 @@ async function requestJson(transport, path, options = {}) {
334
1499
  await sleep(retryDelayMs);
335
1500
  continue;
336
1501
  }
337
- throw error;
338
- }
339
- const networkError = new AureonNetworkError(
340
- error instanceof Error ? error.message : "Network request failed",
341
- { url }
342
- );
343
- if (attempt <= maxRetries) {
344
- transport.logger?.warn("aureon.retry", {
345
- path,
346
- attempt,
347
- reason: "network"
348
- });
349
- await sleep(retryDelayMs);
350
- continue;
351
- }
352
- throw networkError;
353
- } finally {
354
- clearTimeout(timeout);
355
- if (options.signal) options.signal.removeEventListener("abort", onAbort);
356
- }
357
- }
358
- }
359
-
360
- // src/types/client-options.ts
361
- var DEFAULT_TIMEOUT_MS2 = 3e4;
362
- function resolveTimeoutMs(options) {
363
- const value = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
364
- if (!Number.isFinite(value) || value <= 0) {
365
- throw new Error("timeoutMs must be a positive finite number");
366
- }
367
- return value;
368
- }
369
- function resolveHeaders(options) {
370
- const headers = { ...options.headers ?? {} };
371
- return headers;
372
- }
373
- function resolveMaxRetries(options) {
374
- const value = options.maxRetries ?? 0;
375
- if (!Number.isInteger(value) || value < 0) {
376
- throw new Error("maxRetries must be a non-negative integer");
377
- }
378
- return value;
379
- }
380
- function resolveRetryDelayMs(options) {
381
- const value = options.retryDelayMs ?? 250;
382
- if (!Number.isFinite(value) || value < 0) {
383
- throw new Error("retryDelayMs must be a non-negative finite number");
384
- }
385
- return value;
386
- }
387
-
388
- // src/types/market.ts
389
- function normalizeSymbol(symbol) {
390
- return symbol.trim().toUpperCase();
391
- }
392
-
393
- // src/validation/market-input.ts
394
- function normalizeApplyMarketEventInput(input) {
395
- const symbol = normalizeSymbol(input.symbol ?? "");
396
- if (!symbol) throw new AureonValidationError("symbol is required");
397
- if (!Number.isFinite(input.priceChangeRatio)) {
398
- throw new AureonValidationError("priceChangeRatio must be a finite number");
399
- }
400
- if (input.priceChangeRatio <= -0.95) {
401
- throw new AureonValidationError(
402
- "priceChangeRatio is too extreme for preview runtime"
403
- );
404
- }
405
- return {
406
- ...input,
407
- symbol,
408
- autoRestore: input.autoRestore !== false,
409
- name: input.name?.trim() || void 0,
410
- description: input.description?.trim() || void 0
411
- };
412
- }
413
-
414
- // src/types/objective.ts
415
- var OBJECTIVE_KINDS = [
416
- "stable_allocation",
417
- "balanced_portfolio",
418
- "risk_ceiling",
419
- "reward_reinvestment"
420
- ];
421
- var OBJECTIVE_PRIORITIES = [
422
- "low",
423
- "medium",
424
- "high",
425
- "critical"
426
- ];
427
- function isObjectiveKind(value) {
428
- return OBJECTIVE_KINDS.includes(value);
429
- }
430
- function isObjectivePriority(value) {
431
- return OBJECTIVE_PRIORITIES.includes(value);
432
- }
433
-
434
- // src/validation/objective-input.ts
435
- function buildPolicySummary(kind, targetWeight, tolerance) {
436
- const targetPct = (targetWeight * 100).toFixed(1);
437
- const tolPct = (tolerance * 100).toFixed(1);
438
- switch (kind) {
439
- case "stable_allocation":
440
- return `Maintain ${targetPct}% stable allocation within \xB1${tolPct}%`;
441
- case "balanced_portfolio":
442
- return `Hold balanced weights near ${targetPct}% primary sleeve within \xB1${tolPct}%`;
443
- case "risk_ceiling":
444
- return `Keep portfolio risk at or below configured ceiling with ${tolPct}% buffer`;
445
- case "reward_reinvestment":
446
- return `Reinvest available rewards toward ${targetPct}% target sleeve`;
447
- default:
448
- return `Objective policy target ${targetPct}% \xB1${tolPct}%`;
449
- }
450
- }
451
- function normalizeCreateObjectiveInput(input) {
452
- const name = input.name?.trim();
453
- if (!name || name.length < 3) {
454
- throw new AureonValidationError("Objective name must be at least 3 characters");
455
- }
456
- if (!isObjectiveKind(input.kind)) {
457
- throw new AureonValidationError(`Unsupported objective kind: ${input.kind}`);
458
- }
459
- if (input.targetWeight < 0 || input.targetWeight > 1) {
460
- throw new AureonValidationError("targetWeight must be between 0 and 1");
461
- }
462
- if (input.tolerance < 0 || input.tolerance > 0.5) {
463
- throw new AureonValidationError("tolerance must be between 0 and 0.5");
464
- }
465
- const priority = input.priority ?? "high";
466
- if (!isObjectivePriority(priority)) {
467
- throw new AureonValidationError(`Unsupported priority: ${priority}`);
468
- }
469
- if (input.kind === "balanced_portfolio") {
470
- const symbol = input.targetSymbol?.trim().toUpperCase();
471
- if (!symbol) {
472
- throw new AureonValidationError(
473
- "balanced_portfolio requires targetSymbol"
1502
+ throw error;
1503
+ }
1504
+ const networkError = new AureonNetworkError(
1505
+ error instanceof Error ? error.message : "Network request failed",
1506
+ { url }
474
1507
  );
1508
+ if (attempt <= maxRetries) {
1509
+ transport.logger?.warn("aureon.retry", {
1510
+ path,
1511
+ attempt,
1512
+ reason: "network"
1513
+ });
1514
+ await sleep(retryDelayMs);
1515
+ continue;
1516
+ }
1517
+ throw networkError;
1518
+ } finally {
1519
+ clearTimeout(timeout);
1520
+ if (options.signal) options.signal.removeEventListener("abort", onAbort);
475
1521
  }
476
- return {
477
- ...input,
478
- name,
479
- priority,
480
- targetSymbol: symbol,
481
- // SDK / agent path defaults to Automatic. Explicit "manual" is reserved
482
- // for the operator utility Approve UX; not recommended for integrations.
483
- automationMode: input.automationMode === "manual" ? "manual" : "auto"
484
- };
485
1522
  }
486
- return {
487
- ...input,
488
- name,
489
- priority,
490
- targetSymbol: null,
491
- automationMode: input.automationMode === "manual" ? "manual" : "auto"
492
- };
493
1523
  }
494
- function normalizeUpdateObjectiveInput(input) {
495
- const next = { ...input };
496
- if (next.name !== void 0) {
497
- const name = next.name.trim();
498
- if (name.length < 3) {
499
- throw new AureonValidationError("Objective name must be at least 3 characters");
500
- }
501
- next.name = name;
502
- }
503
- if (next.targetWeight !== void 0) {
504
- if (next.targetWeight < 0 || next.targetWeight > 1) {
505
- throw new AureonValidationError("targetWeight must be between 0 and 1");
506
- }
1524
+
1525
+ // src/types/client-options.ts
1526
+ var DEFAULT_TIMEOUT_MS2 = 3e4;
1527
+ function resolveTimeoutMs(options) {
1528
+ const value = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
1529
+ if (!Number.isFinite(value) || value <= 0) {
1530
+ throw new Error("timeoutMs must be a positive finite number");
507
1531
  }
508
- if (next.tolerance !== void 0) {
509
- if (next.tolerance < 0 || next.tolerance > 0.5) {
510
- throw new AureonValidationError("tolerance must be between 0 and 0.5");
511
- }
1532
+ return value;
1533
+ }
1534
+ function resolveHeaders(options) {
1535
+ const headers = { ...options.headers ?? {} };
1536
+ return headers;
1537
+ }
1538
+ function resolveMaxRetries(options) {
1539
+ const value = options.maxRetries ?? 0;
1540
+ if (!Number.isInteger(value) || value < 0) {
1541
+ throw new Error("maxRetries must be a non-negative integer");
512
1542
  }
513
- if (next.priority !== void 0 && !isObjectivePriority(next.priority)) {
514
- throw new AureonValidationError(`Unsupported priority: ${next.priority}`);
1543
+ return value;
1544
+ }
1545
+ function resolveRetryDelayMs(options) {
1546
+ const value = options.retryDelayMs ?? 250;
1547
+ if (!Number.isFinite(value) || value < 0) {
1548
+ throw new Error("retryDelayMs must be a non-negative finite number");
515
1549
  }
516
- if (next.automationMode !== void 0) {
517
- throw new AureonValidationError(
518
- "automationMode cannot be changed after create: recreate the objective instead"
519
- );
1550
+ return value;
1551
+ }
1552
+
1553
+ // src/types/market.ts
1554
+ function normalizeSymbol(symbol) {
1555
+ return symbol.trim().toUpperCase();
1556
+ }
1557
+
1558
+ // src/validation/market-input.ts
1559
+ function normalizeApplyMarketEventInput(input) {
1560
+ const symbol = normalizeSymbol(input.symbol ?? "");
1561
+ if (!symbol) throw new AureonValidationError("symbol is required");
1562
+ if (!Number.isFinite(input.priceChangeRatio)) {
1563
+ throw new AureonValidationError("priceChangeRatio must be a finite number");
520
1564
  }
521
- if (input.targetSymbol !== void 0) {
1565
+ if (input.priceChangeRatio <= -0.95) {
522
1566
  throw new AureonValidationError(
523
- "targetSymbol cannot be changed after create: recreate the objective instead"
1567
+ "priceChangeRatio is too extreme for preview runtime"
524
1568
  );
525
1569
  }
526
- return next;
527
- }
528
- function assertId(value, label) {
529
- if (!value || typeof value !== "string" || value.trim().length < 8) {
530
- throw new AureonValidationError(`Invalid ${label}`);
531
- }
1570
+ return {
1571
+ ...input,
1572
+ symbol,
1573
+ autoRestore: input.autoRestore === true,
1574
+ name: input.name?.trim() || void 0,
1575
+ description: input.description?.trim() || void 0
1576
+ };
532
1577
  }
533
1578
 
534
1579
  // src/client/aureon-client.ts
1580
+ var DEMO_DRIFT_RESTORE_POSITIONS = [
1581
+ {
1582
+ symbol: "USDG",
1583
+ name: "Paxos USDG",
1584
+ category: "stable",
1585
+ quantity: 24e3,
1586
+ markPriceUsd: 1
1587
+ },
1588
+ {
1589
+ symbol: "NVDA",
1590
+ name: "NVIDIA Stock Token",
1591
+ category: "stock_token",
1592
+ quantity: 45,
1593
+ markPriceUsd: 920
1594
+ },
1595
+ {
1596
+ symbol: "AAPL",
1597
+ name: "Apple Stock Token",
1598
+ category: "stock_token",
1599
+ quantity: 80,
1600
+ markPriceUsd: 210
1601
+ },
1602
+ {
1603
+ symbol: "ETH",
1604
+ name: "Ether",
1605
+ category: "gas",
1606
+ quantity: 8.5,
1607
+ markPriceUsd: 3400
1608
+ }
1609
+ ];
535
1610
  var AureonClient = class {
536
1611
  transport;
1612
+ resolvedNetwork;
1613
+ resolvedChainId;
537
1614
  constructor(options = {}) {
538
- const baseUrl = options.baseUrl ?? DEFAULT_API_BASE_URL;
1615
+ const resolved = resolveAureonNetwork({
1616
+ network: options.network,
1617
+ baseUrl: options.baseUrl
1618
+ });
1619
+ this.resolvedNetwork = resolved.network;
1620
+ this.resolvedChainId = resolved.chainId;
1621
+ const baseUrl = resolved.baseUrl;
539
1622
  const staticToken = options.authToken;
540
1623
  const getAccessToken = options.getAccessToken ?? (staticToken ? () => staticToken : void 0);
541
1624
  const staticApiKey = options.apiKey;
@@ -559,6 +1642,14 @@ var AureonClient = class {
559
1642
  get baseUrl() {
560
1643
  return this.transport.baseUrl;
561
1644
  }
1645
+ /** `mainnet` (4663) or `testnet` (46630). */
1646
+ get network() {
1647
+ return this.resolvedNetwork;
1648
+ }
1649
+ /** Chain id bundled with `network`. */
1650
+ get chainId() {
1651
+ return this.resolvedChainId;
1652
+ }
562
1653
  /** Health probe for connectivity checks. No auth required. */
563
1654
  async ping() {
564
1655
  return requestJson(this.transport, ENDPOINTS.healthz);
@@ -744,9 +1835,447 @@ var AureonClient = class {
744
1835
  async getOverview() {
745
1836
  return requestJson(this.transport, ENDPOINTS.overview);
746
1837
  }
1838
+ /**
1839
+ * Objective vs actual portfolio — joins objectives, health, and overview
1840
+ * into comparison rows plus a green-book/off-plan paradox flag.
1841
+ * Auth required.
1842
+ */
1843
+ async getAllocationVsTarget() {
1844
+ const [overview, objectives, health] = await Promise.all([
1845
+ this.getOverview(),
1846
+ this.listObjectives(),
1847
+ this.getHealth()
1848
+ ]);
1849
+ const rows = buildAllocationComparison(objectives, health);
1850
+ const paradox = detectPlanParadox(overview, health);
1851
+ return { rows, paradox, overview };
1852
+ }
1853
+ /**
1854
+ * Registers agent/user intent as an Automatic objective and returns the
1855
+ * AI → objective → portfolio flow snapshot.
1856
+ * Auth required.
1857
+ */
1858
+ async applyFinancialIntent(intent) {
1859
+ const objective = await this.createObjective(
1860
+ resolveObjectiveFromIntent(intent)
1861
+ );
1862
+ try {
1863
+ await this.refreshWatchdog();
1864
+ } catch {
1865
+ }
1866
+ const [healthRecords, portfolio] = await Promise.all([
1867
+ this.getHealth(objective.id),
1868
+ this.getPortfolio()
1869
+ ]);
1870
+ return buildObjectivePortfolioFlow(
1871
+ intent,
1872
+ objective,
1873
+ healthRecords[0] ?? null,
1874
+ portfolio
1875
+ );
1876
+ }
1877
+ /**
1878
+ * Read-only AI → objective → portfolio flow for existing objectives.
1879
+ * Auth required.
1880
+ */
1881
+ async getObjectivePortfolioFlow(objectiveId) {
1882
+ const [objectives, healthRecords, portfolio] = await Promise.all([
1883
+ objectiveId ? [await this.getObjective(objectiveId)] : this.listObjectives(),
1884
+ this.getHealth(objectiveId),
1885
+ this.getPortfolio()
1886
+ ]);
1887
+ const active = objectives.filter(
1888
+ (o) => o.status === "active" || o.status === "validated"
1889
+ );
1890
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
1891
+ return active.map((objective) => {
1892
+ const health = healthById.get(objective.id) ?? null;
1893
+ const intent = {
1894
+ brief: objective.name,
1895
+ kind: objective.kind,
1896
+ targetWeight: objective.policy?.targetWeight ?? 0,
1897
+ tolerance: objective.policy?.tolerance ?? 0.02,
1898
+ targetSymbol: objective.policy?.targetSymbol,
1899
+ name: objective.name,
1900
+ priority: objective.priority
1901
+ };
1902
+ return buildObjectivePortfolioFlow(
1903
+ intent,
1904
+ objective,
1905
+ health,
1906
+ portfolio
1907
+ );
1908
+ });
1909
+ }
1910
+ /**
1911
+ * Controlled drift → detection → restore demo
1912
+ * Seeds book, creates stable objective, applies NVDA rally with auto-restore
1913
+ * disabled, then runs manual restore and returns the three-beat flow.
1914
+ * Auth required.
1915
+ */
1916
+ async runDriftRestoreDemo() {
1917
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
1918
+ const objective = await this.createObjective({
1919
+ name: "Maintain 20% Stable Assets",
1920
+ kind: "stable_allocation",
1921
+ targetWeight: 0.2,
1922
+ tolerance: 0.02
1923
+ });
1924
+ try {
1925
+ await this.refreshWatchdog();
1926
+ } catch {
1927
+ }
1928
+ const [alignedHealthRecords, baselineRows] = await Promise.all([
1929
+ this.getHealth(objective.id),
1930
+ this.getAllocationVsTarget()
1931
+ ]);
1932
+ const alignedHealth = alignedHealthRecords[0];
1933
+ if (!alignedHealth) {
1934
+ throw new AureonValidationError(
1935
+ "Baseline health missing after objective create"
1936
+ );
1937
+ }
1938
+ const alignedRow = baselineRows.rows.find(
1939
+ (r) => r.objectiveId === objective.id
1940
+ );
1941
+ await this.applyMarketEvent({
1942
+ name: "NVDA Stock Token Rally",
1943
+ description: "Controlled mark move \u2014 drift demo",
1944
+ symbol: "NVDA",
1945
+ priceChangeRatio: 0.45,
1946
+ autoRestore: false
1947
+ });
1948
+ const [driftHealthRecords, driftRows, restorePlan] = await Promise.all([
1949
+ this.getHealth(objective.id),
1950
+ this.getAllocationVsTarget(),
1951
+ this.getRestorePlan(objective.id)
1952
+ ]);
1953
+ const driftHealth = driftHealthRecords[0];
1954
+ if (!driftHealth) {
1955
+ throw new AureonValidationError("Drift health missing after market event");
1956
+ }
1957
+ const driftRow = driftRows.rows.find((r) => r.objectiveId === objective.id);
1958
+ const receipt = await this.restoreObjective(objective.id);
1959
+ const [restoredHealthRecords] = await Promise.all([
1960
+ this.getHealth(objective.id)
1961
+ ]);
1962
+ const restoredHealth = restoredHealthRecords[0];
1963
+ return buildDriftRestoreFlow({
1964
+ objective,
1965
+ alignedHealth,
1966
+ driftHealth,
1967
+ driftPlan: restorePlan,
1968
+ restoredHealth: restoredHealth ?? driftHealth,
1969
+ receipt,
1970
+ alignedRow,
1971
+ driftRow
1972
+ });
1973
+ }
1974
+ /**
1975
+ * Read-only drift → detection → restore flow for active objectives.
1976
+ * Auth required.
1977
+ */
1978
+ async getDriftRestoreFlow(objectiveId) {
1979
+ const [objectives, healthRecords, allocation, executions] = await Promise.all([
1980
+ objectiveId ? [await this.getObjective(objectiveId)] : this.listObjectives(),
1981
+ this.getHealth(objectiveId),
1982
+ this.getAllocationVsTarget(),
1983
+ this.listExecutions(objectiveId)
1984
+ ]);
1985
+ const active = objectives.filter(
1986
+ (o) => o.status === "active" || o.status === "validated"
1987
+ );
1988
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
1989
+ const rowById = new Map(
1990
+ allocation.rows.map((r) => [r.objectiveId, r])
1991
+ );
1992
+ const receiptsByObjective = /* @__PURE__ */ new Map();
1993
+ for (const receipt of sortExecutionsNewestFirst(executions)) {
1994
+ if (!receiptsByObjective.has(receipt.objectiveId)) {
1995
+ receiptsByObjective.set(receipt.objectiveId, receipt);
1996
+ }
1997
+ }
1998
+ const flows = [];
1999
+ for (const objective of active) {
2000
+ const health = healthById.get(objective.id);
2001
+ if (!health) continue;
2002
+ let restorePlan;
2003
+ if (health.state === "warning" || health.state === "violation") {
2004
+ try {
2005
+ restorePlan = await this.getRestorePlan(objective.id);
2006
+ } catch {
2007
+ }
2008
+ }
2009
+ flows.push(
2010
+ buildDriftRestoreFlowFromSnapshot({
2011
+ objective,
2012
+ health,
2013
+ allocationRow: rowById.get(objective.id),
2014
+ restorePlan,
2015
+ latestReceipt: receiptsByObjective.get(objective.id)
2016
+ })
2017
+ );
2018
+ }
2019
+ return flows;
2020
+ }
2021
+ async buildReceiptVerificationFlowForReceipt(receipt, timelineEvents) {
2022
+ const validation = validateExecutionReceipt(receipt);
2023
+ let settlement;
2024
+ if (receipt.settlement === "vault") {
2025
+ try {
2026
+ settlement = await this.getExecutionSettlement(receipt.id);
2027
+ } catch {
2028
+ }
2029
+ }
2030
+ const events = timelineEvents ?? findTimelineEventsForReceipt(
2031
+ await this.getTimeline(receipt.objectiveId),
2032
+ receipt
2033
+ );
2034
+ return buildReceiptVerificationFlow({
2035
+ receipt,
2036
+ validation,
2037
+ settlement,
2038
+ timelineEvents: events
2039
+ });
2040
+ }
2041
+ /**
2042
+ * Controlled receipt → verification demo.
2043
+ * Runs drift-restore, then validates receipt and looks up settlement.
2044
+ * Auth required.
2045
+ */
2046
+ async runReceiptVerificationDemo() {
2047
+ const driftFlow = await this.runDriftRestoreDemo();
2048
+ const receipt = driftFlow.phases.restored?.receipt;
2049
+ if (!receipt) {
2050
+ throw new AureonValidationError(
2051
+ "Restore receipt missing after drift-restore demo"
2052
+ );
2053
+ }
2054
+ return this.buildReceiptVerificationFlowForReceipt(receipt);
2055
+ }
2056
+ /**
2057
+ * Read-only receipt → verification flow for execution receipts.
2058
+ * Auth required.
2059
+ */
2060
+ async getReceiptVerificationFlow(executionId) {
2061
+ const executions = sortExecutionsNewestFirst(await this.listExecutions());
2062
+ const targets = executionId ? executions.filter((e) => e.id === executionId) : executions.slice(0, 5);
2063
+ if (targets.length === 0) {
2064
+ return [];
2065
+ }
2066
+ const timeline = await this.getTimeline();
2067
+ const flows = [];
2068
+ for (const receipt of targets) {
2069
+ flows.push(
2070
+ await this.buildReceiptVerificationFlowForReceipt(
2071
+ receipt,
2072
+ findTimelineEventsForReceipt(timeline, receipt)
2073
+ )
2074
+ );
2075
+ }
2076
+ return flows;
2077
+ }
2078
+ /**
2079
+ * Controlled portfolio watch demo.
2080
+ * User brief → Automatic objective → market move while away → auto restore → return briefing.
2081
+ * Auth required.
2082
+ */
2083
+ async runPortfolioWatchDemo(input) {
2084
+ const userBrief = input?.brief?.trim() || DEFAULT_PORTFOLIO_WATCH_BRIEF;
2085
+ const host = input?.host ?? "cursor";
2086
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
2087
+ const intent = {
2088
+ brief: userBrief,
2089
+ kind: "stable_allocation",
2090
+ targetWeight: 0.2,
2091
+ tolerance: 0.02
2092
+ };
2093
+ const setup = await this.applyFinancialIntent(intent);
2094
+ const objective = setup.objective;
2095
+ const registerHealth = setup.health;
2096
+ if (!registerHealth) {
2097
+ throw new AureonValidationError(
2098
+ "Register health missing after applyFinancialIntent"
2099
+ );
2100
+ }
2101
+ const baselineRows = await this.getAllocationVsTarget();
2102
+ const registerRow = baselineRows.rows.find(
2103
+ (r) => r.objectiveId === objective.id
2104
+ );
2105
+ const healthBeforeRecords = await this.getHealth(objective.id);
2106
+ const healthBefore = healthBeforeRecords[0];
2107
+ if (!healthBefore) {
2108
+ throw new AureonValidationError("Baseline health missing before market event");
2109
+ }
2110
+ const marketResult = await this.applyMarketEvent({
2111
+ name: "NVDA rally while you were away",
2112
+ description: "portfolio watch demo \u2014 auto restore on",
2113
+ symbol: "NVDA",
2114
+ priceChangeRatio: 0.45,
2115
+ autoRestore: true
2116
+ });
2117
+ const healthAfter = marketResult.health.find((h) => h.objectiveId === objective.id) ?? healthBefore;
2118
+ const receipt = marketResult.executions.find(
2119
+ (e) => e.objectiveId === objective.id
2120
+ );
2121
+ const timeline = await this.getTimeline(objective.id);
2122
+ return buildPortfolioWatchFlow({
2123
+ userBrief,
2124
+ host,
2125
+ objective,
2126
+ registerHealth,
2127
+ registerRow,
2128
+ whileAway: {
2129
+ marketEvent: marketResult.event,
2130
+ healthBefore,
2131
+ healthAfter,
2132
+ autoRestored: marketResult.executions.length > 0,
2133
+ receipt
2134
+ },
2135
+ briefingHealth: healthAfter,
2136
+ timelineEvents: timeline.slice(0, 10)
2137
+ });
2138
+ }
2139
+ /**
2140
+ * Read-only portfolio watch briefing for Automatic objectives.
2141
+ * Auth required.
2142
+ */
2143
+ async getPortfolioWatchFlow(input) {
2144
+ const userBrief = input?.brief?.trim() || DEFAULT_PORTFOLIO_WATCH_BRIEF;
2145
+ const host = input?.host ?? "mcp";
2146
+ const [objectives, healthRecords, allocation, timeline] = await Promise.all([
2147
+ input?.objectiveId ? [await this.getObjective(input.objectiveId)] : this.listObjectives(),
2148
+ this.getHealth(input?.objectiveId),
2149
+ this.getAllocationVsTarget(),
2150
+ this.getTimeline(input?.objectiveId)
2151
+ ]);
2152
+ const active = objectives.filter(
2153
+ (o) => (o.status === "active" || o.status === "validated") && (o.automationMode ?? "auto") === "auto"
2154
+ );
2155
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
2156
+ const rowById = new Map(allocation.rows.map((r) => [r.objectiveId, r]));
2157
+ const flows = [];
2158
+ for (const objective of active) {
2159
+ const health = healthById.get(objective.id);
2160
+ if (!health) continue;
2161
+ const objectiveTimeline = timeline.filter(
2162
+ (e) => !e.objectiveId || e.objectiveId === objective.id
2163
+ );
2164
+ flows.push(
2165
+ buildPortfolioWatchFlowFromSnapshot({
2166
+ userBrief,
2167
+ host,
2168
+ objective,
2169
+ health,
2170
+ allocationRow: rowById.get(objective.id),
2171
+ timelineEvents: objectiveTimeline.slice(0, 10)
2172
+ })
2173
+ );
2174
+ }
2175
+ return flows;
2176
+ }
2177
+ /**
2178
+ * Controlled full AUREON loop demo (Content Arc).
2179
+ * Intent → plan check (green vs plan with autoRestore false) → restore → receipt verification.
2180
+ * Auth required.
2181
+ */
2182
+ async runFullAureonLoopDemo(input) {
2183
+ const userBrief = input?.brief?.trim() || DEFAULT_FULL_LOOP_BRIEF;
2184
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
2185
+ const intent = {
2186
+ brief: userBrief,
2187
+ kind: "stable_allocation",
2188
+ targetWeight: 0.2,
2189
+ tolerance: 0.02
2190
+ };
2191
+ const setup = await this.applyFinancialIntent(intent);
2192
+ const objective = setup.objective;
2193
+ const baselineHealth = setup.health;
2194
+ if (!baselineHealth) {
2195
+ throw new AureonValidationError(
2196
+ "Baseline health missing after applyFinancialIntent"
2197
+ );
2198
+ }
2199
+ await this.applyMarketEvent({
2200
+ name: "NVDA rally \u2014 green book, off-plan sleeve",
2201
+ description: "Full loop \u2014 autoRestore false to expose plan paradox",
2202
+ symbol: "NVDA",
2203
+ priceChangeRatio: 0.45,
2204
+ autoRestore: false
2205
+ });
2206
+ const [afterShockHealthRecords, allocation] = await Promise.all([
2207
+ this.getHealth(objective.id),
2208
+ this.getAllocationVsTarget()
2209
+ ]);
2210
+ const afterShockHealth = afterShockHealthRecords[0];
2211
+ if (!afterShockHealth) {
2212
+ throw new AureonValidationError("Health missing after market event");
2213
+ }
2214
+ const afterShockRow = allocation.rows.find(
2215
+ (r) => r.objectiveId === objective.id
2216
+ );
2217
+ const receipt = await this.restoreObjective(objective.id);
2218
+ const restoredHealthRecords = await this.getHealth(objective.id);
2219
+ const restoredHealth = restoredHealthRecords[0] ?? afterShockHealth;
2220
+ const verification = await this.buildReceiptVerificationFlowForReceipt(receipt);
2221
+ return buildFullAureonLoopFlow({
2222
+ userBrief,
2223
+ objective,
2224
+ baselineHealth,
2225
+ afterShockHealth,
2226
+ afterShockRow,
2227
+ paradox: allocation.paradox,
2228
+ restoredHealth,
2229
+ receipt,
2230
+ verification
2231
+ });
2232
+ }
2233
+ /**
2234
+ * Read-only full AUREON loop for active objectives with a latest receipt.
2235
+ * Auth required.
2236
+ */
2237
+ async getFullAureonLoopFlow(input) {
2238
+ const userBrief = input?.brief?.trim() || DEFAULT_FULL_LOOP_BRIEF;
2239
+ const [objectives, healthRecords, allocation, executions] = await Promise.all([
2240
+ input?.objectiveId ? [await this.getObjective(input.objectiveId)] : this.listObjectives(),
2241
+ this.getHealth(input?.objectiveId),
2242
+ this.getAllocationVsTarget(),
2243
+ this.listExecutions(input?.objectiveId)
2244
+ ]);
2245
+ const active = objectives.filter(
2246
+ (o) => o.status === "active" || o.status === "validated"
2247
+ );
2248
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
2249
+ const rowById = new Map(allocation.rows.map((r) => [r.objectiveId, r]));
2250
+ const receiptsByObjective = /* @__PURE__ */ new Map();
2251
+ for (const receipt of sortExecutionsNewestFirst(executions)) {
2252
+ if (!receiptsByObjective.has(receipt.objectiveId)) {
2253
+ receiptsByObjective.set(receipt.objectiveId, receipt);
2254
+ }
2255
+ }
2256
+ const flows = [];
2257
+ for (const objective of active) {
2258
+ const health = healthById.get(objective.id);
2259
+ if (!health) continue;
2260
+ const latestReceipt = receiptsByObjective.get(objective.id);
2261
+ if (!latestReceipt) continue;
2262
+ const verification = await this.buildReceiptVerificationFlowForReceipt(latestReceipt);
2263
+ const flow = buildFullAureonLoopFlowFromSnapshot({
2264
+ userBrief,
2265
+ objective,
2266
+ health,
2267
+ allocationRow: rowById.get(objective.id),
2268
+ paradox: allocation.paradox,
2269
+ latestReceipt,
2270
+ verification
2271
+ });
2272
+ if (flow) flows.push(flow);
2273
+ }
2274
+ return flows;
2275
+ }
747
2276
  /**
748
2277
  * Applies a controlled market event to portfolio marks.
749
- * When autoRestore is true, the API evaluates health and may run staged restorative execution.
2278
+ * When autoRestore is true, the API may run restore. Omit or false = drift only, no restore. Automatic still 409s if the vault cannot execute.
750
2279
  * Auth required.
751
2280
  */
752
2281
  async applyMarketEvent(input) {
@@ -790,7 +2319,8 @@ var AureonClient = class {
790
2319
  });
791
2320
  }
792
2321
  /**
793
- * Runs vault-backed restorative execution for an objective outside policy.
2322
+ * Runs restorative execution for an objective outside policy.
2323
+ * Receipt.settlement may be vault or staged. Only verifiedOnChain is proof.
794
2324
  * Auth required.
795
2325
  */
796
2326
  async restoreObjective(objectiveId) {
@@ -808,6 +2338,35 @@ var AureonClient = class {
808
2338
  );
809
2339
  return result.executions;
810
2340
  }
2341
+ /** Returns chain-verified settlement record for an execution when present. Auth required. */
2342
+ async getExecutionSettlement(executionId) {
2343
+ assertId(executionId, "execution id");
2344
+ return requestJson(this.transport, executionSettlementPath(executionId));
2345
+ }
2346
+ /** Lists chain-verified settlement records for the authenticated wallet. Auth required. */
2347
+ async listSettlements(objectiveId) {
2348
+ const path = withQuery(ENDPOINTS.settlements, { objectiveId });
2349
+ const result = await requestJson(
2350
+ this.transport,
2351
+ path
2352
+ );
2353
+ return result.settlements;
2354
+ }
2355
+ /**
2356
+ * Manual backfill: verify a vault tx on-chain and attach settlement proof.
2357
+ * Auth required.
2358
+ */
2359
+ async confirmExecutionSettlement(executionId, transactionHash) {
2360
+ assertId(executionId, "execution id");
2361
+ const hash = transactionHash.trim();
2362
+ if (!hash) {
2363
+ throw new AureonValidationError("transactionHash is required");
2364
+ }
2365
+ return requestJson(this.transport, executionConfirmSettlementPath(executionId), {
2366
+ method: "POST",
2367
+ body: { transactionHash: hash }
2368
+ });
2369
+ }
811
2370
  /** Returns Phase 2 ObjectiveRegistry deployment status. Auth required. */
812
2371
  async getRegistryStatus() {
813
2372
  return requestJson(this.transport, ENDPOINTS.registryStatus);
@@ -928,19 +2487,59 @@ var AureonClient = class {
928
2487
  method: "POST"
929
2488
  });
930
2489
  }
2490
+ /**
2491
+ * Joins objective → registry → receipts → settlements → timeline.
2492
+ * Missing proof is labeled as a gap. Nothing is invented. Auth required.
2493
+ */
2494
+ async getAuditTrail(objectiveId) {
2495
+ assertId(objectiveId, "objective id");
2496
+ const [objective, healthRows, receipts, settlementsResult, timeline] = await Promise.all([
2497
+ this.getObjective(objectiveId),
2498
+ this.getHealth(objectiveId),
2499
+ this.listExecutions(objectiveId),
2500
+ this.listSettlements(objectiveId).then(
2501
+ (rows) => ({ ok: true, rows }),
2502
+ () => ({ ok: false, rows: [] })
2503
+ ),
2504
+ this.getTimeline(objectiveId)
2505
+ ]);
2506
+ let registry = {
2507
+ registered: false,
2508
+ objectiveId
2509
+ };
2510
+ let registryLookupFailed = false;
2511
+ try {
2512
+ registry = await this.getObjectiveRegistry(objectiveId);
2513
+ } catch {
2514
+ registryLookupFailed = true;
2515
+ }
2516
+ return buildFinancialAuditTrail({
2517
+ objective,
2518
+ health: healthRows[0],
2519
+ registry,
2520
+ receipts: sortExecutionsNewestFirst(receipts),
2521
+ settlements: settlementsResult.rows,
2522
+ timeline,
2523
+ registryLookupFailed,
2524
+ settlementsLookupFailed: !settlementsResult.ok
2525
+ });
2526
+ }
931
2527
  };
932
2528
 
933
2529
  // src/client/factory.ts
934
2530
  function createAureonClient(options = {}) {
935
- return new AureonClient({
936
- baseUrl: options.baseUrl ?? DEFAULT_API_BASE_URL,
937
- ...options
938
- });
2531
+ return new AureonClient(options);
939
2532
  }
940
2533
  function createLocalAureonClient(overrides = {}) {
2534
+ if (overrides.network && overrides.network !== "mainnet") {
2535
+ throw new Error(
2536
+ 'createLocalAureonClient is mainnet-only (8788 / 4663). Use createAureonClient({ network: "testnet" }) for the public host.'
2537
+ );
2538
+ }
941
2539
  return new AureonClient({
942
2540
  ...overrides,
943
- baseUrl: overrides.baseUrl ?? LOCAL_API_BASE_URL
2541
+ network: "mainnet",
2542
+ baseUrl: overrides.baseUrl ?? MAINNET_API_BASE_URL
944
2543
  });
945
2544
  }
946
2545
 
@@ -1039,15 +2638,24 @@ var TIMELINE_EVENT_TYPES = [
1039
2638
  "capital_provisioned",
1040
2639
  "capital_cleared",
1041
2640
  "capital_synced",
1042
- "registry_anchored"
2641
+ "registry_registered",
2642
+ "settlement_recorded"
1043
2643
  ];
1044
2644
  function isTimelineEventType(value) {
1045
2645
  return TIMELINE_EVENT_TYPES.includes(value);
1046
2646
  }
1047
2647
 
1048
- // src/types/execution.ts
1049
- function isVaultSettlement(receipt) {
1050
- return receipt.settlement === "vault";
2648
+ // src/types/settlement.ts
2649
+ function formatSettlementSummary(record) {
2650
+ const parts = [
2651
+ "vault settlement (chain-verified)",
2652
+ `block ${record.blockNumber}`,
2653
+ record.explorerUrl
2654
+ ];
2655
+ if (record.registryRef) {
2656
+ parts.push(`registry ${record.registryRef.objectiveKey.slice(0, 10)}\u2026`);
2657
+ }
2658
+ return parts.join(" \xB7 ");
1051
2659
  }
1052
2660
 
1053
2661
  // src/adapters/logging-adapter.ts
@@ -1076,6 +2684,6 @@ function createConsoleLogger(prefix = "aureon-sdk") {
1076
2684
  };
1077
2685
  }
1078
2686
 
1079
- export { API_KEY_HEADER, AureonClient, AureonConflictError, AureonError, AureonNetworkError, AureonNotFoundError, AureonTimeoutError, AureonValidationError, DEFAULT_API_BASE_URL, DEFAULT_TIMEOUT_MS, ENDPOINTS, LOCAL_API_BASE_URL, OBJECTIVE_KINDS, OBJECTIVE_PRIORITIES, PRODUCT_NAME, PRODUCT_TAGLINE, SDK_NAME, SDK_VERSION, TIMELINE_EVENT_TYPES, assertBaseUrl, buildPolicySummary, createAureonClient, createConsoleLogger, createLocalAureonClient, createSessionTokenProvider, errorFromHttpStatus, formatIsoTime, formatSignedPercent, formatUsd, formatWeight, healthTone, isAureonError, isHealthState, isObjectiveKind, isObjectivePriority, isTimelineEventType, isVaultSettlement, joinUrl, normalizeCreateObjectiveInput, normalizeUpdateObjectiveInput, pickWorstHealth, requestJson, resolveFetch, silentLogger, withQuery };
2687
+ export { API_KEY_HEADER, AUREON_NETWORKS, AureonClient, AureonConflictError, AureonError, AureonNetworkError, AureonNotFoundError, AureonTimeoutError, AureonValidationError, DEFAULT_API_BASE_URL, DEFAULT_FULL_LOOP_BRIEF, DEFAULT_PORTFOLIO_WATCH_BRIEF, DEFAULT_TIMEOUT_MS, ENDPOINTS, LOCAL_API_BASE_URL, MAINNET_API_BASE_URL, MAINNET_CHAIN_ID, OBJECTIVE_KINDS, OBJECTIVE_PRIORITIES, PRODUCT_NAME, PRODUCT_TAGLINE, SDK_NAME, SDK_VERSION, TESTNET_API_BASE_URL, TESTNET_CHAIN_ID, TIMELINE_EVENT_TYPES, assertBaseUrl, assertValidExecutionReceipt, buildAllocationComparison, buildDriftRestoreFlow, buildDriftRestoreFlowFromSnapshot, buildFinancialAuditTrail, buildFullAureonLoopFlow, buildFullAureonLoopFlowFromSnapshot, buildObjectivePortfolioFlow, buildPolicySummary, buildPortfolioWatchBriefingLines, buildPortfolioWatchFlow, buildPortfolioWatchFlowFromSnapshot, buildReceiptVerificationFlow, createAureonClient, createConsoleLogger, createLocalAureonClient, createSessionTokenProvider, detectPlanParadox, errorFromHttpStatus, findTimelineEventsForReceipt, formatAuditTrailLines, formatIsoTime, formatReceiptSummary, formatSettlementSummary, formatSignedPercent, formatUsd, formatWeight, healthTone, inferAureonNetworkFromUrl, inferDriftPhase, inferFullAureonLoopPhase, inferPortfolioWatchPhase, inferProofTier, isAureonError, isChainVerifiedReceipt, isHealthState, isObjectiveKind, isObjectivePriority, isTimelineEventType, isValidExecutionReceipt, isVaultSettlement, joinUrl, normalizeCreateObjectiveInput, normalizeUpdateObjectiveInput, parseFinancialIntent, pickWorstHealth, requestJson, resolveAureonNetwork, resolveAureonNetworkFromEnv, resolveFetch, resolveObjectiveFromIntent, silentLogger, validateExecutionReceipt, validateSettlementRecord, withQuery };
1080
2688
  //# sourceMappingURL=index.js.map
1081
2689
  //# sourceMappingURL=index.js.map