@mnemom/mnemom 0.16.1 → 0.16.3
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/README.md +1 -0
- package/dist/commands/agents.d.ts +14 -0
- package/dist/commands/agents.js +100 -2
- package/dist/commands/card.d.ts +43 -0
- package/dist/commands/card.js +153 -102
- package/dist/commands/logs.js +11 -1
- package/dist/commands/onboard.d.ts +59 -0
- package/dist/commands/onboard.js +395 -0
- package/dist/commands/org.d.ts +13 -0
- package/dist/commands/org.js +63 -2
- package/dist/commands/protection.d.ts +10 -0
- package/dist/commands/protection.js +109 -0
- package/dist/commands/status.js +5 -0
- package/dist/commands/try-me.js +16 -1
- package/dist/commands/usage.d.ts +35 -0
- package/dist/commands/usage.js +265 -0
- package/dist/commands/wrap.d.ts +25 -0
- package/dist/commands/wrap.js +331 -0
- package/dist/index.js +192 -6
- package/dist/lib/agent-config.d.ts +27 -0
- package/dist/lib/agent-config.js +86 -0
- package/dist/lib/api.d.ts +122 -1
- package/dist/lib/api.js +128 -183
- package/dist/lib/auth.js +21 -1
- package/dist/lib/cli-config.d.ts +33 -0
- package/dist/lib/cli-config.js +70 -0
- package/dist/lib/config.d.ts +12 -0
- package/dist/lib/config.js +55 -3
- package/dist/lib/keyed-identity.d.ts +35 -0
- package/dist/lib/keyed-identity.js +363 -0
- package/dist/lib/oauth.d.ts +26 -4
- package/dist/lib/oauth.js +98 -29
- package/dist/lib/protection-drift.d.ts +117 -0
- package/dist/lib/protection-drift.js +180 -0
- package/dist/lib/skills.js +25 -12
- package/dist/lib/version-gate.d.ts +37 -0
- package/dist/lib/version-gate.js +84 -0
- package/package.json +7 -7
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent identity config store — ~/.mnemom/agent.json.
|
|
3
|
+
*
|
|
4
|
+
* Persists the calling agent's resolved identity so skill verbs (try-me,
|
|
5
|
+
* onboard, wrap) write on first success and read thereafter — no re-prompt for
|
|
6
|
+
* identity on subsequent invocations (MNE-937).
|
|
7
|
+
*
|
|
8
|
+
* Co-located with auth.ts → auth.json. Separate by design: auth.json holds
|
|
9
|
+
* bearer credentials (wiped on logout); agent.json holds identity metadata
|
|
10
|
+
* that survives re-login and provider key rotation.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { MNEMOM_DIR } from "./config.js";
|
|
15
|
+
function agentFile() {
|
|
16
|
+
return path.join(MNEMOM_DIR, "agent.json");
|
|
17
|
+
}
|
|
18
|
+
/** Load the agent config; a missing or corrupt file returns `{}`. */
|
|
19
|
+
export function loadAgentConfig() {
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(agentFile()))
|
|
22
|
+
return {};
|
|
23
|
+
const parsed = JSON.parse(fs.readFileSync(agentFile(), "utf-8"));
|
|
24
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function writeAgentConfig(config) {
|
|
31
|
+
if (!fs.existsSync(MNEMOM_DIR)) {
|
|
32
|
+
// 0700 to match the auth store — the directory holds credentials.
|
|
33
|
+
fs.mkdirSync(MNEMOM_DIR, { recursive: true, mode: 0o700 });
|
|
34
|
+
}
|
|
35
|
+
const resolvedPath = path.resolve(agentFile());
|
|
36
|
+
const tmpFile = `${resolvedPath}.${process.pid}.tmp`;
|
|
37
|
+
// Write-then-rename for atomicity; 0600 owner-only to match the auth store posture.
|
|
38
|
+
try {
|
|
39
|
+
fs.writeFileSync(tmpFile, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
40
|
+
try {
|
|
41
|
+
fs.chmodSync(tmpFile, 0o600);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
/* best effort on platforms without POSIX perms */
|
|
45
|
+
}
|
|
46
|
+
fs.renameSync(tmpFile, resolvedPath);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// Don't leave a partially-written `${pid}.tmp` sibling behind on a failed
|
|
50
|
+
// write/rename — best-effort unlink, then re-throw the original error.
|
|
51
|
+
try {
|
|
52
|
+
fs.unlinkSync(tmpFile);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* the tmp file may not have been created — nothing to clean up */
|
|
56
|
+
}
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Persist the full agent config (replaces the file). */
|
|
61
|
+
export function saveAgentConfig(config) {
|
|
62
|
+
writeAgentConfig(config);
|
|
63
|
+
}
|
|
64
|
+
/** Shallow-merge `partial` into the existing config and persist. */
|
|
65
|
+
export function mergeAgentConfig(partial) {
|
|
66
|
+
const current = loadAgentConfig();
|
|
67
|
+
const next = { ...current };
|
|
68
|
+
for (const key of Object.keys(partial)) {
|
|
69
|
+
if (partial[key] !== undefined) {
|
|
70
|
+
next[key] = partial[key];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
writeAgentConfig(next);
|
|
74
|
+
}
|
|
75
|
+
export function getAgentId() {
|
|
76
|
+
return loadAgentConfig().agent_id;
|
|
77
|
+
}
|
|
78
|
+
export function getAgentName() {
|
|
79
|
+
return loadAgentConfig().agent_name;
|
|
80
|
+
}
|
|
81
|
+
export function getOrgId() {
|
|
82
|
+
return loadAgentConfig().org_id;
|
|
83
|
+
}
|
|
84
|
+
export function getGatewayUrl() {
|
|
85
|
+
return loadAgentConfig().gateway_url;
|
|
86
|
+
}
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -57,6 +57,15 @@ export interface IntegrityScore {
|
|
|
57
57
|
verified_traces: number;
|
|
58
58
|
violation_count: number;
|
|
59
59
|
integrity_score: number;
|
|
60
|
+
/**
|
|
61
|
+
* MNE-596 — count of traces AAP structurally flagged but were reclassified
|
|
62
|
+
* as a correctly policy-refused attack (agreeing with AIP's `clear`
|
|
63
|
+
* verdict). Logged/visible here for transparency; NOT subtracted from
|
|
64
|
+
* `verified_traces` (they already count as verified) and NOT included in
|
|
65
|
+
* `violation_count`. Optional for backward compatibility with older API
|
|
66
|
+
* deployments that don't yet emit this field.
|
|
67
|
+
*/
|
|
68
|
+
bounded_refusal_count?: number;
|
|
60
69
|
}
|
|
61
70
|
/**
|
|
62
71
|
* GET /v1/traces row — components/schemas/APTrace.
|
|
@@ -103,6 +112,17 @@ export interface Trace {
|
|
|
103
112
|
verification: {
|
|
104
113
|
verified: boolean;
|
|
105
114
|
violations: TraceViolation[];
|
|
115
|
+
/**
|
|
116
|
+
* MNE-596 — when set to "bounded_refusal", AAP structurally flagged a
|
|
117
|
+
* violation (`verified: false`) but the observer's DDR cross-check found
|
|
118
|
+
* AIP independently confirmed `clear` AND no bounded action was ever
|
|
119
|
+
* executed (a pure refusal, no tool call) — i.e. the agent was attacked
|
|
120
|
+
* and correctly declined. This is NOT a real violation: it must not
|
|
121
|
+
* render as `[VIOLATION]` and must not count against integrity_score
|
|
122
|
+
* (see GET /v1/integrity/:id's `bounded_refusal_count`). `violations`
|
|
123
|
+
* stays populated for audit/debugging — only the classification changes.
|
|
124
|
+
*/
|
|
125
|
+
classification?: "bounded_refusal" | string;
|
|
106
126
|
} | null;
|
|
107
127
|
created_at?: string;
|
|
108
128
|
}
|
|
@@ -262,7 +282,11 @@ export interface OrgFleetAgent {
|
|
|
262
282
|
owner_email: string | null;
|
|
263
283
|
last_seen: string | null;
|
|
264
284
|
created_at: string;
|
|
265
|
-
|
|
285
|
+
/** MNE-460: renamed from `integrity_score` to disambiguate from reputation's
|
|
286
|
+
* `integrity_ratio` (a different metric). Trace-verification ratio:
|
|
287
|
+
* verified/total traces, lifetime, 0-1. Type-only in this CLI — never
|
|
288
|
+
* displayed by `fleetToRow`, so this rename carries zero behavioral risk. */
|
|
289
|
+
trace_verification_ratio: number;
|
|
266
290
|
coverage_ratio: number;
|
|
267
291
|
latest_verdict: string | null;
|
|
268
292
|
active_drift_alerts: number;
|
|
@@ -329,6 +353,24 @@ export declare function claimAgent(agentId: string, body: {
|
|
|
329
353
|
}, opts?: {
|
|
330
354
|
idempotencyKey?: string;
|
|
331
355
|
}): Promise<ClaimAgentResult>;
|
|
356
|
+
export interface MoveAgentResult {
|
|
357
|
+
moved: boolean;
|
|
358
|
+
agent_id: string;
|
|
359
|
+
from_org_id: string | null;
|
|
360
|
+
to_org_id: string | null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* POST /v1/agents/:id/move — role-based relocation between orgs.
|
|
364
|
+
*
|
|
365
|
+
* The role-based counterpart to claimAgent's re-claim: the server requires
|
|
366
|
+
* the caller to be an owner/admin of BOTH the agent's current org and
|
|
367
|
+
* `destOrgId` — no hash proof / agent key involved. Moving to the current
|
|
368
|
+
* org is an idempotent no-op (`moved: false`). An Idempotency-Key is minted
|
|
369
|
+
* (held across the 401-refresh retry) like every mutation.
|
|
370
|
+
*/
|
|
371
|
+
export declare function moveAgent(agentId: string, destOrgId: string, opts?: {
|
|
372
|
+
idempotencyKey?: string;
|
|
373
|
+
}): Promise<MoveAgentResult>;
|
|
332
374
|
/**
|
|
333
375
|
* GET /v1/auth/me/personal-org — accessor for the user's personal org.
|
|
334
376
|
* Idempotent: lazily provisions for legacy accounts that pre-date the
|
|
@@ -1044,4 +1086,83 @@ export interface RecipeReportResult {
|
|
|
1044
1086
|
related_recipe_id: string;
|
|
1045
1087
|
}
|
|
1046
1088
|
export declare function reportRecipeFnFp(recipeId: string, input: RecipeReportInput): Promise<RecipeReportResult>;
|
|
1089
|
+
/**
|
|
1090
|
+
* One row from GET /v1/orgs/:org_id/usage/by-person — consumption grouped by
|
|
1091
|
+
* (person, provider, model).
|
|
1092
|
+
*
|
|
1093
|
+
* Field names mirror the wire contract in the API's `openapi.json` exactly.
|
|
1094
|
+
* They are NOT invented here: the first version of this client was written
|
|
1095
|
+
* against a guessed shape (`rows`/`tokens_consumed`/`requests` on a
|
|
1096
|
+
* `/v1/orgs/:id/usage` path) and every one of its tests passed because they
|
|
1097
|
+
* all mocked the response. `/v1/orgs/:id/usage` is a DIFFERENT, pre-existing
|
|
1098
|
+
* endpoint that returns per-DAY operational metrics
|
|
1099
|
+
* (`{ period: 'daily', days, data }`), so the command rendered `undefined`
|
|
1100
|
+
* against the real server. Any change here must be checked against
|
|
1101
|
+
* `openapi.json`, not against these types.
|
|
1102
|
+
*
|
|
1103
|
+
* Token and request totals are 64-bit integers serialized as strings.
|
|
1104
|
+
* Never coerce to Number — values from large orgs can exceed Number.MAX_SAFE_INTEGER.
|
|
1105
|
+
*/
|
|
1106
|
+
export interface OrgUsageRow {
|
|
1107
|
+
/** Null for unattributed consumption — a real row, never dropped. */
|
|
1108
|
+
user_id: string | null;
|
|
1109
|
+
display_name: string;
|
|
1110
|
+
email: string | null;
|
|
1111
|
+
membership_state: string;
|
|
1112
|
+
/** "Unknown" when the event carried no provider. */
|
|
1113
|
+
provider: string;
|
|
1114
|
+
/** "Unknown" when the event carried no model. */
|
|
1115
|
+
model: string;
|
|
1116
|
+
/** 64-bit integer as string. */
|
|
1117
|
+
tokens_in: string;
|
|
1118
|
+
/** 64-bit integer as string. */
|
|
1119
|
+
tokens_out: string;
|
|
1120
|
+
/** 64-bit integer as string. */
|
|
1121
|
+
request_count: string;
|
|
1122
|
+
}
|
|
1123
|
+
/** Totals over the COMPLETE filtered result, computed before pagination. */
|
|
1124
|
+
export interface OrgUsageTotals {
|
|
1125
|
+
tokens_in: string;
|
|
1126
|
+
tokens_out: string;
|
|
1127
|
+
request_count: string;
|
|
1128
|
+
attributed_request_count: string;
|
|
1129
|
+
unattributed_request_count: string;
|
|
1130
|
+
/** Fraction 0–1, already rounded by the server. */
|
|
1131
|
+
attribution_rate: number;
|
|
1132
|
+
}
|
|
1133
|
+
export interface OrgUsageResponse {
|
|
1134
|
+
/** Window start, inclusive (ISO-8601, UTC midnight). */
|
|
1135
|
+
from: string;
|
|
1136
|
+
/** Window end, exclusive (ISO-8601, UTC midnight). */
|
|
1137
|
+
to: string;
|
|
1138
|
+
/** Earliest attributed event for the org, or null before the first one. */
|
|
1139
|
+
collection_started_at: string | null;
|
|
1140
|
+
data: OrgUsageRow[];
|
|
1141
|
+
totals: OrgUsageTotals;
|
|
1142
|
+
/** Opaque keyset cursor, or null on the last page. */
|
|
1143
|
+
next_cursor: string | null;
|
|
1144
|
+
}
|
|
1145
|
+
/** The only windows the endpoint accepts (`days` enum in `openapi.json`). */
|
|
1146
|
+
export declare const USAGE_ALLOWED_DAYS: readonly [7, 30, 90];
|
|
1147
|
+
export type UsageDays = (typeof USAGE_ALLOWED_DAYS)[number];
|
|
1148
|
+
export interface OrgUsageQuery {
|
|
1149
|
+
days?: UsageDays;
|
|
1150
|
+
personId?: string;
|
|
1151
|
+
provider?: string;
|
|
1152
|
+
model?: string;
|
|
1153
|
+
limit?: number;
|
|
1154
|
+
cursor?: string;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* GET /v1/orgs/:org_id/usage/by-person — consumption by person/provider/model.
|
|
1158
|
+
*
|
|
1159
|
+
* Role-gated to owner/admin/auditor; member and viewer get a real 403.
|
|
1160
|
+
*
|
|
1161
|
+
* Gated by USAGE_ATTRIBUTION_API_ENABLED, which returns **404, not 403**, when
|
|
1162
|
+
* off — deliberately, so a disabled endpoint is indistinguishable from one that
|
|
1163
|
+
* does not exist. That makes 404 ambiguous by design (flag off OR unknown org
|
|
1164
|
+
* OR no access), so no `notFoundLabel` is passed here: a caller must not tell
|
|
1165
|
+
* the user "org not found" on what is most often just the flag being off.
|
|
1166
|
+
*/
|
|
1167
|
+
export declare function getOrgUsage(orgId: string, opts?: OrgUsageQuery): Promise<OrgUsageResponse>;
|
|
1047
1168
|
export {};
|