@gethelio/proxy 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  <meta name="referrer" content="no-referrer" />
7
7
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
8
  <title>Helio Dashboard</title>
9
- <script type="module" crossorigin src="/assets/index-BRvkMXWl.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-D9zeFAzU.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-DtnT1Y9r.css">
11
11
  </head>
12
12
  <body>
package/dist/index.d.ts CHANGED
@@ -735,6 +735,10 @@ declare class ConfigError extends Error {
735
735
  */
736
736
  declare function loadConfig(filePath: string, env?: Record<string, string | undefined>): Promise<HelioConfig>;
737
737
 
738
+ /** Every outcome a reload attempt can record. `watch_failed` means the watch itself failed; nothing was read. */
739
+ declare const POLICY_RELOAD_OUTCOMES: readonly ["applied", "rejected_invalid", "rejected_unroutable", "rejected_budget_flush", "rejected_pinned", "watch_failed"];
740
+ type PolicyReloadOutcome = (typeof POLICY_RELOAD_OUTCOMES)[number];
741
+
738
742
  /** Pre-compiled glob matcher for tool names. */
739
743
  interface ToolMatcher {
740
744
  /** Original glob pattern string (e.g. "send_*") for audit/logging. */
@@ -1461,9 +1465,12 @@ interface AuditRecord {
1461
1465
  * records; `'install_scan'` for sideband install evaluations; and
1462
1466
  * `'evaluation_expired'` for sideband evaluations whose `/audit` never
1463
1467
  * arrived (the bypass/tamper signal — block_reason stays null so they do not
1464
- * count as enforcement blocks).
1468
+ * count as enforcement blocks); and `'policy_reload'` for a config reload
1469
+ * attempt the proxy records about itself (issue #341): the outcome is in
1470
+ * `block_reason` and under `evidence_chain.policy_reload`, and the decision
1471
+ * column holds the constant `policy_reload` so analytics can exclude the kind.
1465
1472
  */
1466
- readonly record_kind: 'tool_call' | 'drift_event' | 'install_scan' | 'evaluation_expired';
1473
+ readonly record_kind: 'tool_call' | 'drift_event' | 'install_scan' | 'evaluation_expired' | 'policy_reload';
1467
1474
  /**
1468
1475
  * Enforcement origin: `'mcp'` for the proxy path, or an adapter-supplied
1469
1476
  * origin string (e.g. `'openclaw'`) for sideband-governed calls. Surfaces
@@ -1492,9 +1499,20 @@ interface AuditRecord {
1492
1499
  * could silently omit it would exempt that door from attribution.
1493
1500
  */
1494
1501
  readonly upstream: string | null;
1502
+ /**
1503
+ * SHA-256 (lowercase hex) of the config file bytes in force when the
1504
+ * record was written (issue #341); the value `helio config hash` prints.
1505
+ * Stamped by the writer, never by a record builder. Null on rows written
1506
+ * before the column existed.
1507
+ */
1508
+ readonly config_sha256: string | null;
1495
1509
  /** ISO 8601 timestamp of when the record was persisted. */
1496
1510
  readonly created_at: string;
1497
1511
  }
1512
+ /** What a record builder produces: the stored shape without the ids and with the writer-stamped hash optional. */
1513
+ type AuditRecordInput = Omit<AuditRecord, 'id' | 'created_at' | 'config_sha256'> & {
1514
+ readonly config_sha256?: string | null;
1515
+ };
1498
1516
  /** Fields available for filtering audit record queries. */
1499
1517
  interface AuditQueryFilters {
1500
1518
  /** Filter by tool name (exact match). */
@@ -1687,7 +1705,7 @@ declare class AuditStore {
1687
1705
  * @param createdAt - Optional override for the created_at timestamp (for testing).
1688
1706
  * @param id - Optional pre-generated ID (used by AuditWriter to share ID with SSE event bus).
1689
1707
  */
1690
- insert(record: Omit<AuditRecord, 'id' | 'created_at'>, createdAt?: string, id?: string): string;
1708
+ insert(record: AuditRecordInput, createdAt?: string, id?: string): string;
1691
1709
  /**
1692
1710
  * Insert multiple audit records in a single SQLite transaction.
1693
1711
  *
@@ -1703,7 +1721,7 @@ declare class AuditStore {
1703
1721
  * the SSE event bus before the record is persisted.
1704
1722
  * @returns The number of records successfully inserted.
1705
1723
  */
1706
- insertBatch(records: ReadonlyArray<Omit<AuditRecord, 'id' | 'created_at'>>, onError?: (record: Omit<AuditRecord, 'id' | 'created_at'>, err: unknown) => void, ids?: ReadonlyArray<string>, onPersist?: (record: Omit<AuditRecord, 'id' | 'created_at'>, id: string) => void): number;
1724
+ insertBatch(records: ReadonlyArray<AuditRecordInput>, onError?: (record: AuditRecordInput, err: unknown) => void, ids?: ReadonlyArray<string>, onPersist?: (record: AuditRecordInput, id: string) => void): number;
1707
1725
  /** Get a single record by ID, or undefined if not found. */
1708
1726
  get(id: string): AuditRecord | undefined;
1709
1727
  /** Query records with filters and pagination. Capped at 1,000 per page. */
@@ -1743,9 +1761,11 @@ interface AuditWriterOptions {
1743
1761
  /** Max milliseconds between flushes (default: 100). */
1744
1762
  readonly flushIntervalMs?: number;
1745
1763
  /** Optional callback invoked when a record enters the in-memory buffer. */
1746
- readonly onPush?: (record: Omit<AuditRecord, 'id' | 'created_at'>, id: string) => void;
1764
+ readonly onPush?: (record: AuditRecordInput, id: string) => void;
1747
1765
  /** Optional callback invoked after a record is successfully persisted. */
1748
- readonly onPersist?: (record: Omit<AuditRecord, 'id' | 'created_at'>, id: string) => void;
1766
+ readonly onPersist?: (record: AuditRecordInput, id: string) => void;
1767
+ /** The hash of the config in force at construction; every record that leaves `config_sha256` nullish is stamped with the current value. */
1768
+ readonly configSha256?: string;
1749
1769
  }
1750
1770
  /**
1751
1771
  * Async buffered audit record writer.
@@ -1759,6 +1779,15 @@ declare class AuditWriter {
1759
1779
  private readonly bufferSize;
1760
1780
  private readonly onPush;
1761
1781
  private readonly onPersist;
1782
+ /**
1783
+ * The hash of the config file in force (issue #341), stamped onto every
1784
+ * record whose builder left `config_sha256` nullish. Seeded at
1785
+ * construction so no record is pushed unstamped before the first
1786
+ * `setConfigSha256`; replaced by the reload path once the new policy is
1787
+ * in force. Null only when the caller never had a hash (library
1788
+ * embeddings without a config file).
1789
+ */
1790
+ private configSha256;
1762
1791
  private buffer;
1763
1792
  private timer;
1764
1793
  private flushSoonTimer;
@@ -1774,7 +1803,7 @@ declare class AuditWriter {
1774
1803
  * is scheduled. This keeps request-path latency bounded even under bursty
1775
1804
  * write load.
1776
1805
  */
1777
- push(record: Omit<AuditRecord, 'id' | 'created_at'>, id?: string): void;
1806
+ push(input: AuditRecordInput, id?: string): void;
1778
1807
  /**
1779
1808
  * Push a record and schedule a high-priority async flush.
1780
1809
  *
@@ -1783,7 +1812,20 @@ declare class AuditWriter {
1783
1812
  * A fatal-process crash still invokes the crash-drain hook, which calls
1784
1813
  * `flush()` synchronously before exit.
1785
1814
  */
1786
- pushImmediate(record: Omit<AuditRecord, 'id' | 'created_at'>, id?: string): void;
1815
+ pushImmediate(input: AuditRecordInput, id?: string): void;
1816
+ /**
1817
+ * Replace the config hash every later record is stamped with. Called by
1818
+ * the reload path after the new policy is in force, before the reload's
1819
+ * own record is pushed, so the reload record and every call it governs
1820
+ * carry the new hash.
1821
+ */
1822
+ setConfigSha256(hash: string): void;
1823
+ /**
1824
+ * Stamp the active config hash onto a record whose builder left the field
1825
+ * nullish; a record that already carries a hash is passed through. The
1826
+ * stamped record is what `onPush`, the store, and `onPersist` see.
1827
+ */
1828
+ private stamp;
1787
1829
  /**
1788
1830
  * Schedule a flush on the next tick, coalescing multiple calls into one.
1789
1831
  */
@@ -3463,7 +3505,23 @@ declare class GovernanceConfigError extends Error {
3463
3505
  * reserved `'<header_mismatch>'` sentinel — either way `top_tools` already
3464
3506
  * excludes rejected records, so these rows never pollute tool rankings.
3465
3507
  */
3466
- declare function buildHeaderMismatchAuditRecord(rejection: HeaderMismatchRejection, environment?: string, upstream?: string): Omit<AuditRecord, 'id' | 'created_at'>;
3508
+ declare function buildHeaderMismatchAuditRecord(rejection: HeaderMismatchRejection, environment?: string, upstream?: string): AuditRecordInput;
3509
+
3510
+ interface PolicyReloadEvidence {
3511
+ readonly outcome: PolicyReloadOutcome;
3512
+ readonly config_path: string;
3513
+ readonly sha256_before: string;
3514
+ readonly sha256_after: string | null;
3515
+ readonly rule_count_before: number;
3516
+ readonly rule_count_after: number | null;
3517
+ readonly default_action_before: 'allow' | 'deny';
3518
+ readonly default_action_after: 'allow' | 'deny' | null;
3519
+ readonly budget_count_before: number;
3520
+ readonly budget_count_after: number | null;
3521
+ readonly rules_removed: readonly string[];
3522
+ readonly restart_required_paths: readonly string[];
3523
+ readonly error: string | null;
3524
+ }
3467
3525
 
3468
3526
  /** @internal Exported for testing only. */
3469
3527
  declare class QueueChannel implements ApprovalChannel {
@@ -3659,6 +3717,13 @@ type BudgetUpdateEvent = BudgetCommitEvent;
3659
3717
  * engine's breach-event DTO.
3660
3718
  */
3661
3719
  type BudgetBreachedEvent = BudgetBreachEvent;
3720
+ /** A policy reload attempt, emitted beside the record's own `action` event (issue #341). */
3721
+ interface PolicyReloadEvent extends PolicyReloadEvidence {
3722
+ /** The audit record's id. */
3723
+ readonly id: string;
3724
+ /** The record's timestamp. */
3725
+ readonly at: string;
3726
+ }
3662
3727
  /** Map of event type names to their payload types. */
3663
3728
  interface DashboardEvents {
3664
3729
  action: ActionEvent;
@@ -3668,6 +3733,7 @@ interface DashboardEvents {
3668
3733
  approval_notification_failed: ApprovalNotificationFailedEvent;
3669
3734
  budget_update: BudgetUpdateEvent;
3670
3735
  budget_breached: BudgetBreachedEvent;
3736
+ policy_reload: PolicyReloadEvent;
3671
3737
  }
3672
3738
  /** Union of all dashboard event type names. */
3673
3739
  type DashboardEventType = keyof DashboardEvents;