@mnemom/mnemom 0.12.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.
package/dist/lib/api.d.ts CHANGED
@@ -9,56 +9,187 @@ export declare const API_BASE: string;
9
9
  * cached reservation) — when omitted, we mint a UUIDv4.
10
10
  */
11
11
  export declare function newIdempotencyKey(): string;
12
+ /**
13
+ * GET /v1/agents/:id.
14
+ *
15
+ * Source-verified at canonical ref c18491e6 (handleGetAgent): the OWNER view
16
+ * returns the raw agents row plus a computed `status` (active if last_seen
17
+ * within 1h); a non-owner of a claimed+public agent gets publicAgentProjection
18
+ * ({ id, name, claimed: !!claimed_by, last_seen, ... }). The previously-typed
19
+ * `gateway` is a PHANTOM — no agents column and neither projection emits it;
20
+ * it was always undefined. `claimed` is only emitted on the public projection
21
+ * (derived from claimed_by); the owner view carries claimed_by/claimed_at.
22
+ * Fields are optional/nullable to tolerate both projections.
23
+ */
12
24
  export interface Agent {
13
25
  id: string;
14
- gateway: string;
26
+ status?: "active" | "offline";
15
27
  last_seen: string | null;
16
- claimed: boolean;
17
- email?: string;
18
- created_at: string;
28
+ email?: string | null;
29
+ created_at?: string;
30
+ claimed_by?: string | null;
31
+ claimed_at?: string | null;
32
+ name?: string | null;
33
+ claimed?: boolean;
19
34
  }
20
35
  /**
21
- * Per api.mnemom.ai/openapi.json#components/schemas/IntegrityScore:
36
+ * GET /v1/integrity/:id — components/schemas/IntegrityScore:
22
37
  *
23
38
  * { agent_id, total_traces, verified_traces, violation_count, integrity_score }
24
39
  *
25
40
  * `integrity_score` is a value in [0, 1].
26
41
  *
27
- * Pre-this-fix the CLI's interface declared `score` / `verified` /
28
- * `violations` / `last_updated` none of which exist on the wire. The
29
- * `mnemom integrity` command rendered every field as `undefined` (and the
30
- * score as `NaN%`) against any agent. Field names now match the docs
31
- * verbatim.
42
+ * The array-vs-object divergence flagged here previously is RESOLVED upstream:
43
+ * source-verified at canonical contract ref c18491e6, handleGetIntegrity now
44
+ * normalizes BOTH the RPC path (which Supabase wraps as a one-row array) and
45
+ * the manual fallback to a single object with `agent_id` always populated
46
+ * the interface comment in src/index.ts credits "CLI consumers" for the fix.
47
+ * So `agent_id` is reliably emitted (hence required here). The defensive
48
+ * array-unwrap in getIntegrity below is retained only as belt-and-suspenders
49
+ * for a CLI pointed at an older, pre-normalization API.
32
50
  *
33
- * NOTE API shape divergence (flagged for a follow-up mnemom-api PR):
34
- * the RPC path in handleGetIntegrity returns an *array* of one object
35
- * (Supabase wraps TABLE-returning RPCs as arrays) without `agent_id`;
36
- * the manual fallback returns the *object* with `agent_id`. The docs
37
- * canonical is the object shape. We accept both shapes defensively in
38
- * getIntegrity below so this CLI works against today's prod API and
39
- * keeps working when the API normalizes to a single shape.
51
+ * (The spec schema does not list a `required` array, but the emitted-set
52
+ * always includes all five fields doc-set == emitted-set.)
40
53
  */
41
54
  export interface IntegrityScore {
42
- agent_id?: string;
55
+ agent_id: string;
43
56
  total_traces: number;
44
57
  verified_traces: number;
45
58
  violation_count: number;
46
59
  integrity_score: number;
47
60
  }
