@buildaureon/sdk 0.1.2 → 0.1.7

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
@@ -14,7 +14,7 @@ function userAgentHeader(version) {
14
14
  var DEFAULT_API_BASE_URL = "https://api.aureonlabs.network";
15
15
  var LOCAL_API_BASE_URL = "http://127.0.0.1:8787";
16
16
  var DEFAULT_TIMEOUT_MS = 3e4;
17
- var SDK_VERSION = "0.1.2";
17
+ var SDK_VERSION = "0.1.7";
18
18
  var SDK_NAME = "@buildaureon/sdk";
19
19
  var PRODUCT_NAME = "AUREON";
20
20
  var PRODUCT_TAGLINE = "Financial Compass for Robinhood Chain";
@@ -45,7 +45,8 @@ var ENDPOINTS = {
45
45
  authDevLogin: "/auth/dev-login",
46
46
  authMe: "/auth/me",
47
47
  developerApiKeys: "/developer/api-keys",
48
- registryStatus: "/registry/status"
48
+ registryStatus: "/registry/status",
49
+ settlements: "/settlements"
49
50
  };
50
51
  function objectivePath(id) {
51
52
  return `${ENDPOINTS.objectives}/${encodeURIComponent(id)}`;
@@ -74,6 +75,12 @@ function registryPreparePath(id) {
74
75
  function registryConfirmPath(id) {
75
76
  return `${registryObjectivePath(id)}/confirm`;
76
77
  }
78
+ function executionSettlementPath(id) {
79
+ return `/executions/${encodeURIComponent(id)}/settlement`;
80
+ }
81
+ function executionConfirmSettlementPath(id) {
82
+ return `/executions/${encodeURIComponent(id)}/confirm-settlement`;
83
+ }
77
84
 
78
85
  // src/errors/codes.ts
79
86
  var RETRYABLE_CODES = [
@@ -168,22 +175,1110 @@ var AureonTimeoutError = class extends AureonError {
168
175
  }
169
176
  };
170
177
 
171
- // src/errors/http.ts
178
+ // src/formatting/allocation.ts
179
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["active", "validated"]);
180
+ function isOffPlan(state) {
181
+ return state === "warning" || state === "violation";
182
+ }
183
+ function buildAllocationComparison(objectives, health) {
184
+ const healthById = new Map(health.map((h) => [h.objectiveId, h]));
185
+ return objectives.filter((o) => ACTIVE_STATUSES.has(o.status)).map((objective) => {
186
+ const record = healthById.get(objective.id);
187
+ const targetWeight = objective.policy?.targetWeight ?? record?.targetMetric ?? 0;
188
+ const currentMetric = record?.currentMetric ?? 0;
189
+ return {
190
+ objectiveId: objective.id,
191
+ name: objective.name,
192
+ kind: objective.kind,
193
+ targetSymbol: objective.policy?.targetSymbol,
194
+ targetWeight,
195
+ currentMetric,
196
+ deviation: record?.deviation ?? currentMetric - targetWeight,
197
+ state: record?.state ?? "paused"
198
+ };
199
+ });
200
+ }
201
+ function detectPlanParadox(overview, health) {
202
+ const offPlan = health.filter((h) => isOffPlan(h.state));
203
+ const offPlanCount = offPlan.length;
204
+ const bookUp = overview.change24hPct != null && !overview.change24hBaselineOnly ? overview.change24hPct >= 0 : overview.attentionCount > 0 && overview.totalNotionalUsd > 0;
205
+ const detected = bookUp && offPlanCount > 0;
206
+ let message = "Portfolio and objectives are aligned.";
207
+ if (detected) {
208
+ const pct = overview.change24hPct != null && !overview.change24hBaselineOnly ? `${(overview.change24hPct * 100).toFixed(1)}%` : "recent activity";
209
+ message = `Book is up (${pct}), but ${offPlanCount} objective${offPlanCount === 1 ? "" : "s"} are off-plan.`;
210
+ } else if (offPlanCount > 0) {
211
+ message = `${offPlanCount} objective${offPlanCount === 1 ? "" : "s"} need attention.`;
212
+ }
213
+ return { detected, bookUp, offPlanCount, message };
214
+ }
215
+
216
+ // src/types/objective.ts
217
+ var OBJECTIVE_KINDS = [
218
+ "stable_allocation",
219
+ "balanced_portfolio",
220
+ "risk_ceiling",
221
+ "reward_reinvestment"
222
+ ];
223
+ var OBJECTIVE_PRIORITIES = [
224
+ "low",
225
+ "medium",
226
+ "high",
227
+ "critical"
228
+ ];
229
+ function isObjectiveKind(value) {
230
+ return OBJECTIVE_KINDS.includes(value);
231
+ }
232
+ function isObjectivePriority(value) {
233
+ return OBJECTIVE_PRIORITIES.includes(value);
234
+ }
235
+
236
+ // src/validation/objective-input.ts
237
+ function buildPolicySummary(kind, targetWeight, tolerance) {
238
+ const targetPct = (targetWeight * 100).toFixed(1);
239
+ const tolPct = (tolerance * 100).toFixed(1);
240
+ switch (kind) {
241
+ case "stable_allocation":
242
+ return `Maintain ${targetPct}% stable allocation within \xB1${tolPct}%`;
243
+ case "balanced_portfolio":
244
+ return `Hold balanced weights near ${targetPct}% primary sleeve within \xB1${tolPct}%`;
245
+ case "risk_ceiling":
246
+ return `Keep portfolio risk at or below configured ceiling with ${tolPct}% buffer`;
247
+ case "reward_reinvestment":
248
+ return `Reinvest available rewards toward ${targetPct}% target sleeve`;
249
+ default:
250
+ return `Objective policy target ${targetPct}% \xB1${tolPct}%`;
251
+ }
252
+ }
253
+ function normalizeCreateObjectiveInput(input) {
254
+ const name = input.name?.trim();
255
+ if (!name || name.length < 3) {
256
+ throw new AureonValidationError("Objective name must be at least 3 characters");
257
+ }
258
+ if (!isObjectiveKind(input.kind)) {
259
+ throw new AureonValidationError(`Unsupported objective kind: ${input.kind}`);
260
+ }
261
+ if (input.targetWeight < 0 || input.targetWeight > 1) {
262
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
263
+ }
264
+ if (input.tolerance < 0 || input.tolerance > 0.5) {
265
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
266
+ }
267
+ const priority = input.priority ?? "high";
268
+ if (!isObjectivePriority(priority)) {
269
+ throw new AureonValidationError(`Unsupported priority: ${priority}`);
270
+ }
271
+ if (input.kind === "balanced_portfolio") {
272
+ const symbol = input.targetSymbol?.trim().toUpperCase();
273
+ if (!symbol) {
274
+ throw new AureonValidationError(
275
+ "balanced_portfolio requires targetSymbol"
276
+ );
277
+ }
278
+ return {
279
+ ...input,
280
+ name,
281
+ priority,
282
+ targetSymbol: symbol,
283
+ // SDK / agent path defaults to Automatic. Explicit "manual" is reserved
284
+ // for the operator utility Approve UX; not recommended for integrations.
285
+ automationMode: input.automationMode === "manual" ? "manual" : "auto"
286
+ };
287
+ }
288
+ return {
289
+ ...input,
290
+ name,
291
+ priority,
292
+ targetSymbol: null,
293
+ automationMode: input.automationMode === "manual" ? "manual" : "auto"
294
+ };
295
+ }
296
+ function normalizeUpdateObjectiveInput(input) {
297
+ const next = { ...input };
298
+ if (next.name !== void 0) {
299
+ const name = next.name.trim();
300
+ if (name.length < 3) {
301
+ throw new AureonValidationError("Objective name must be at least 3 characters");
302
+ }
303
+ next.name = name;
304
+ }
305
+ if (next.targetWeight !== void 0) {
306
+ if (next.targetWeight < 0 || next.targetWeight > 1) {
307
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
308
+ }
309
+ }
310
+ if (next.tolerance !== void 0) {
311
+ if (next.tolerance < 0 || next.tolerance > 0.5) {
312
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
313
+ }
314
+ }
315
+ if (next.priority !== void 0 && !isObjectivePriority(next.priority)) {
316
+ throw new AureonValidationError(`Unsupported priority: ${next.priority}`);
317
+ }
318
+ if (next.automationMode !== void 0) {
319
+ throw new AureonValidationError(
320
+ "automationMode cannot be changed after create: recreate the objective instead"
321
+ );
322
+ }
323
+ if (input.targetSymbol !== void 0) {
324
+ throw new AureonValidationError(
325
+ "targetSymbol cannot be changed after create: recreate the objective instead"
326
+ );
327
+ }
328
+ return next;
329
+ }
330
+ function assertId(value, label) {
331
+ if (!value || typeof value !== "string" || value.trim().length < 8) {
332
+ throw new AureonValidationError(`Invalid ${label}`);
333
+ }
334
+ }
335
+
336
+ // src/formatting/drift-restore.ts
337
+ function isOffPlan2(state) {
338
+ return state === "warning" || state === "violation";
339
+ }
340
+ function inferDriftPhase(health) {
341
+ if (health.state === "healthy") return "aligned";
342
+ if (isOffPlan2(health.state)) return "drift_detected";
343
+ return "aligned";
344
+ }
345
+ function buildDriftRestoreFlow(input) {
346
+ const targetWeight = input.objective.policy?.targetWeight ?? input.alignedHealth.targetMetric ?? 0;
347
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
348
+ const summary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
349
+ const restored = input.restoredHealth ? {
350
+ health: input.restoredHealth,
351
+ receipt: input.receipt,
352
+ settlement: input.receipt?.settlement
353
+ } : void 0;
354
+ const currentPhase = restored ? restored.health.state === "healthy" || !isOffPlan2(restored.health.state) ? "restored" : "drift_detected" : inferDriftPhase(input.driftHealth);
355
+ let message = "Rule set and portfolio aligned. Ready to detect drift when marks move.";
356
+ if (currentPhase === "drift_detected") {
357
+ message = "We broke the rule on purpose. AUREON detected drift \u2014 allocation moved off policy.";
358
+ } else if (currentPhase === "restored") {
359
+ const settlement = input.receipt?.settlement ?? "staged";
360
+ message = `Drift detected and restore completed (${settlement} settlement). Policy is back within tolerance.`;
361
+ }
362
+ return {
363
+ objectiveId: input.objective.id,
364
+ rule: { summary, targetWeight, tolerance },
365
+ phases: {
366
+ aligned: {
367
+ health: input.alignedHealth,
368
+ allocationRow: input.alignedRow
369
+ },
370
+ drift: {
371
+ health: input.driftHealth,
372
+ allocationRow: input.driftRow,
373
+ restorePlan: input.driftPlan
374
+ },
375
+ restored
376
+ },
377
+ currentPhase,
378
+ message
379
+ };
380
+ }
381
+ function buildDriftRestoreFlowFromSnapshot(input) {
382
+ const targetWeight = input.objective.policy?.targetWeight ?? input.health.targetMetric ?? 0;
383
+ const offPlan = isOffPlan2(input.health.state);
384
+ const hasRestore = input.latestReceipt && input.latestReceipt.objectiveId === input.objective.id;
385
+ if (!offPlan && !hasRestore) {
386
+ return buildDriftRestoreFlow({
387
+ objective: input.objective,
388
+ alignedHealth: input.health,
389
+ driftHealth: input.health,
390
+ alignedRow: input.allocationRow,
391
+ driftRow: input.allocationRow
392
+ });
393
+ }
394
+ if (offPlan) {
395
+ return buildDriftRestoreFlow({
396
+ objective: input.objective,
397
+ alignedHealth: {
398
+ ...input.health,
399
+ state: "healthy",
400
+ currentMetric: targetWeight,
401
+ deviation: 0,
402
+ message: "On track \u2014 still inside your target range."
403
+ },
404
+ driftHealth: input.health,
405
+ driftPlan: input.restorePlan,
406
+ alignedRow: input.allocationRow,
407
+ driftRow: input.allocationRow
408
+ });
409
+ }
410
+ return buildDriftRestoreFlow({
411
+ objective: input.objective,
412
+ alignedHealth: {
413
+ ...input.health,
414
+ state: "healthy",
415
+ currentMetric: targetWeight,
416
+ deviation: 0,
417
+ message: "On track \u2014 still inside your target range."
418
+ },
419
+ driftHealth: {
420
+ ...input.health,
421
+ state: "warning",
422
+ message: "Prior drift detected."
423
+ },
424
+ restoredHealth: input.health,
425
+ receipt: input.latestReceipt,
426
+ alignedRow: input.allocationRow,
427
+ driftRow: input.allocationRow
428
+ });
429
+ }
430
+
431
+ // src/types/execution.ts
432
+ function shortTransactionHash(hash, head = 10, tail = 6) {
433
+ if (hash.length <= head + tail + 1) return hash;
434
+ return `${hash.slice(0, head)}\u2026${hash.slice(-tail)}`;
435
+ }
436
+ function sortExecutionsNewestFirst(receipts) {
437
+ return [...receipts].sort((a, b) => a.createdAt < b.createdAt ? 1 : -1);
438
+ }
439
+ function isVaultSettlement(receipt) {
440
+ return receipt.settlement === "vault";
441
+ }
442
+ function isChainVerifiedReceipt(receipt) {
443
+ return receipt.verifiedOnChain === true;
444
+ }
445
+ function formatReceiptSummary(receipt) {
446
+ const settlementLabel = receipt.verifiedOnChain ? "vault settlement (chain-verified)" : receipt.settlement === "vault" ? "vault settlement (unverified on-chain)" : "staged settlement (capital book)";
447
+ const parts = [receipt.action, settlementLabel, receipt.status];
448
+ if (receipt.explorerUrl) parts.push(receipt.explorerUrl);
449
+ if (receipt.registryRef) {
450
+ parts.push(
451
+ `registry ${shortTransactionHash(receipt.registryRef.objectiveKey, 8, 4)}`
452
+ );
453
+ }
454
+ return parts.join(" \xB7 ");
455
+ }
456
+ function findTimelineEventsForReceipt(events, receipt) {
457
+ return events.filter((event) => {
458
+ if (event.type !== "execution_started" && event.type !== "execution_completed") {
459
+ return false;
460
+ }
461
+ const executionId = event.payload?.executionId;
462
+ return typeof executionId === "string" && executionId === receipt.id;
463
+ });
464
+ }
465
+
466
+ // src/validation/receipt-validator.ts
467
+ var EXECUTION_STATUSES = /* @__PURE__ */ new Set([
468
+ "pending",
469
+ "submitted",
470
+ "confirmed",
471
+ "failed"
472
+ ]);
473
+ var SETTLEMENTS = /* @__PURE__ */ new Set(["vault", "staged"]);
474
+ var TX_HASH_0X = /^0x[a-fA-F0-9]{64}$/;
475
+ var ADDRESS_0X = /^0x[a-fA-F0-9]{40}$/;
476
+ var BYTES32_0X = /^0x[a-fA-F0-9]{64}$/;
477
+ var ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T/;
478
+ function issue(code, message, path) {
479
+ return path ? { code, message, path } : { code, message };
480
+ }
172
481
  function isRecord(value) {
173
482
  return typeof value === "object" && value !== null && !Array.isArray(value);
174
483
  }
484
+ function isNonEmptyString(value) {
485
+ return typeof value === "string" && value.trim().length > 0;
486
+ }
487
+ function isVaultTransactionHash(hash) {
488
+ return hash.startsWith("pending_vault_") || TX_HASH_0X.test(hash);
489
+ }
490
+ function isRealVaultTxHash(hash) {
491
+ return TX_HASH_0X.test(hash);
492
+ }
493
+ function isStagedTransactionHash(hash) {
494
+ return !isVaultTransactionHash(hash);
495
+ }
496
+ function validateSettlementRecord(input, options = {}) {
497
+ const issues = [];
498
+ if (!isRecord(input)) {
499
+ return {
500
+ valid: false,
501
+ issues: [issue("INVALID_INPUT", "Settlement record must be an object")]
502
+ };
503
+ }
504
+ const requiredStrings = [
505
+ ["id", "id"],
506
+ ["walletAddress", "walletAddress"],
507
+ ["transactionHash", "transactionHash"],
508
+ ["vaultAddress", "vaultAddress"],
509
+ ["tokenSell", "tokenSell"],
510
+ ["tokenBuy", "tokenBuy"],
511
+ ["amountIn", "amountIn"],
512
+ ["amountOut", "amountOut"],
513
+ ["explorerUrl", "explorerUrl"],
514
+ ["verifiedAt", "verifiedAt"],
515
+ ["status", "status"]
516
+ ];
517
+ for (const [key, path] of requiredStrings) {
518
+ if (!isNonEmptyString(input[key])) {
519
+ issues.push(issue("MISSING_FIELD", `Missing ${key}`, path));
520
+ }
521
+ }
522
+ if (input.settlement !== "vault") {
523
+ issues.push(
524
+ issue(
525
+ "INVALID_SETTLEMENT_RECORD",
526
+ 'settlement must be "vault" on chain records',
527
+ "settlement"
528
+ )
529
+ );
530
+ }
531
+ if (typeof input.blockNumber !== "number" || !Number.isFinite(input.blockNumber) || input.blockNumber < 0) {
532
+ issues.push(
533
+ issue("INVALID_SETTLEMENT_RECORD", "blockNumber must be a non-negative number", "blockNumber")
534
+ );
535
+ }
536
+ if (typeof input.logIndex !== "number" || !Number.isInteger(input.logIndex) || input.logIndex < 0) {
537
+ issues.push(
538
+ issue("INVALID_SETTLEMENT_RECORD", "logIndex must be a non-negative integer", "logIndex")
539
+ );
540
+ }
541
+ const tx = String(input.transactionHash ?? "");
542
+ if (tx && !TX_HASH_0X.test(tx)) {
543
+ issues.push(
544
+ issue("INVALID_SETTLEMENT_RECORD", "transactionHash must be a 32-byte hex hash", "transactionHash")
545
+ );
546
+ }
547
+ if (input.status !== "confirmed" && input.status !== "orphan") {
548
+ issues.push(
549
+ issue(
550
+ "INVALID_SETTLEMENT_RECORD",
551
+ 'status must be "confirmed" or "orphan"',
552
+ "status"
553
+ )
554
+ );
555
+ }
556
+ if (options.executionId && input.executionId != null && input.executionId !== options.executionId) {
557
+ issues.push(
558
+ issue(
559
+ "VERIFIED_RECORD_MISMATCH",
560
+ "settlementRecord.executionId must match receipt.id",
561
+ "executionId"
562
+ )
563
+ );
564
+ }
565
+ if (isRecord(input.registryRef)) {
566
+ const refIssues = validateRegistryRef(input.registryRef, "registryRef");
567
+ issues.push(...refIssues);
568
+ }
569
+ return { valid: issues.length === 0, issues };
570
+ }
571
+ function validateRegistryRef(ref, basePath) {
572
+ const issues = [];
573
+ const objectiveKey = ref.objectiveKey;
574
+ const contractAddress = ref.contractAddress;
575
+ if (!isNonEmptyString(objectiveKey) || !BYTES32_0X.test(objectiveKey)) {
576
+ issues.push(
577
+ issue(
578
+ "INVALID_REGISTRY_REF",
579
+ "objectiveKey must be a 32-byte hex string",
580
+ `${basePath}.objectiveKey`
581
+ )
582
+ );
583
+ }
584
+ if (!isNonEmptyString(contractAddress) || !ADDRESS_0X.test(contractAddress)) {
585
+ issues.push(
586
+ issue(
587
+ "INVALID_REGISTRY_REF",
588
+ "contractAddress must be a 20-byte hex address",
589
+ `${basePath}.contractAddress`
590
+ )
591
+ );
592
+ }
593
+ return issues;
594
+ }
595
+ function validateExecutionReceipt(input) {
596
+ const issues = [];
597
+ if (!isRecord(input)) {
598
+ return {
599
+ valid: false,
600
+ issues: [issue("INVALID_INPUT", "Receipt must be an object")]
601
+ };
602
+ }
603
+ const required = [
604
+ ["id", "id"],
605
+ ["objectiveId", "objectiveId"],
606
+ ["action", "action"],
607
+ ["status", "status"],
608
+ ["transactionHash", "transactionHash"],
609
+ ["result", "result"],
610
+ ["createdAt", "createdAt"],
611
+ ["settlement", "settlement"]
612
+ ];
613
+ for (const [key, path] of required) {
614
+ if (!isNonEmptyString(input[key])) {
615
+ issues.push(issue("MISSING_FIELD", `Missing ${key}`, path));
616
+ }
617
+ }
618
+ const settlement = input.settlement;
619
+ if (settlement != null && !SETTLEMENTS.has(String(settlement))) {
620
+ issues.push(
621
+ issue(
622
+ "INVALID_SETTLEMENT",
623
+ 'settlement must be "vault" or "staged"',
624
+ "settlement"
625
+ )
626
+ );
627
+ }
628
+ const status = input.status;
629
+ if (status != null && !EXECUTION_STATUSES.has(String(status))) {
630
+ issues.push(
631
+ issue(
632
+ "INVALID_STATUS",
633
+ "status must be pending, submitted, confirmed, or failed",
634
+ "status"
635
+ )
636
+ );
637
+ }
638
+ if (status === "confirmed" && !isNonEmptyString(input.confirmedAt)) {
639
+ issues.push(
640
+ issue("MISSING_CONFIRMED_AT", "confirmed status requires confirmedAt", "confirmedAt")
641
+ );
642
+ }
643
+ if (isNonEmptyString(input.createdAt) && !ISO_TIMESTAMP.test(input.createdAt)) {
644
+ issues.push(
645
+ issue("MISSING_FIELD", "createdAt must be an ISO-8601 timestamp", "createdAt")
646
+ );
647
+ }
648
+ const txHash = String(input.transactionHash ?? "");
649
+ const settlementStr = String(settlement ?? "");
650
+ if (settlementStr === "staged") {
651
+ if (input.explorerUrl != null && input.explorerUrl !== "") {
652
+ issues.push(
653
+ issue(
654
+ "STAGED_WITH_EXPLORER",
655
+ "staged receipts must not include explorerUrl",
656
+ "explorerUrl"
657
+ )
658
+ );
659
+ }
660
+ if (input.verifiedOnChain === true) {
661
+ issues.push(
662
+ issue(
663
+ "STAGED_VERIFIED_ON_CHAIN",
664
+ "staged receipts cannot be verifiedOnChain",
665
+ "verifiedOnChain"
666
+ )
667
+ );
668
+ }
669
+ if (input.settlementRecord != null) {
670
+ issues.push(
671
+ issue(
672
+ "STAGED_WITH_SETTLEMENT_RECORD",
673
+ "staged receipts must not include settlementRecord",
674
+ "settlementRecord"
675
+ )
676
+ );
677
+ }
678
+ if (txHash && !isStagedTransactionHash(txHash)) {
679
+ issues.push(
680
+ issue(
681
+ "INVALID_VAULT_HASH",
682
+ "staged settlement must not use a vault transaction hash",
683
+ "transactionHash"
684
+ )
685
+ );
686
+ }
687
+ }
688
+ if (settlementStr === "vault") {
689
+ if (txHash && !isVaultTransactionHash(txHash)) {
690
+ issues.push(
691
+ issue(
692
+ "INVALID_VAULT_HASH",
693
+ "vault settlement requires 0x\u2026 hash or pending_vault_* prefix",
694
+ "transactionHash"
695
+ )
696
+ );
697
+ }
698
+ if (isRealVaultTxHash(txHash)) {
699
+ if (input.explorerUrl == null || input.explorerUrl === "") {
700
+ issues.push(
701
+ issue(
702
+ "VAULT_MISSING_EXPLORER",
703
+ "confirmed vault tx must include explorerUrl",
704
+ "explorerUrl"
705
+ )
706
+ );
707
+ }
708
+ }
709
+ }
710
+ if (input.verifiedOnChain === true) {
711
+ if (settlementStr !== "vault") {
712
+ issues.push(
713
+ issue(
714
+ "VERIFIED_WITHOUT_RECORD",
715
+ "verifiedOnChain requires vault settlement",
716
+ "verifiedOnChain"
717
+ )
718
+ );
719
+ }
720
+ if (input.settlementRecord == null) {
721
+ issues.push(
722
+ issue(
723
+ "VERIFIED_WITHOUT_RECORD",
724
+ "verifiedOnChain requires settlementRecord",
725
+ "verifiedOnChain"
726
+ )
727
+ );
728
+ } else {
729
+ const nested = validateSettlementRecord(input.settlementRecord, {
730
+ executionId: isNonEmptyString(input.id) ? input.id : void 0
731
+ });
732
+ for (const nestedIssue of nested.issues) {
733
+ issues.push({
734
+ ...nestedIssue,
735
+ path: nestedIssue.path ? `settlementRecord.${nestedIssue.path}` : "settlementRecord"
736
+ });
737
+ }
738
+ }
739
+ } else if (input.settlementRecord != null && input.verifiedOnChain !== false) {
740
+ if (input.verifiedOnChain !== true) {
741
+ issues.push(
742
+ issue(
743
+ "VERIFIED_RECORD_MISMATCH",
744
+ "settlementRecord present but verifiedOnChain is not true",
745
+ "verifiedOnChain"
746
+ )
747
+ );
748
+ }
749
+ }
750
+ if (isRecord(input.registryRef)) {
751
+ issues.push(...validateRegistryRef(input.registryRef, "registryRef"));
752
+ }
753
+ return { valid: issues.length === 0, issues };
754
+ }
755
+ function isValidExecutionReceipt(input) {
756
+ return validateExecutionReceipt(input).valid;
757
+ }
758
+ function assertValidExecutionReceipt(receipt) {
759
+ const result = validateExecutionReceipt(receipt);
760
+ if (!result.valid) {
761
+ throw new AureonValidationError("Invalid execution receipt", {
762
+ issues: result.issues
763
+ });
764
+ }
765
+ }
766
+
767
+ // src/formatting/receipt-verification.ts
768
+ function inferProofTier(receipt, validation, settlement) {
769
+ if (!validation.valid) return "claim_only";
770
+ if (settlement?.verifiedOnChain === true || isChainVerifiedReceipt(receipt) || receipt.verifiedOnChain === true) {
771
+ return "chain_verified";
772
+ }
773
+ return "schema_valid";
774
+ }
775
+ function inferCurrentPhase(validation, proofTier) {
776
+ if (!validation.valid) return "validation_failed";
777
+ if (proofTier === "chain_verified") return "chain_verified";
778
+ return "validated";
779
+ }
780
+ function buildReceiptVerificationFlow(input) {
781
+ const validation = input.validation ?? validateExecutionReceipt(input.receipt);
782
+ const proofTier = inferProofTier(
783
+ input.receipt,
784
+ validation,
785
+ input.settlement
786
+ );
787
+ const currentPhase = inferCurrentPhase(validation, proofTier);
788
+ let message = "An AI saying 'transaction successful' is only a claim \u2014 validate the receipt before trusting it.";
789
+ if (currentPhase === "validation_failed") {
790
+ message = "Receipt failed validation \u2014 do not summarize as proof. Fix honesty issues before reporting success.";
791
+ } else if (currentPhase === "chain_verified") {
792
+ message = "Receipt passes validation and has independent on-chain settlement proof.";
793
+ } else if (proofTier === "schema_valid") {
794
+ const label = input.receipt.settlement === "staged" ? "staged (capital book)" : "vault (not yet chain-observed)";
795
+ message = `Receipt passes validation (${label}). Schema-valid does not mean chain-verified \u2014 check settlement lookup for vault proof.`;
796
+ }
797
+ return {
798
+ executionId: input.receipt.id,
799
+ receipt: input.receipt,
800
+ phases: {
801
+ claimed: {
802
+ summary: formatReceiptSummary(input.receipt),
803
+ status: input.receipt.status,
804
+ settlement: input.receipt.settlement,
805
+ result: input.receipt.result
806
+ },
807
+ validation,
808
+ settlement: input.settlement,
809
+ timelineEvents: input.timelineEvents
810
+ },
811
+ proofTier,
812
+ currentPhase,
813
+ message
814
+ };
815
+ }
816
+
817
+ // src/formatting/portfolio-watch.ts
818
+ var DEFAULT_PORTFOLIO_WATCH_BRIEF = "Watch my portfolio while I'm away \u2014 keep about 20% in stable assets.";
819
+ function isOffPlan3(state) {
820
+ return state === "warning" || state === "violation";
821
+ }
822
+ function inferPortfolioWatchPhase(input) {
823
+ if (input.whileAway) return "return_briefing";
824
+ if (isOffPlan3(input.registerHealth.state)) return "while_away";
825
+ return "watch_registered";
826
+ }
827
+ function buildPortfolioWatchBriefingLines(input) {
828
+ const hostLabel = input.host === "cursor" ? "Cursor" : input.host === "claude" ? "Claude" : "MCP agent";
829
+ const lines = [
830
+ `You asked ${hostLabel} to watch your portfolio while away.`,
831
+ `Policy registered: ${input.objective.name} (${input.objective.automationMode ?? "auto"} mode).`
832
+ ];
833
+ if (input.whileAway) {
834
+ lines.push(
835
+ `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}.`
836
+ );
837
+ if (input.whileAway.autoRestored && input.whileAway.receipt) {
838
+ lines.push(
839
+ `Automatic restore ran \u2014 settlement ${input.whileAway.receipt.settlement}. Receipt id ${input.whileAway.receipt.id}.`
840
+ );
841
+ } else if (input.whileAway.autoRestored) {
842
+ lines.push("Automatic restore ran \u2014 check executions for receipt.");
843
+ } else {
844
+ lines.push("No automatic restore fired \u2014 review health and restore plan.");
845
+ }
846
+ } else if (isOffPlan3(input.briefingHealth.state)) {
847
+ lines.push(
848
+ `Portfolio is off-plan (${input.briefingHealth.state}) \u2014 agent should surface restore options.`
849
+ );
850
+ } else {
851
+ lines.push("Portfolio remains within policy \u2014 no action required.");
852
+ }
853
+ if (input.timelineEvents.length > 0) {
854
+ const types = [...new Set(input.timelineEvents.map((e) => e.type))].slice(
855
+ 0,
856
+ 4
857
+ );
858
+ lines.push(`Timeline: ${input.timelineEvents.length} recent event(s) \u2014 ${types.join(", ")}.`);
859
+ }
860
+ lines.push(`Current health: ${input.briefingHealth.state}.`);
861
+ return lines;
862
+ }
863
+ function buildPortfolioWatchFlow(input) {
864
+ const targetWeight = input.objective.policy?.targetWeight ?? input.registerHealth.targetMetric ?? 0;
865
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
866
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
867
+ const whileAway = input.whileAway ? {
868
+ marketEventName: input.whileAway.marketEvent.name,
869
+ symbol: input.whileAway.marketEvent.symbol,
870
+ priceChangeRatio: input.whileAway.marketEvent.priceChangeRatio,
871
+ healthBefore: input.whileAway.healthBefore,
872
+ healthAfter: input.whileAway.healthAfter,
873
+ autoRestored: input.whileAway.autoRestored,
874
+ receipt: input.whileAway.receipt
875
+ } : void 0;
876
+ const summaryLines = buildPortfolioWatchBriefingLines({
877
+ userBrief: input.userBrief,
878
+ host: input.host,
879
+ objective: input.objective,
880
+ registerHealth: input.registerHealth,
881
+ briefingHealth: input.briefingHealth,
882
+ whileAway,
883
+ timelineEvents: input.timelineEvents
884
+ });
885
+ const currentPhase = inferPortfolioWatchPhase({
886
+ registerHealth: input.registerHealth,
887
+ whileAway
888
+ });
889
+ let message = "Tell your agent to watch the portfolio \u2014 intent becomes an Automatic objective, then read health and timeline when you return.";
890
+ if (currentPhase === "return_briefing" && whileAway?.autoRestored) {
891
+ message = "While you were away, the market moved and Automatic mode restored policy. Review the briefing \u2014 not a blank check, a registered rule.";
892
+ } else if (currentPhase === "while_away" || isOffPlan3(input.briefingHealth.state)) {
893
+ message = "Portfolio drifted off-plan. The agent should report health and restore options \u2014 not discretionary trades.";
894
+ }
895
+ return {
896
+ objectiveId: input.objective.id,
897
+ userBrief: input.userBrief,
898
+ host: input.host,
899
+ phases: {
900
+ register: {
901
+ objectiveName: input.objective.name,
902
+ automationMode: input.objective.automationMode ?? "auto",
903
+ policySummary,
904
+ health: input.registerHealth,
905
+ allocationRow: input.registerRow
906
+ },
907
+ whileAway,
908
+ briefing: {
909
+ health: input.briefingHealth,
910
+ timelineEventCount: input.timelineEvents.length,
911
+ timelineEvents: input.timelineEvents,
912
+ summaryLines
913
+ }
914
+ },
915
+ currentPhase,
916
+ message
917
+ };
918
+ }
919
+ function buildPortfolioWatchFlowFromSnapshot(input) {
920
+ return buildPortfolioWatchFlow({
921
+ userBrief: input.userBrief,
922
+ host: input.host,
923
+ objective: input.objective,
924
+ registerHealth: input.health,
925
+ registerRow: input.allocationRow,
926
+ briefingHealth: input.health,
927
+ timelineEvents: input.timelineEvents
928
+ });
929
+ }
930
+
931
+ // src/formatting/full-aureon-loop.ts
932
+ var DEFAULT_FULL_LOOP_BRIEF = "Keep about 20% in stable assets \u2014 grow the book without abandoning the plan.";
933
+ function inferFullAureonLoopPhase(input) {
934
+ if (input.hasRestore && input.verificationValid) return "verified";
935
+ if (input.hasRestore) return "restored";
936
+ return "plan_check";
937
+ }
938
+ function buildFullAureonLoopFlow(input) {
939
+ const targetWeight = input.objective.policy?.targetWeight ?? input.baselineHealth.targetMetric ?? 0;
940
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
941
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
942
+ const verification = input.verification ?? buildReceiptVerificationFlow({ receipt: input.receipt });
943
+ const currentPhase = inferFullAureonLoopPhase({
944
+ hasRestore: true,
945
+ verificationValid: verification.phases.validation.valid
946
+ });
947
+ let message = "We're not building another portfolio tracker. AUREON registers intent, checks the plan, restores when off-plan, and verifies the receipt.";
948
+ if (!verification.phases.validation.valid) {
949
+ message = "Loop completed restore, but the receipt failed validation \u2014 do not treat success text as proof.";
950
+ } else if (verification.proofTier === "chain_verified") {
951
+ message = "Full loop complete: intent \u2192 plan check \u2192 restore \u2192 chain-verified receipt. Not a tracker \u2014 a Financial Compass.";
952
+ } else {
953
+ message = "Full loop complete: intent \u2192 plan check \u2192 restore \u2192 schema-valid receipt. We're not building another portfolio tracker.";
954
+ }
955
+ return {
956
+ objectiveId: input.objective.id,
957
+ userBrief: input.userBrief,
958
+ phases: {
959
+ intent: {
960
+ objectiveName: input.objective.name,
961
+ policySummary,
962
+ automationMode: input.objective.automationMode ?? "auto",
963
+ health: input.baselineHealth
964
+ },
965
+ planCheck: {
966
+ baselineAligned: input.baselineHealth.state === "healthy",
967
+ afterShock: {
968
+ health: input.afterShockHealth,
969
+ allocationRow: input.afterShockRow,
970
+ paradox: input.paradox
971
+ }
972
+ },
973
+ driftRestore: {
974
+ healthBefore: input.afterShockHealth,
975
+ healthAfter: input.restoredHealth,
976
+ receipt: input.receipt,
977
+ settlement: input.receipt.settlement
978
+ },
979
+ verification
980
+ },
981
+ currentPhase,
982
+ message
983
+ };
984
+ }
985
+ function buildFullAureonLoopFlowFromSnapshot(input) {
986
+ if (!input.latestReceipt) return null;
987
+ const verification = input.verification ?? buildReceiptVerificationFlow({ receipt: input.latestReceipt });
988
+ return buildFullAureonLoopFlow({
989
+ userBrief: input.userBrief,
990
+ objective: input.objective,
991
+ baselineHealth: input.health,
992
+ afterShockHealth: input.health,
993
+ afterShockRow: input.allocationRow,
994
+ paradox: input.paradox,
995
+ restoredHealth: input.health,
996
+ receipt: input.latestReceipt,
997
+ verification
998
+ });
999
+ }
1000
+
1001
+ // src/formatting/audit-trail.ts
1002
+ function buildFinancialAuditTrail(input) {
1003
+ const targetWeight = input.objective.policy?.targetWeight ?? 0;
1004
+ const tolerance = input.objective.policy?.tolerance ?? 0.02;
1005
+ const policySummary = input.objective.policy?.summary ?? buildPolicySummary(input.objective.kind, targetWeight, tolerance);
1006
+ const registered = input.registry?.registered === true ? input.registry.record : void 0;
1007
+ const receipts = input.receipts.map((receipt) => {
1008
+ const validation = validateExecutionReceipt(receipt);
1009
+ return {
1010
+ id: receipt.id,
1011
+ action: receipt.action,
1012
+ settlement: receipt.settlement,
1013
+ status: receipt.status,
1014
+ valid: validation.valid,
1015
+ verifiedOnChain: receipt.verifiedOnChain === true,
1016
+ explorerUrl: receipt.explorerUrl ?? null,
1017
+ summary: formatReceiptSummary(receipt),
1018
+ validation
1019
+ };
1020
+ });
1021
+ const timeline = input.timeline.map((event) => ({
1022
+ id: event.id,
1023
+ type: event.type,
1024
+ message: event.message,
1025
+ createdAt: event.createdAt,
1026
+ executionId: typeof event.payload?.executionId === "string" ? event.payload.executionId : null
1027
+ }));
1028
+ const gaps = [];
1029
+ if (input.registryLookupFailed || input.settlementsLookupFailed) {
1030
+ const parts = [
1031
+ input.registryLookupFailed ? "Registry lookup failed." : null,
1032
+ input.settlementsLookupFailed ? "Settlement lookup failed." : null
1033
+ ].filter((part) => Boolean(part));
1034
+ gaps.push({
1035
+ code: "lookup_failed",
1036
+ message: `${parts.join(" ")} Do not treat this as a confirmed gap.`
1037
+ });
1038
+ }
1039
+ if (!registered && !input.registryLookupFailed) {
1040
+ gaps.push({
1041
+ code: "not_registered",
1042
+ message: "Objective is not registered on ObjectiveRegistry."
1043
+ });
1044
+ }
1045
+ if (receipts.length === 0) {
1046
+ gaps.push({
1047
+ code: "no_executions",
1048
+ message: "No execution receipts for this objective."
1049
+ });
1050
+ } else {
1051
+ if (receipts.every((row) => row.settlement === "staged")) {
1052
+ gaps.push({
1053
+ code: "staged_only",
1054
+ message: "Every receipt is staged. None are on-chain."
1055
+ });
1056
+ }
1057
+ if (receipts.some((row) => row.settlement === "vault" && !row.verifiedOnChain)) {
1058
+ gaps.push({
1059
+ code: "vault_unverified",
1060
+ message: "At least one vault receipt has no independent settlement record yet."
1061
+ });
1062
+ }
1063
+ if (receipts.some((row) => !row.valid)) {
1064
+ gaps.push({
1065
+ code: "invalid_receipt",
1066
+ message: "At least one receipt failed local validation. Do not trust it."
1067
+ });
1068
+ }
1069
+ }
1070
+ const everyReceiptStaged = receipts.length > 0 && receipts.every((row) => row.settlement === "staged");
1071
+ if (input.settlements.length === 0 && !input.settlementsLookupFailed && !everyReceiptStaged) {
1072
+ gaps.push({
1073
+ code: "no_settlements",
1074
+ message: "No chain settlement records linked to this objective."
1075
+ });
1076
+ }
1077
+ if (timeline.length === 0) {
1078
+ gaps.push({
1079
+ code: "no_timeline",
1080
+ message: "No timeline events for this objective."
1081
+ });
1082
+ }
1083
+ return {
1084
+ objectiveId: input.objective.id,
1085
+ objectiveName: input.objective.name,
1086
+ policySummary,
1087
+ healthState: input.health?.state ?? null,
1088
+ generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1089
+ registry: registered ? { present: true, record: registered } : { present: false },
1090
+ receipts,
1091
+ settlements: input.settlements,
1092
+ timeline,
1093
+ gaps,
1094
+ message: auditTrailMessage({
1095
+ registered: Boolean(registered),
1096
+ receiptCount: receipts.length,
1097
+ settlementCount: input.settlements.length,
1098
+ invalid: receipts.some((row) => !row.valid),
1099
+ chainVerified: receipts.some((row) => row.verifiedOnChain)
1100
+ })
1101
+ };
1102
+ }
1103
+ function formatAuditTrailLines(trail) {
1104
+ const lines = [
1105
+ `Objective ${trail.objectiveName} (${trail.objectiveId})`,
1106
+ `Policy ${trail.policySummary}`,
1107
+ `Health ${trail.healthState ?? "unknown"}`,
1108
+ `Registry ${trail.registry.present ? "registered on-chain" : "not registered"}`,
1109
+ `Receipts ${trail.receipts.length}`,
1110
+ `Settlements ${trail.settlements.length}`,
1111
+ `Timeline ${trail.timeline.length}`
1112
+ ];
1113
+ if (trail.gaps.length > 0) {
1114
+ lines.push("Gaps");
1115
+ for (const gap of trail.gaps) {
1116
+ lines.push(` - ${gap.message}`);
1117
+ }
1118
+ }
1119
+ lines.push(trail.message);
1120
+ return lines;
1121
+ }
1122
+ function auditTrailMessage(input) {
1123
+ if (input.invalid) {
1124
+ return "Audit trail assembled with dishonest or incomplete receipts. Do not treat success text as proof.";
1125
+ }
1126
+ if (input.receiptCount === 0) {
1127
+ return "Audit trail shows the objective only. No restore has been recorded yet.";
1128
+ }
1129
+ if (input.chainVerified && input.registered) {
1130
+ return "Audit trail complete on testnet: registered objective, receipts, and chain settlement. Not mainnet.";
1131
+ }
1132
+ if (input.chainVerified) {
1133
+ return "Receipts include chain settlement proof. Objective is not registered on-chain.";
1134
+ }
1135
+ if (input.settlementCount === 0) {
1136
+ return "Audit trail shows receipts without chain settlement records. Staged or unverified vault \u2014 not independent proof.";
1137
+ }
1138
+ return "Audit trail assembled from what exists. Gaps are labeled. Nothing missing was invented.";
1139
+ }
1140
+
1141
+ // src/formatting/intent.ts
1142
+ var DEFAULT_TOLERANCE = 0.02;
1143
+ function defaultName(intent) {
1144
+ if (intent.name?.trim()) return intent.name.trim();
1145
+ const pct = (intent.targetWeight * 100).toFixed(0);
1146
+ switch (intent.kind) {
1147
+ case "stable_allocation":
1148
+ return `Maintain ${pct}% Stable Assets`;
1149
+ case "balanced_portfolio":
1150
+ return `Maintain ${pct}% ${intent.targetSymbol ?? "Sleeve"}`;
1151
+ case "risk_ceiling":
1152
+ return `Risk ceiling policy`;
1153
+ case "reward_reinvestment":
1154
+ return `Reinvest rewards toward ${pct}%`;
1155
+ default:
1156
+ return intent.brief.slice(0, 64);
1157
+ }
1158
+ }
1159
+ function resolveObjectiveFromIntent(intent) {
1160
+ const brief = intent.brief?.trim();
1161
+ if (!brief || brief.length < 3) {
1162
+ throw new AureonValidationError("Intent brief must be at least 3 characters");
1163
+ }
1164
+ if (intent.targetWeight < 0 || intent.targetWeight > 1) {
1165
+ throw new AureonValidationError("targetWeight must be between 0 and 1");
1166
+ }
1167
+ const tolerance = intent.tolerance ?? DEFAULT_TOLERANCE;
1168
+ if (tolerance < 0 || tolerance > 0.5) {
1169
+ throw new AureonValidationError("tolerance must be between 0 and 0.5");
1170
+ }
1171
+ const base = {
1172
+ name: defaultName(intent),
1173
+ kind: intent.kind,
1174
+ targetWeight: intent.targetWeight,
1175
+ tolerance,
1176
+ priority: intent.priority ?? "high",
1177
+ automationMode: "auto"
1178
+ };
1179
+ if (intent.kind === "balanced_portfolio") {
1180
+ const symbol = intent.targetSymbol?.trim().toUpperCase();
1181
+ if (!symbol) {
1182
+ throw new AureonValidationError(
1183
+ "balanced_portfolio intent requires targetSymbol"
1184
+ );
1185
+ }
1186
+ return { ...base, targetSymbol: symbol };
1187
+ }
1188
+ return base;
1189
+ }
1190
+ function buildObjectivePortfolioFlow(intent, objective, health, portfolio) {
1191
+ const policySummary = objective.policy?.summary ?? buildPolicySummary(intent.kind, intent.targetWeight, intent.tolerance);
1192
+ const state = health?.state ?? "paused";
1193
+ const current = health?.currentMetric;
1194
+ const target = health?.targetMetric ?? intent.targetWeight;
1195
+ let message = "Intent registered as objective. Portfolio is now scored against that policy.";
1196
+ if (health && state === "healthy") {
1197
+ message = `Portfolio aligns with intent \u2014 ${policySummary}.`;
1198
+ } else if (health && (state === "warning" || state === "violation")) {
1199
+ message = `Objective is active but portfolio is off-plan (${state}). Current ${((current ?? 0) * 100).toFixed(1)}% vs target ${(target * 100).toFixed(1)}%.`;
1200
+ }
1201
+ return {
1202
+ intent: { brief: intent.brief.trim(), policySummary },
1203
+ objective,
1204
+ health,
1205
+ portfolio: {
1206
+ totalNotionalUsd: portfolio.totalNotionalUsd,
1207
+ stableWeight: portfolio.stableWeight,
1208
+ positions: portfolio.positions
1209
+ },
1210
+ message
1211
+ };
1212
+ }
1213
+ function parseFinancialIntent(brief) {
1214
+ const text = brief.trim();
1215
+ if (!text) {
1216
+ throw new AureonValidationError("Intent brief is required");
1217
+ }
1218
+ const stableMatch = text.match(
1219
+ /(\d+(?:\.\d+)?)\s*%?\s*(?:of\s+(?:my\s+)?portfolio\s+in\s+)?stable/i
1220
+ );
1221
+ if (stableMatch) {
1222
+ const pct = Number(stableMatch[1]) / 100;
1223
+ return {
1224
+ brief: text,
1225
+ kind: "stable_allocation",
1226
+ targetWeight: pct,
1227
+ tolerance: DEFAULT_TOLERANCE
1228
+ };
1229
+ }
1230
+ const holdMatch = text.match(
1231
+ /(?:hold|keep|maintain)\s+(?:about\s+)?(\d+(?:\.\d+)?)\s*%?\s*(?:in\s+)?([A-Z]{2,10})/i
1232
+ );
1233
+ if (holdMatch) {
1234
+ const pct = Number(holdMatch[1]) / 100;
1235
+ const symbol = holdMatch[2].toUpperCase();
1236
+ if (symbol === "STABLE" || symbol === "STABLES") {
1237
+ return {
1238
+ brief: text,
1239
+ kind: "stable_allocation",
1240
+ targetWeight: pct,
1241
+ tolerance: DEFAULT_TOLERANCE
1242
+ };
1243
+ }
1244
+ return {
1245
+ brief: text,
1246
+ kind: "balanced_portfolio",
1247
+ targetWeight: pct,
1248
+ tolerance: DEFAULT_TOLERANCE,
1249
+ targetSymbol: symbol
1250
+ };
1251
+ }
1252
+ const pctOnly = text.match(/(\d+(?:\.\d+)?)\s*%/);
1253
+ if (pctOnly && /stable/i.test(text)) {
1254
+ return {
1255
+ brief: text,
1256
+ kind: "stable_allocation",
1257
+ targetWeight: Number(pctOnly[1]) / 100,
1258
+ tolerance: DEFAULT_TOLERANCE
1259
+ };
1260
+ }
1261
+ throw new AureonValidationError(
1262
+ "Could not parse intent from brief \u2014 supply structured FinancialIntent fields"
1263
+ );
1264
+ }
1265
+
1266
+ // src/errors/http.ts
1267
+ function isRecord2(value) {
1268
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1269
+ }
175
1270
  function extractMessage(body) {
176
- if (!isRecord(body)) return null;
1271
+ if (!isRecord2(body)) return null;
177
1272
  if (typeof body.message === "string") return body.message;
178
1273
  if (typeof body.error === "string") return body.error;
179
- if (isRecord(body.error) && typeof body.error.message === "string") {
1274
+ if (isRecord2(body.error) && typeof body.error.message === "string") {
180
1275
  return body.error.message;
181
1276
  }
182
1277
  return null;
183
1278
  }
184
1279
  function errorFromHttpStatus(status, body) {
185
1280
  const message = extractMessage(body) ?? defaultMessageForStatus(status);
186
- const details = isRecord(body) ? body : { body };
1281
+ const details = isRecord2(body) ? body : { body };
187
1282
  if (status === 400) return new AureonValidationError(message, details);
188
1283
  if (status === 404) return new AureonNotFoundError(message, details);
189
1284
  if (status === 409) return new AureonConflictError(message, details);
@@ -327,211 +1422,121 @@ async function requestJson(transport, path, options = {}) {
327
1422
  if (attempt <= maxRetries && isRetryableError(error)) {
328
1423
  transport.logger?.warn("aureon.retry", {
329
1424
  path,
330
- attempt,
331
- code: error.code,
332
- status: error.status
333
- });
334
- await sleep(retryDelayMs);
335
- continue;
336
- }
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"
1425
+ attempt,
1426
+ code: error.code,
1427
+ status: error.status
1428
+ });
1429
+ await sleep(retryDelayMs);
1430
+ continue;
1431
+ }
1432
+ throw error;
1433
+ }
1434
+ const networkError = new AureonNetworkError(
1435
+ error instanceof Error ? error.message : "Network request failed",
1436
+ { url }
474
1437
  );
1438
+ if (attempt <= maxRetries) {
1439
+ transport.logger?.warn("aureon.retry", {
1440
+ path,
1441
+ attempt,
1442
+ reason: "network"
1443
+ });
1444
+ await sleep(retryDelayMs);
1445
+ continue;
1446
+ }
1447
+ throw networkError;
1448
+ } finally {
1449
+ clearTimeout(timeout);
1450
+ if (options.signal) options.signal.removeEventListener("abort", onAbort);
475
1451
  }
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
1452
  }