61
+ /**
62
+ * GET /v1/traces row — components/schemas/APTrace.
63
+ *
64
+ * Source-verified at canonical ref c18491e6: handleGetTraces selects the raw
65
+ * `traces` table rows (no projection), whose columns are AIP-NESTED:
66
+ * `trace_id, timestamp, action(object), decision(object), verification(object|null)`
67
+ * (confirmed by the explicit selects at index.ts:2563 / :4217 and the
68
+ * `trace.decision.selection_reasoning` / `trace.verification?.verified`
69
+ * readers).
70
+ *
71
+ * Pre-this-fix the CLI typed this FLAT — { id, action:string, verified:boolean,
72
+ * reasoning, tool_name } — none of which the wire emits. `mnemom logs` rendered
73
+ * every action as "[object Object]" and flagged EVERY trace as [VIOLATION]
74
+ * (undefined `verified` is falsy). Field names + nesting now match the wire.
75
+ */
48
76
  export interface Trace {
49
- id: string;
77
+ trace_id: string;
50
78
  agent_id: string;
51
79
  timestamp: string;
52
- action: string;
53
- verified: boolean;
54
- reasoning?: string;
55
- tool_name?: string;
56
- tool_input?: Record<string, unknown>;
80
+ action: {
81
+ type?: string;
82
+ name?: string;
83
+ category?: string;
84
+ };
85
+ decision?: {
86
+ selected?: string;
87
+ selection_reasoning?: string;
88
+ values_applied?: string[];
89
+ confidence?: number | null;
90
+ };
91
+ verification: {
92
+ verified: boolean;
93
+ violations: string[];
94
+ } | null;
95
+ created_at?: string;
96
+ }
97
+ /**
98
+ * Canonical API error envelope — backward-tolerant.
99
+ *
100
+ * Source-verified against mnemom-api (2026-05-28): the wire is NOT uniformly
101
+ * nested. Three live shapes:
102
+ * 1. NESTED {error:{code,message}, spec_deviation?} — the spec-validate
103
+ * enforce-hook only (500 internal_error / undocumented_status_code /
104
+ * enforced body deviations). src/runtime/spec-validate-hook.ts.
105
+ * 2. FLAT {error:"message"} — every per-module
106
+ * errorResponse() helper (org/sideband/network/admin/governance + the
107
+ * top-level errorResponse in index.ts). The DOMINANT shape.
108
+ * 3. FLAT+code {error:"message", code:"..."} — auth routes
109
+ * (src/auth/routes.ts).
110
+ * No route emits a top-level `message` field; `details` is reserved/forward-
111
+ * tolerant. We parse all three and prefer nested.
112
+ *
113
+ * LIVE-WIRE NOTE (shepherd 48-probe sweep, staging+prod): the spec-validate
114
+ * ENFORCE hook is active on both deployed envs and normalizes every body to the
115
+ * NESTED shape before the wire — so in practice the CLI sees nested universally.
116
+ * The flat / flat+code tolerance is insurance for non-enforce contexts
117
+ * (local dev server, enforce=observe). The hook also adds a top-level
118
+ * `spec_deviation` sibling AND can REWRITE an undocumented status to a synthetic
119
+ * 500 (e.g. an undocumented 404 → 500 with `spec_deviation.original_status:404`).
120
+ * We surface that as MnemomApiError.effectiveStatus so command-layer 404/empty-
121
+ * state branching isn't fooled by the rewrite.
122
+ *
123
+ * Pre-this-fix ApiError was flat {error,message} read at `error.message` (top
124
+ * level) — undefined for every real shape → the server message was dropped.
125
+ */
126
+ export interface SpecDeviation {
127
+ keyword?: string;
128
+ field?: string;
129
+ original_status?: number;
57
130
  }
58
131
  export interface ApiError {
59
- error: string;
60
- message: string;
132
+ error?: string | {
133
+ code?: string;
134
+ message?: string;
135
+ details?: unknown;
136
+ };
137
+ message?: string;
138
+ code?: string;
139
+ details?: unknown;
140
+ conflict_agent_id?: string;
141
+ spec_deviation?: SpecDeviation;
61
142
  }
143
+ /** Normalized error fields extracted from any of the three envelope shapes. */
144
+ export interface ParsedApiError {
145
+ message?: string;
146
+ code?: string;
147
+ details?: unknown;
148
+ conflict_agent_id?: string;
149
+ spec_deviation?: SpecDeviation;
150
+ }
151
+ /**
152
+ * Extract the human message + code + details from a raw error body,
153
+ * tolerating all three live envelope shapes (prefer nested).
154
+ */
155
+ export declare function normalizeApiError(raw: ApiError | null | undefined): ParsedApiError;
156
+ /** Parse a non-OK Response body into normalized error fields (never throws). */
157
+ export declare function parseApiErrorBody(response: Response): Promise<ParsedApiError>;
158
+ /**
159
+ * Structured API error. `message` is the human-readable server message (or a
160
+ * fallback); `.status`/`.code`/`.details`/`.conflictAgentId` give callers a
161
+ * structured surface to branch on without parsing the message string.
162
+ * `instanceof MnemomApiError` narrows in command-layer catch blocks.
163
+ */
164
+ export declare class MnemomApiError extends Error {
165
+ /** The wire HTTP status — may be a synthetic 500 from the enforce hook. */
166
+ readonly status: number;
167
+ /**
168
+ * The TRUE intended status: `spec_deviation.original_status ?? status`. The
169
+ * enforce hook can rewrite an undocumented status to a synthetic 500; command
170
+ * layers MUST branch on `effectiveStatus` (not `status`) for 404/empty-state
171
+ * detection or they'll be fooled by the rewrite.
172
+ */
173
+ readonly effectiveStatus: number;
174
+ readonly code?: string;
175
+ readonly details?: unknown;
176
+ readonly conflictAgentId?: string;
177
+ /** Top-level enforce-hook sibling (present only on a spec-rewritten body). */
178
+ readonly specDeviation?: SpecDeviation;
179
+ constructor(status: number, message: string, opts?: {
180
+ code?: string;
181
+ details?: unknown;
182
+ conflictAgentId?: string;
183
+ specDeviation?: SpecDeviation;
184
+ });
185
+ }
186
+ /**
187
+ * Read a non-OK Response into a MnemomApiError, preferring the server message
188
+ * and falling back to `${fallback}: ${status}` when the body carried none.
189
+ * Captures the enforce-hook `spec_deviation` sibling so `.effectiveStatus`
190
+ * reflects the true intended status behind any synthetic-500 rewrite.
191
+ */
192
+ export declare function readApiError(response: Response, fallback: string): Promise<MnemomApiError>;
62
193
  export declare function postApi<T>(endpoint: string, body: unknown, opts?: {
63
194
  idempotencyKey?: string;
64
195
  }): Promise<T>;
@@ -76,6 +207,13 @@ export interface AgentListItem {
76
207
  containment_status: string | null;
77
208
  key_prefix?: string | null;
78
209
  }
210
+ /**
211
+ * @deprecated Legacy per-user listing — `GET /v1/agents` is scoped by the
212
+ * caller's `claimed_by` rows, which ADR-062 retired as an authorization /
213
+ * listing boundary (org_id is now the sole boundary). New code must list via
214
+ * {@link listOrgAgents} (org-scoped). Retained only for back-compat callers;
215
+ * the `mnemom agents` command and name resolution no longer use it.
216
+ */
79
217
  export declare function listAgents(): Promise<AgentListItem[]>;