486
- return {
487
- ...input,
488
- name,
489
- priority,
490
- targetSymbol: null,
491
- automationMode: input.automationMode === "manual" ? "manual" : "auto"
492
- };
493
1453
  }
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
- }
1454
+
1455
+ // src/types/client-options.ts
1456
+ var DEFAULT_TIMEOUT_MS2 = 3e4;
1457
+ function resolveTimeoutMs(options) {
1458
+ const value = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
1459
+ if (!Number.isFinite(value) || value <= 0) {
1460
+ throw new Error("timeoutMs must be a positive finite number");
507
1461
  }
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
- }
1462
+ return value;
1463
+ }
1464
+ function resolveHeaders(options) {
1465
+ const headers = { ...options.headers ?? {} };
1466
+ return headers;
1467
+ }
1468
+ function resolveMaxRetries(options) {
1469
+ const value = options.maxRetries ?? 0;
1470
+ if (!Number.isInteger(value) || value < 0) {
1471
+ throw new Error("maxRetries must be a non-negative integer");
512
1472
  }
513
- if (next.priority !== void 0 && !isObjectivePriority(next.priority)) {
514
- throw new AureonValidationError(`Unsupported priority: ${next.priority}`);
1473
+ return value;
1474
+ }
1475
+ function resolveRetryDelayMs(options) {
1476
+ const value = options.retryDelayMs ?? 250;
1477
+ if (!Number.isFinite(value) || value < 0) {
1478
+ throw new Error("retryDelayMs must be a non-negative finite number");
515
1479
  }
516
- if (next.automationMode !== void 0) {
517
- throw new AureonValidationError(
518
- "automationMode cannot be changed after create: recreate the objective instead"
519
- );
1480
+ return value;
1481
+ }
1482
+
1483
+ // src/types/market.ts
1484
+ function normalizeSymbol(symbol) {
1485
+ return symbol.trim().toUpperCase();
1486
+ }
1487
+
1488
+ // src/validation/market-input.ts
1489
+ function normalizeApplyMarketEventInput(input) {
1490
+ const symbol = normalizeSymbol(input.symbol ?? "");
1491
+ if (!symbol) throw new AureonValidationError("symbol is required");
1492
+ if (!Number.isFinite(input.priceChangeRatio)) {
1493
+ throw new AureonValidationError("priceChangeRatio must be a finite number");
520
1494
  }
521
- if (input.targetSymbol !== void 0) {
1495
+ if (input.priceChangeRatio <= -0.95) {
522
1496
  throw new AureonValidationError(
523
- "targetSymbol cannot be changed after create: recreate the objective instead"
1497
+ "priceChangeRatio is too extreme for preview runtime"
524
1498
  );
525
1499
  }
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
- }
1500
+ return {
1501
+ ...input,
1502
+ symbol,
1503
+ autoRestore: input.autoRestore === true,
1504
+ name: input.name?.trim() || void 0,
1505
+ description: input.description?.trim() || void 0
1506
+ };
532
1507
  }
533
1508
 
534
1509
  // src/client/aureon-client.ts
1510
+ var DEMO_DRIFT_RESTORE_POSITIONS = [
1511
+ {
1512
+ symbol: "USDG",
1513
+ name: "Paxos USDG",
1514
+ category: "stable",
1515
+ quantity: 24e3,
1516
+ markPriceUsd: 1
1517
+ },
1518
+ {
1519
+ symbol: "NVDA",
1520
+ name: "NVIDIA Stock Token",
1521
+ category: "stock_token",
1522
+ quantity: 45,
1523
+ markPriceUsd: 920
1524
+ },
1525
+ {
1526
+ symbol: "AAPL",
1527
+ name: "Apple Stock Token",
1528
+ category: "stock_token",
1529
+ quantity: 80,
1530
+ markPriceUsd: 210
1531
+ },
1532
+ {
1533
+ symbol: "ETH",
1534
+ name: "Ether",
1535
+ category: "gas",
1536
+ quantity: 8.5,
1537
+ markPriceUsd: 3400
1538
+ }
1539
+ ];
535
1540
  var AureonClient = class {
536
1541
  transport;
537
1542
  constructor(options = {}) {
@@ -744,9 +1749,447 @@ var AureonClient = class {
744
1749
  async getOverview() {
745
1750
  return requestJson(this.transport, ENDPOINTS.overview);
746
1751
  }
1752
+ /**
1753
+ * Objective vs actual portfolio — joins objectives, health, and overview
1754
+ * into comparison rows plus a green-book/off-plan paradox flag.
1755
+ * Auth required.
1756
+ */
1757
+ async getAllocationVsTarget() {
1758
+ const [overview, objectives, health] = await Promise.all([
1759
+ this.getOverview(),
1760
+ this.listObjectives(),
1761
+ this.getHealth()
1762
+ ]);
1763
+ const rows = buildAllocationComparison(objectives, health);
1764
+ const paradox = detectPlanParadox(overview, health);
1765
+ return { rows, paradox, overview };
1766
+ }
1767
+ /**
1768
+ * Registers agent/user intent as an Automatic objective and returns the
1769
+ * AI → objective → portfolio flow snapshot.
1770
+ * Auth required.
1771
+ */
1772
+ async applyFinancialIntent(intent) {
1773
+ const objective = await this.createObjective(
1774
+ resolveObjectiveFromIntent(intent)
1775
+ );
1776
+ try {
1777
+ await this.refreshWatchdog();
1778
+ } catch {
1779
+ }
1780
+ const [healthRecords, portfolio] = await Promise.all([
1781
+ this.getHealth(objective.id),
1782
+ this.getPortfolio()
1783
+ ]);
1784
+ return buildObjectivePortfolioFlow(
1785
+ intent,
1786
+ objective,
1787
+ healthRecords[0] ?? null,
1788
+ portfolio
1789
+ );
1790
+ }
1791
+ /**
1792
+ * Read-only AI → objective → portfolio flow for existing objectives.
1793
+ * Auth required.
1794
+ */
1795
+ async getObjectivePortfolioFlow(objectiveId) {
1796
+ const [objectives, healthRecords, portfolio] = await Promise.all([
1797
+ objectiveId ? [await this.getObjective(objectiveId)] : this.listObjectives(),
1798
+ this.getHealth(objectiveId),
1799
+ this.getPortfolio()
1800
+ ]);
1801
+ const active = objectives.filter(
1802
+ (o) => o.status === "active" || o.status === "validated"
1803
+ );
1804
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
1805
+ return active.map((objective) => {
1806
+ const health = healthById.get(objective.id) ?? null;
1807
+ const intent = {
1808
+ brief: objective.name,
1809
+ kind: objective.kind,
1810
+ targetWeight: objective.policy?.targetWeight ?? 0,
1811
+ tolerance: objective.policy?.tolerance ?? 0.02,
1812
+ targetSymbol: objective.policy?.targetSymbol,
1813
+ name: objective.name,
1814
+ priority: objective.priority
1815
+ };
1816
+ return buildObjectivePortfolioFlow(
1817
+ intent,
1818
+ objective,
1819
+ health,
1820
+ portfolio
1821
+ );
1822
+ });
1823
+ }
1824
+ /**
1825
+ * Controlled drift → detection → restore demo
1826
+ * Seeds book, creates stable objective, applies NVDA rally with auto-restore
1827
+ * disabled, then runs manual restore and returns the three-beat flow.
1828
+ * Auth required.
1829
+ */
1830
+ async runDriftRestoreDemo() {
1831
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
1832
+ const objective = await this.createObjective({
1833
+ name: "Maintain 20% Stable Assets",
1834
+ kind: "stable_allocation",
1835
+ targetWeight: 0.2,
1836
+ tolerance: 0.02
1837
+ });
1838
+ try {
1839
+ await this.refreshWatchdog();
1840
+ } catch {
1841
+ }
1842
+ const [alignedHealthRecords, baselineRows] = await Promise.all([
1843
+ this.getHealth(objective.id),
1844
+ this.getAllocationVsTarget()
1845
+ ]);
1846
+ const alignedHealth = alignedHealthRecords[0];
1847
+ if (!alignedHealth) {
1848
+ throw new AureonValidationError(
1849
+ "Baseline health missing after objective create"
1850
+ );
1851
+ }
1852
+ const alignedRow = baselineRows.rows.find(
1853
+ (r) => r.objectiveId === objective.id
1854
+ );
1855
+ await this.applyMarketEvent({
1856
+ name: "NVDA Stock Token Rally",
1857
+ description: "Controlled mark move \u2014 drift demo",
1858
+ symbol: "NVDA",
1859
+ priceChangeRatio: 0.45,
1860
+ autoRestore: false
1861
+ });
1862
+ const [driftHealthRecords, driftRows, restorePlan] = await Promise.all([
1863
+ this.getHealth(objective.id),
1864
+ this.getAllocationVsTarget(),
1865
+ this.getRestorePlan(objective.id)
1866
+ ]);
1867
+ const driftHealth = driftHealthRecords[0];
1868
+ if (!driftHealth) {
1869
+ throw new AureonValidationError("Drift health missing after market event");
1870
+ }
1871
+ const driftRow = driftRows.rows.find((r) => r.objectiveId === objective.id);
1872
+ const receipt = await this.restoreObjective(objective.id);
1873
+ const [restoredHealthRecords] = await Promise.all([
1874
+ this.getHealth(objective.id)
1875
+ ]);
1876
+ const restoredHealth = restoredHealthRecords[0];
1877
+ return buildDriftRestoreFlow({
1878
+ objective,
1879
+ alignedHealth,
1880
+ driftHealth,
1881
+ driftPlan: restorePlan,
1882
+ restoredHealth: restoredHealth ?? driftHealth,
1883
+ receipt,
1884
+ alignedRow,
1885
+ driftRow
1886
+ });
1887
+ }
1888
+ /**
1889
+ * Read-only drift → detection → restore flow for active objectives.
1890
+ * Auth required.
1891
+ */
1892
+ async getDriftRestoreFlow(objectiveId) {
1893
+ const [objectives, healthRecords, allocation, executions] = await Promise.all([
1894
+ objectiveId ? [await this.getObjective(objectiveId)] : this.listObjectives(),
1895
+ this.getHealth(objectiveId),
1896
+ this.getAllocationVsTarget(),
1897
+ this.listExecutions(objectiveId)
1898
+ ]);
1899
+ const active = objectives.filter(
1900
+ (o) => o.status === "active" || o.status === "validated"
1901
+ );
1902
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
1903
+ const rowById = new Map(
1904
+ allocation.rows.map((r) => [r.objectiveId, r])
1905
+ );
1906
+ const receiptsByObjective = /* @__PURE__ */ new Map();
1907
+ for (const receipt of sortExecutionsNewestFirst(executions)) {
1908
+ if (!receiptsByObjective.has(receipt.objectiveId)) {
1909
+ receiptsByObjective.set(receipt.objectiveId, receipt);
1910
+ }
1911
+ }
1912
+ const flows = [];
1913
+ for (const objective of active) {
1914
+ const health = healthById.get(objective.id);
1915
+ if (!health) continue;
1916
+ let restorePlan;
1917
+ if (health.state === "warning" || health.state === "violation") {
1918
+ try {
1919
+ restorePlan = await this.getRestorePlan(objective.id);
1920
+ } catch {
1921
+ }
1922
+ }
1923
+ flows.push(
1924
+ buildDriftRestoreFlowFromSnapshot({
1925
+ objective,
1926
+ health,
1927
+ allocationRow: rowById.get(objective.id),
1928
+ restorePlan,
1929
+ latestReceipt: receiptsByObjective.get(objective.id)
1930
+ })
1931
+ );
1932
+ }
1933
+ return flows;
1934
+ }
1935
+ async buildReceiptVerificationFlowForReceipt(receipt, timelineEvents) {
1936
+ const validation = validateExecutionReceipt(receipt);
1937
+ let settlement;
1938
+ if (receipt.settlement === "vault") {
1939
+ try {
1940
+ settlement = await this.getExecutionSettlement(receipt.id);
1941
+ } catch {
1942
+ }
1943
+ }
1944
+ const events = timelineEvents ?? findTimelineEventsForReceipt(
1945
+ await this.getTimeline(receipt.objectiveId),
1946
+ receipt
1947
+ );
1948
+ return buildReceiptVerificationFlow({
1949
+ receipt,
1950
+ validation,
1951
+ settlement,
1952
+ timelineEvents: events
1953
+ });
1954
+ }
1955
+ /**
1956
+ * Controlled receipt → verification demo.
1957
+ * Runs drift-restore, then validates receipt and looks up settlement.
1958
+ * Auth required.
1959
+ */
1960
+ async runReceiptVerificationDemo() {
1961
+ const driftFlow = await this.runDriftRestoreDemo();
1962
+ const receipt = driftFlow.phases.restored?.receipt;
1963
+ if (!receipt) {
1964
+ throw new AureonValidationError(
1965
+ "Restore receipt missing after drift-restore demo"
1966
+ );
1967
+ }
1968
+ return this.buildReceiptVerificationFlowForReceipt(receipt);
1969
+ }
1970
+ /**
1971
+ * Read-only receipt → verification flow for execution receipts.
1972
+ * Auth required.
1973
+ */
1974
+ async getReceiptVerificationFlow(executionId) {
1975
+ const executions = sortExecutionsNewestFirst(await this.listExecutions());
1976
+ const targets = executionId ? executions.filter((e) => e.id === executionId) : executions.slice(0, 5);
1977
+ if (targets.length === 0) {
1978
+ return [];
1979
+ }
1980
+ const timeline = await this.getTimeline();
1981
+ const flows = [];
1982
+ for (const receipt of targets) {
1983
+ flows.push(
1984
+ await this.buildReceiptVerificationFlowForReceipt(
1985
+ receipt,
1986
+ findTimelineEventsForReceipt(timeline, receipt)
1987
+ )
1988
+ );
1989
+ }
1990
+ return flows;
1991
+ }
1992
+ /**
1993
+ * Controlled portfolio watch demo.
1994
+ * User brief → Automatic objective → market move while away → auto restore → return briefing.
1995
+ * Auth required.
1996
+ */
1997
+ async runPortfolioWatchDemo(input) {
1998
+ const userBrief = input?.brief?.trim() || DEFAULT_PORTFOLIO_WATCH_BRIEF;
1999
+ const host = input?.host ?? "cursor";
2000
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
2001
+ const intent = {
2002
+ brief: userBrief,
2003
+ kind: "stable_allocation",
2004
+ targetWeight: 0.2,
2005
+ tolerance: 0.02
2006
+ };
2007
+ const setup = await this.applyFinancialIntent(intent);
2008
+ const objective = setup.objective;
2009
+ const registerHealth = setup.health;
2010
+ if (!registerHealth) {
2011
+ throw new AureonValidationError(
2012
+ "Register health missing after applyFinancialIntent"
2013
+ );
2014
+ }
2015
+ const baselineRows = await this.getAllocationVsTarget();
2016
+ const registerRow = baselineRows.rows.find(
2017
+ (r) => r.objectiveId === objective.id
2018
+ );
2019
+ const healthBeforeRecords = await this.getHealth(objective.id);
2020
+ const healthBefore = healthBeforeRecords[0];
2021
+ if (!healthBefore) {
2022
+ throw new AureonValidationError("Baseline health missing before market event");
2023
+ }
2024
+ const marketResult = await this.applyMarketEvent({
2025
+ name: "NVDA rally while you were away",
2026
+ description: "portfolio watch demo \u2014 auto restore on",
2027
+ symbol: "NVDA",
2028
+ priceChangeRatio: 0.45,
2029
+ autoRestore: true
2030
+ });
2031
+ const healthAfter = marketResult.health.find((h) => h.objectiveId === objective.id) ?? healthBefore;
2032
+ const receipt = marketResult.executions.find(
2033
+ (e) => e.objectiveId === objective.id
2034
+ );
2035
+ const timeline = await this.getTimeline(objective.id);
2036
+ return buildPortfolioWatchFlow({
2037
+ userBrief,
2038
+ host,
2039
+ objective,
2040
+ registerHealth,
2041
+ registerRow,
2042
+ whileAway: {
2043
+ marketEvent: marketResult.event,
2044
+ healthBefore,
2045
+ healthAfter,
2046
+ autoRestored: marketResult.executions.length > 0,
2047
+ receipt
2048
+ },
2049
+ briefingHealth: healthAfter,
2050
+ timelineEvents: timeline.slice(0, 10)
2051
+ });
2052
+ }
2053
+ /**
2054
+ * Read-only portfolio watch briefing for Automatic objectives.
2055
+ * Auth required.
2056
+ */
2057
+ async getPortfolioWatchFlow(input) {
2058
+ const userBrief = input?.brief?.trim() || DEFAULT_PORTFOLIO_WATCH_BRIEF;
2059
+ const host = input?.host ?? "mcp";
2060
+ const [objectives, healthRecords, allocation, timeline] = await Promise.all([
2061
+ input?.objectiveId ? [await this.getObjective(input.objectiveId)] : this.listObjectives(),
2062
+ this.getHealth(input?.objectiveId),
2063
+ this.getAllocationVsTarget(),
2064
+ this.getTimeline(input?.objectiveId)
2065
+ ]);
2066
+ const active = objectives.filter(
2067
+ (o) => (o.status === "active" || o.status === "validated") && (o.automationMode ?? "auto") === "auto"
2068
+ );
2069
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
2070
+ const rowById = new Map(allocation.rows.map((r) => [r.objectiveId, r]));
2071
+ const flows = [];
2072
+ for (const objective of active) {
2073
+ const health = healthById.get(objective.id);
2074
+ if (!health) continue;
2075
+ const objectiveTimeline = timeline.filter(
2076
+ (e) => !e.objectiveId || e.objectiveId === objective.id
2077
+ );
2078
+ flows.push(
2079
+ buildPortfolioWatchFlowFromSnapshot({
2080
+ userBrief,
2081
+ host,
2082
+ objective,
2083
+ health,
2084
+ allocationRow: rowById.get(objective.id),
2085
+ timelineEvents: objectiveTimeline.slice(0, 10)
2086
+ })
2087
+ );
2088
+ }
2089
+ return flows;
2090
+ }
2091
+ /**
2092
+ * Controlled full AUREON loop demo (Content Arc).
2093
+ * Intent → plan check (green vs plan with autoRestore false) → restore → receipt verification.
2094
+ * Auth required.
2095
+ */
2096
+ async runFullAureonLoopDemo(input) {
2097
+ const userBrief = input?.brief?.trim() || DEFAULT_FULL_LOOP_BRIEF;
2098
+ await this.setPortfolio(DEMO_DRIFT_RESTORE_POSITIONS);
2099
+ const intent = {
2100
+ brief: userBrief,
2101
+ kind: "stable_allocation",
2102
+ targetWeight: 0.2,
2103
+ tolerance: 0.02
2104
+ };
2105
+ const setup = await this.applyFinancialIntent(intent);
2106
+ const objective = setup.objective;
2107
+ const baselineHealth = setup.health;
2108
+ if (!baselineHealth) {
2109
+ throw new AureonValidationError(
2110
+ "Baseline health missing after applyFinancialIntent"
2111
+ );
2112
+ }
2113
+ await this.applyMarketEvent({
2114
+ name: "NVDA rally \u2014 green book, off-plan sleeve",
2115
+ description: "Full loop \u2014 autoRestore false to expose plan paradox",
2116
+ symbol: "NVDA",
2117
+ priceChangeRatio: 0.45,
2118
+ autoRestore: false
2119
+ });
2120
+ const [afterShockHealthRecords, allocation] = await Promise.all([
2121
+ this.getHealth(objective.id),
2122
+ this.getAllocationVsTarget()
2123
+ ]);
2124
+ const afterShockHealth = afterShockHealthRecords[0];
2125
+ if (!afterShockHealth) {
2126
+ throw new AureonValidationError("Health missing after market event");
2127
+ }
2128
+ const afterShockRow = allocation.rows.find(
2129
+ (r) => r.objectiveId === objective.id
2130
+ );
2131
+ const receipt = await this.restoreObjective(objective.id);
2132
+ const restoredHealthRecords = await this.getHealth(objective.id);
2133
+ const restoredHealth = restoredHealthRecords[0] ?? afterShockHealth;
2134
+ const verification = await this.buildReceiptVerificationFlowForReceipt(receipt);
2135
+ return buildFullAureonLoopFlow({
2136
+ userBrief,
2137
+ objective,
2138
+ baselineHealth,
2139
+ afterShockHealth,
2140
+ afterShockRow,
2141
+ paradox: allocation.paradox,
2142
+ restoredHealth,
2143
+ receipt,
2144
+ verification
2145
+ });
2146
+ }
2147
+ /**
2148
+ * Read-only full AUREON loop for active objectives with a latest receipt.
2149
+ * Auth required.
2150
+ */
2151
+ async getFullAureonLoopFlow(input) {
2152
+ const userBrief = input?.brief?.trim() || DEFAULT_FULL_LOOP_BRIEF;
2153
+ const [objectives, healthRecords, allocation, executions] = await Promise.all([
2154
+ input?.objectiveId ? [await this.getObjective(input.objectiveId)] : this.listObjectives(),
2155
+ this.getHealth(input?.objectiveId),
2156
+ this.getAllocationVsTarget(),
2157
+ this.listExecutions(input?.objectiveId)
2158
+ ]);
2159
+ const active = objectives.filter(
2160
+ (o) => o.status === "active" || o.status === "validated"
2161
+ );
2162
+ const healthById = new Map(healthRecords.map((h) => [h.objectiveId, h]));
2163
+ const rowById = new Map(allocation.rows.map((r) => [r.objectiveId, r]));
2164
+ const receiptsByObjective = /* @__PURE__ */ new Map();
2165
+ for (const receipt of sortExecutionsNewestFirst(executions)) {
2166
+ if (!receiptsByObjective.has(receipt.objectiveId)) {
2167
+ receiptsByObjective.set(receipt.objectiveId, receipt);
2168
+ }
2169
+ }
2170
+ const flows = [];
2171
+ for (const objective of active) {
2172
+ const health = healthById.get(objective.id);
2173
+ if (!health) continue;
2174
+ const latestReceipt = receiptsByObjective.get(objective.id);
2175
+ if (!latestReceipt) continue;
2176
+ const verification = await this.buildReceiptVerificationFlowForReceipt(latestReceipt);
2177
+ const flow = buildFullAureonLoopFlowFromSnapshot({
2178
+ userBrief,
2179
+ objective,
2180
+ health,
2181
+ allocationRow: rowById.get(objective.id),
2182
+ paradox: allocation.paradox,
2183
+ latestReceipt,
2184
+ verification
2185
+ });
2186
+ if (flow) flows.push(flow);
2187
+ }
2188
+ return flows;
2189
+ }
747
2190
  /**
748
2191
  * Applies a controlled market event to portfolio marks.
749
- * When autoRestore is true, the API evaluates health and may run staged restorative execution.
2192
+ * 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
2193
  * Auth required.
751
2194
  */
752
2195
  async applyMarketEvent(input) {
@@ -790,7 +2233,8 @@ var AureonClient = class {
790
2233
  });
791
2234
  }
792
2235
  /**
793
- * Runs vault-backed restorative execution for an objective outside policy.
2236
+ * Runs restorative execution for an objective outside policy.
2237
+ * Receipt.settlement may be vault or staged. Only verifiedOnChain is proof.
794
2238
  * Auth required.
795
2239
  */
796
2240
  async restoreObjective(objectiveId) {
@@ -808,6 +2252,35 @@ var AureonClient = class {
808
2252
  );
809
2253
  return result.executions;
810
2254
  }
2255
+ /** Returns chain-verified settlement record for an execution when present. Auth required. */
2256
+ async getExecutionSettlement(executionId) {
2257
+ assertId(executionId, "execution id");
2258
+ return requestJson(this.transport, executionSettlementPath(executionId));
2259
+ }
2260
+ /** Lists chain-verified settlement records for the authenticated wallet. Auth required. */
2261
+ async listSettlements(objectiveId) {
2262
+ const path = withQuery(ENDPOINTS.settlements, { objectiveId });
2263
+ const result = await requestJson(
2264
+ this.transport,
2265
+ path
2266
+ );
2267
+ return result.settlements;
2268
+ }
2269
+ /**
2270
+ * Manual backfill: verify a vault tx on-chain and attach settlement proof.
2271
+ * Auth required.
2272
+ */
2273
+ async confirmExecutionSettlement(executionId, transactionHash) {
2274
+ assertId(executionId, "execution id");
2275
+ const hash = transactionHash.trim();
2276
+ if (!hash) {
2277
+ throw new AureonValidationError("transactionHash is required");
2278
+ }
2279
+ return requestJson(this.transport, executionConfirmSettlementPath(executionId), {
2280
+ method: "POST",
2281
+ body: { transactionHash: hash }
2282
+ });
2283
+ }
811
2284
  /** Returns Phase 2 ObjectiveRegistry deployment status. Auth required. */
812
2285
  async getRegistryStatus() {
813
2286
  return requestJson(this.transport, ENDPOINTS.registryStatus);
@@ -928,6 +2401,43 @@ var AureonClient = class {
928
2401
  method: "POST"
929
2402
  });
930
2403
  }
2404
+ /**
2405
+ * Joins objective → registry → receipts → settlements → timeline.
2406
+ * Missing proof is labeled as a gap. Nothing is invented. Auth required.
2407
+ */
2408
+ async getAuditTrail(objectiveId) {
2409
+ assertId(objectiveId, "objective id");
2410
+ const [objective, healthRows, receipts, settlementsResult, timeline] = await Promise.all([
2411
+ this.getObjective(objectiveId),
2412
+ this.getHealth(objectiveId),
2413
+ this.listExecutions(objectiveId),
2414
+ this.listSettlements(objectiveId).then(
2415
+ (rows) => ({ ok: true, rows }),
2416
+ () => ({ ok: false, rows: [] })
2417
+ ),
2418
+ this.getTimeline(objectiveId)
2419
+ ]);
2420
+ let registry = {
2421
+ registered: false,
2422
+ objectiveId
2423
+ };
2424
+ let registryLookupFailed = false;
2425
+ try {
2426
+ registry = await this.getObjectiveRegistry(objectiveId);
2427
+ } catch {
2428
+ registryLookupFailed = true;
2429
+ }
2430
+ return buildFinancialAuditTrail({
2431
+ objective,
2432
+ health: healthRows[0],
2433
+ registry,
2434
+ receipts: sortExecutionsNewestFirst(receipts),
2435
+ settlements: settlementsResult.rows,
2436
+ timeline,
2437
+ registryLookupFailed,
2438
+ settlementsLookupFailed: !settlementsResult.ok
2439
+ });
2440
+ }
931
2441
  };
932
2442
 
933
2443
  // src/client/factory.ts
@@ -1039,15 +2549,24 @@ var TIMELINE_EVENT_TYPES = [
1039
2549
  "capital_provisioned",
1040
2550
  "capital_cleared",
1041
2551
  "capital_synced",
1042
- "registry_anchored"
2552
+ "registry_registered",
2553
+ "settlement_recorded"
1043
2554
  ];
1044
2555
  function isTimelineEventType(value) {
1045
2556
  return TIMELINE_EVENT_TYPES.includes(value);
1046
2557
  }
1047
2558
 
1048
- // src/types/execution.ts
1049
- function isVaultSettlement(receipt) {
1050
- return receipt.settlement === "vault";
2559
+ // src/types/settlement.ts
2560
+ function formatSettlementSummary(record) {
2561
+ const parts = [
2562
+ "vault settlement (chain-verified)",
2563
+ `block ${record.blockNumber}`,
2564
+ record.explorerUrl
2565
+ ];
2566
+ if (record.registryRef) {
2567
+ parts.push(`registry ${record.registryRef.objectiveKey.slice(0, 10)}\u2026`);
2568
+ }
2569
+ return parts.join(" \xB7 ");
1051
2570
  }
1052
2571
 
1053
2572
  // src/adapters/logging-adapter.ts
@@ -1076,6 +2595,6 @@ function createConsoleLogger(prefix = "aureon-sdk") {
1076
2595
  };
1077
2596
  }
1078
2597
 
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 };
2598
+ export { API_KEY_HEADER, 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, OBJECTIVE_KINDS, OBJECTIVE_PRIORITIES, PRODUCT_NAME, PRODUCT_TAGLINE, SDK_NAME, SDK_VERSION, 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, inferDriftPhase, inferFullAureonLoopPhase, inferPortfolioWatchPhase, inferProofTier, isAureonError, isChainVerifiedReceipt, isHealthState, isObjectiveKind, isObjectivePriority, isTimelineEventType, isValidExecutionReceipt, isVaultSettlement, joinUrl, normalizeCreateObjectiveInput, normalizeUpdateObjectiveInput, parseFinancialIntent, pickWorstHealth, requestJson, resolveFetch, resolveObjectiveFromIntent, silentLogger, validateExecutionReceipt, validateSettlementRecord, withQuery };
1080
2599
  //# sourceMappingURL=index.js.map
1081
2600
  //# sourceMappingURL=index.js.map