80
218
  export interface OrgListItem {
81
219
  org_id: string;
@@ -100,6 +238,85 @@ export interface PersonalOrgRef {
100
238
  * first; multi-user orgs follow.
101
239
  */
102
240
  export declare function listMyOrgs(): Promise<OrgListItem[]>;
241
+ /**
242
+ * One agent row from the org-scoped fleet (`get_org_agent_fleet`). Mirrors the
243
+ * website's `OrgFleetAgent` shape so the CLI and dashboard read the same
244
+ * surface. This is the ADR-062-canonical listing: every agent has exactly one
245
+ * governing org, and the fleet is keyed on `org_id` (not `claimed_by`).
246
+ */
247
+ export interface OrgFleetAgent {
248
+ agent_id: string;
249
+ agent_name: string;
250
+ owner_email: string | null;
251
+ last_seen: string | null;
252
+ created_at: string;
253
+ integrity_score: number;
254
+ coverage_ratio: number;
255
+ latest_verdict: string | null;
256
+ active_drift_alerts: number;
257
+ worst_drift_severity: string | null;
258
+ check_count: number;
259
+ containment_status?: string | null;
260
+ avatar_url?: string | null;
261
+ }
262
+ /**
263
+ * GET /v1/orgs/:org_id/agents — the org-scoped agent fleet (ADR-062). Any
264
+ * member of the org may read it; non-members get 403.
265
+ */
266
+ export declare function fetchOrgFleet(orgId: string): Promise<OrgFleetAgent[]>;
267
+ /** An {@link AgentListItem} tagged with the org it belongs to. */
268
+ export interface OrgAgentRow extends AgentListItem {
269
+ org_id: string;
270
+ org_name: string;
271
+ }
272
+ /**
273
+ * List agents the ADR-062 way: scoped by org membership, never by `claimed_by`.
274
+ *
275
+ * - With `orgId`: lists that single org's fleet.
276
+ * - Without `orgId`: aggregates the fleet across every org the caller belongs
277
+ * to (the same pattern as {@link listAllTeams}), tagging each row with its
278
+ * org. Orgs the caller can't read (403/404) are skipped silently so one
279
+ * inaccessible org doesn't fail the whole listing.
280
+ */
281
+ export declare function listOrgAgents(orgId?: string): Promise<OrgAgentRow[]>;
282
+ /**
283
+ * Result of a successful agent claim (ADR-062). Mirrors the locked
284
+ * `POST /v1/agents/:id/claim` 200 body verbatim — only the fields the
285
+ * endpoint actually returns. `org_id` is the org the agent ACTUALLY landed
286
+ * in (resolved server-side: the requested org, or the caller's personal org
287
+ * when none was supplied), so the CLI can confirm placement without a
288
+ * follow-up read. Optional/defaulted on the type so a mid-deploy CLI pointed
289
+ * at an older API that omits a field degrades to a sane value rather than
290
+ * crashing.
291
+ */
292
+ export interface ClaimAgentResult {
293
+ claimed: boolean;
294
+ agent_id: string;
295
+ /** Resolved landing org (may differ from the request); null if unscoped. */
296
+ org_id: string | null;
297
+ /** ISO-8601 claim timestamp; null if the server omits it. */
298
+ claimed_at: string | null;
299
+ }
300
+ /**
301
+ * POST /v1/agents/:id/claim — claim an agent into an org (ADR-062).
302
+ *
303
+ * Body is `{ hash_proof, org_id? }`. `hash_proof` (the agent's full SHA-256
304
+ * proof) authenticates the caller as the agent's owner; the user's own auth
305
+ * header rides along too so the platform can validate org membership and link
306
+ * the principal. Omit `orgId` to land in the caller's personal org (the
307
+ * platform default); a supplied `orgId` is validated against the caller's
308
+ * memberships server-side (a non-member gets a 403 the command layer turns
309
+ * into a teaching error listing claimable orgs).
310
+ *
311
+ * An Idempotency-Key is minted (and held across the 401-refresh retry) so a
312
+ * retried claim replays the same logical operation server-side.
313
+ */
314
+ export declare function claimAgent(agentId: string, body: {
315
+ hashProof: string;
316
+ orgId?: string;
317
+ }, opts?: {
318
+ idempotencyKey?: string;
319
+ }): Promise<ClaimAgentResult>;
103
320
  /**
104
321
  * GET /v1/auth/me/personal-org — accessor for the user's personal org.
105
322
  * Idempotent: lazily provisions for legacy accounts that pre-date the
@@ -238,11 +455,11 @@ export declare function revokeTeamAdmin(teamId: string, userId: string): Promise
238
455
  */
239
456
  export declare function listTeamAdmins(teamId: string): Promise<TeamAdminListResponse>;
240
457
  /**
241
- * Look up an agent in the authenticated user's account by name.
458
+ * Look up an agent by name across the caller's org fleets (ADR-062-scoped via
459
+ * {@link listOrgAgents}, not the legacy `claimed_by` list).
242
460
  * Tries exact match first, then single partial match.
243
461
  * Throws if multiple agents partially match (ambiguous).
244
462
  * Returns null if no match found.
245
- * Note: capped at 100 agents by listAgents().
246
463
  */
247
464
  export declare function getAgentByName(name: string): Promise<AgentListItem | null>;
248
465
  /**
@@ -270,88 +487,6 @@ export declare function getIntegrity(id: string): Promise<IntegrityScore>;
270
487
  * don't need a special case for it.
271
488
  */
272
489
  export declare function getTraces(id: string, limit?: number): Promise<Trace[]>;
273
- export interface AlignmentCard {
274
- card_id?: string;
275
- version?: string;
276
- issued_at?: string;
277
- expires_at?: string;
278
- principal?: {
279
- name?: string;
280
- type?: string;
281
- organization?: string;
282
- };
283
- values?: {
284
- declared?: string[];
285
- definitions?: Record<string, string>;
286
- };
287
- autonomy_envelope?: {
288
- bounded_actions?: string[];
289
- forbidden_actions?: string[];
290
- escalation_triggers?: Array<{
291
- condition: string;
292
- action?: string;
293
- }>;
294
- };
295
- audit_commitment?: {
296
- log_level?: string;
297
- retention_days?: number;
298
- access_policy?: string;
299
- };
300
- extensions?: Record<string, unknown>;
301
- [key: string]: unknown;
302
- }
303
- export interface CardResponse {
304
- card_id: string;
305
- agent_id: string;
306
- card_json: AlignmentCard;
307
- created_at: string;
308
- updated_at: string;
309
- }
310
- export declare function getCard(agentId: string): Promise<CardResponse | null>;
311
- export declare function updateCard(agentId: string, cardJson: AlignmentCard, opts?: {
312
- idempotencyKey?: string;
313
- }): Promise<{
314
- updated: boolean;
315
- card_id: string;
316
- }>;
317
- export declare function reverifyAgent(agentId: string, opts?: {
318
- idempotencyKey?: string;
319
- }): Promise<{
320
- reverified: number;
321
- }>;
322
- export interface PolicyResponse {
323
- id: string;
324
- name: string;
325
- description: string | null;
326
- policy_json: Record<string, unknown>;
327
- version: number;
328
- created_by: string;
329
- created_at: string;
330
- }
331
- export interface PolicyListResponse {
332
- agent_id: string;
333
- policy: PolicyResponse | null;
334
- }
335
- export declare function getPolicy(agentId: string): Promise<PolicyListResponse | null>;
336
- export declare function publishPolicy(agentId: string, policyJson: Record<string, unknown>, opts?: {
337
- idempotencyKey?: string;
338
- }): Promise<{
339
- id: string;
340
- version: number;
341
- created: boolean;
342
- }>;
343
- export declare function testPolicyHistorical(agentId: string, policyJson: Record<string, unknown>, limit?: number): Promise<{
344
- agent_id: string;
345
- policy_name: string;
346
- total_traces: number;
347
- results: unknown[];
348
- summary: {
349
- pass: number;
350
- warn: number;
351
- fail: number;
352
- skipped: number;
353
- };
354
- }>;
355
490
  /**
356
491
  * Fetch the canonical alignment card as YAML (or JSON fallback).
357
492
  * Returns the raw response body as a string.
@@ -402,6 +537,30 @@ export declare const PROTECTION_CARD_MAX_BYTES: number;
402
537
  export declare function putProtectionCard(agentId: string, body: string, contentType?: "text/yaml" | "application/json", opts?: {
403
538
  idempotencyKey?: string;
404
539
  }): Promise<PublishedCardResponse>;
540
+ export interface CardPreviewResult {
541
+ /** true ⇔ the server accepted+composed the card (HTTP 200). */
542
+ valid: boolean;
543
+ status: number;
544
+ /** Canonical composed card (200 only). */
545
+ composed?: Record<string, unknown>;
546
+ /** Per-field tightenings applied by the org/platform floor (200 only). */
547
+ conflicts?: unknown[];
548
+ /** Warning-mode coherence findings (200 only). */
549
+ coherence_violations?: unknown[];
550
+ /** Parsed error envelope on a 4xx validation failure (invalid only). */
551
+ error?: {
552
+ code?: string;
553
+ message?: string;
554
+ details?: unknown;
555
+ };
556
+ }
557
+ /**
558
+ * Dry-run validate + compose a card body against the server's authoritative
559
+ * pipeline. Returns a structured result — a 422/400 validation failure is an
560
+ * expected `valid:false` outcome (NOT a thrown error). 401 throws (caller
561
+ * should fall back to offline validation); 403/5xx throw a MnemomApiError.
562
+ */
563
+ export declare function previewComposeAgentCard(agentId: string, kind: TeamTemplateKind, body: string, contentType?: "text/yaml" | "application/json"): Promise<CardPreviewResult>;
405
564
  /**
406
565
  * Resolve an agent name or ID to a server agent ID.
407
566
  *
@@ -646,7 +805,7 @@ export declare function getSafeHouseHarnessState(): Promise<{
646
805
  fast: HarnessRunSummary | null;
647
806
  }>;
648
807
  export type GovernanceSignalScope = "platform" | "org" | "team" | "agent";
649
- export type GovernanceSignalSource = "sideband.drift" | "sideband.coherence" | "sideband.fault_line" | "sideband.fleet";
808
+ export type GovernanceSignalSource = "sideband.drift" | "sideband.coherence" | "sideband.fault_line" | "sideband.fleet" | "network.threat_level.changed";
650
809
  export type GovernanceSignalSeverity = "info" | "warn" | "high" | "critical";
651
810
  export type GovernanceSignalStatus = "open" | "acknowledged" | "resolved" | "dismissed" | "expired";
652
811
  export type GovernanceResolutionStatus = "action_taken" | "wont_fix" | "duplicate" | "false_positive" | "self_resolved";
@@ -674,6 +833,7 @@ export interface GovernanceSignal {
674
833
  resolved_by: string | null;
675
834
  resolved_at: string | null;
676
835
  expires_at: string | null;
836
+ webhook_delivery_id: string | null;
677
837
  notification_state: Record<string, unknown>;
678
838
  created_at: string;
679
839
  updated_at: string;
@@ -828,6 +988,36 @@ export declare function rotateApiKey(keyId: string): Promise<ApiKeyCreated>;
828
988
  * audit; `is_active` flips to false and `revoked_at` is timestamped.
829
989
  */
830
990
  export declare function revokeApiKey(keyId: string): Promise<void>;
991
+ export interface LicenseValidationResult {
992
+ valid: boolean;
993
+ license_id?: string;
994
+ plan_id?: string;
995
+ feature_flags?: Record<string, boolean>;
996
+ limits?: Record<string, unknown>;
997
+ expires_at?: string;
998
+ next_check_seconds?: number;
999
+ warning?: string | null;
1000
+ [key: string]: unknown;
1001
+ }
1002
+ /**
1003
+ * POST /v1/license/validate — validate a license JWT for an instance.
1004
+ *
1005
+ * UNAUTHENTICATED by design (the license JWT IS the credential; this is a
1006
+ * pre-login path) — sends NO auth header. Source-verified at canonical ref
1007
+ * c18491e6 (handleLicenseValidate): the request body keys are
1008
+ * `{ license, instance_id, instance_metadata }` (NOT `jwt`) — sending `jwt`
1009
+ * would 400 "license is required".
1010
+ *
1011
+ * Returns the parsed validation result on 2xx (including the grace-period
1012
+ * `valid:false` 200 body); throws MnemomApiError on non-2xx so the command
1013
+ * renders `.message`/`.status`. This fixes the live H1 "[object Object]" bug:
1014
+ * the old command-layer `err.error || "unknown"` string-coerced the nested
1015
+ * `{code,message}` object that the enforce hook puts on the wire.
1016
+ *
1017
+ * `license deactivate` may reuse this (best-effort, fire-and-forget) by
1018
+ * catching the throw at the command layer.
1019
+ */
1020
+ export declare function validateLicense(jwt: string, instanceId: string, instanceMetadata?: Record<string, unknown>): Promise<LicenseValidationResult>;
831
1021
  export interface RecipeReportInput {
832
1022
  type: "fn" | "fp";
833
1023
  summary: string;