@mnemom/mnemom 0.17.31 → 0.17.32-next.1

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.
Files changed (46) hide show
  1. package/dist/base-url-proxy.mjs +30 -0
  2. package/dist/commands/agent-fuel.d.ts +75 -0
  3. package/dist/commands/agent-fuel.js +298 -0
  4. package/dist/commands/agent-implicit-notice.d.ts +31 -0
  5. package/dist/commands/agent-implicit-notice.js +73 -0
  6. package/dist/commands/agent-session.d.ts +2 -1
  7. package/dist/commands/agent-session.js +2 -1
  8. package/dist/commands/agent-share-fixture.d.ts +20 -0
  9. package/dist/commands/agent-share-fixture.js +134 -0
  10. package/dist/commands/agent-share.d.ts +87 -0
  11. package/dist/commands/agent-share.js +251 -0
  12. package/dist/commands/agent.d.ts +75 -2
  13. package/dist/commands/agent.js +165 -28
  14. package/dist/commands/status.js +1 -1
  15. package/dist/commands/update.js +3 -1
  16. package/dist/commands/validate.d.ts +1 -1
  17. package/dist/commands/validate.js +1 -1
  18. package/dist/index.js +68 -12
  19. package/dist/lib/agent.d.ts +34 -19
  20. package/dist/lib/agent.js +41 -32
  21. package/dist/lib/api.d.ts +66 -0
  22. package/dist/lib/api.js +50 -10
  23. package/dist/lib/canonical-json.d.ts +12 -0
  24. package/dist/lib/canonical-json.js +22 -0
  25. package/dist/lib/commit-report.d.ts +109 -0
  26. package/dist/lib/commit-report.js +205 -0
  27. package/dist/lib/conduct-receipt.d.ts +81 -0
  28. package/dist/lib/conduct-receipt.js +177 -0
  29. package/dist/lib/contract-hash.d.ts +61 -0
  30. package/dist/lib/contract-hash.js +126 -0
  31. package/dist/lib/experimental.d.ts +10 -1
  32. package/dist/lib/experimental.js +35 -1
  33. package/dist/lib/oauth.d.ts +2 -1
  34. package/dist/lib/oauth.js +2 -1
  35. package/dist/lib/statusline-bin.d.ts +2 -2
  36. package/dist/lib/statusline-bin.js +4 -7
  37. package/dist/lib/statusline-feed.d.ts +33 -0
  38. package/dist/lib/statusline-feed.js +68 -0
  39. package/dist/lib/statusline.d.ts +39 -88
  40. package/dist/lib/statusline.js +58 -250
  41. package/dist/lib/update/decide.d.ts +22 -3
  42. package/dist/lib/update/decide.js +35 -12
  43. package/dist/lib/update/doctor.js +5 -1
  44. package/dist/lib/update/run.d.ts +2 -0
  45. package/dist/lib/update/run.js +22 -8
  46. package/package.json +1 -1
@@ -15,8 +15,13 @@
15
15
  * for entitled callers (internal cohort, staff, `cli_next` invite —
16
16
  * GET /v1/cli/channel-entitlement); otherwise it behaves as latest.
17
17
  *
18
- * Never downgrades automatically: switching next → latest while ahead of latest
19
- * keeps the current build until latest overtakes it (`mnemom update --force`).
18
+ * Never downgrades on its own, with one exception: a moved tag. Switching
19
+ * next → latest while ahead of latest keeps the current build until latest
20
+ * overtakes it (`mnemom update --force`). But when the channel pointed at the
21
+ * running build on the last check and now points lower, the tag was moved back
22
+ * (a bad release rolled back by hand: OIDC publishing can't move a tag, a
23
+ * person can), and the next check follows it down. A build the channel never
24
+ * pointed at (a dev build, a newer local install) is never downgraded.
20
25
  */
21
26
  export type UpdateChannel = "latest" | "next";
22
27
  export type UpdateMode = "auto" | "notify" | "off";
@@ -64,6 +69,13 @@ export interface UpdateState {
64
69
  method?: InstallMethod;
65
70
  /** Channel the last successful update installed from; feeds defaultChannel. */
66
71
  followChannel?: UpdateChannel;
72
+ /** What the channel pointed at on the last check; detects a tag moved back. */
73
+ channelTarget?: {
74
+ channel: UpdateChannel;
75
+ version: string;
76
+ };
77
+ /** `available` is the channel's tag moved back below the running build. */
78
+ movedBack?: boolean;
67
79
  /** The background worker startup last spawned, while it runs (cleared when it finishes). */
68
80
  workerPid?: number;
69
81
  workerSince?: number;
@@ -124,6 +136,11 @@ export declare function entitlementFresh(cache: EntitlementCache | undefined, no
124
136
  * registry response).
125
137
  */
126
138
  export declare function pickTarget(tags: DistTags, channel: UpdateChannel): string | undefined;
139
+ /**
140
+ * The channel's tag was moved back off the running build: on the last check it
141
+ * pointed at `current`, and now it points at an older version.
142
+ */
143
+ export declare function tagMovedBack(previous: UpdateState["channelTarget"], channel: UpdateChannel, current: string, target: string | undefined): boolean;
127
144
  /**
128
145
  * Longest a worker lives: it waits for its parent `mnemom` (a whole agent session)
129
146
  * up to this long. A recorded worker older than this is treated as gone, so a
@@ -155,7 +172,8 @@ export type UpdateAction = {
155
172
  /**
156
173
  * The single decision: given where the registry is and how we're installed,
157
174
  * install / notify / do nothing. `force` (explicit `mnemom update --force`)
158
- * permits a downgrade to the channel's target.
175
+ * permits a downgrade to the channel's target; `followMovedTag` (see
176
+ * tagMovedBack) permits one to the version the channel's tag was moved back to.
159
177
  */
160
178
  export declare function decide(input: {
161
179
  current: string;
@@ -164,6 +182,7 @@ export declare function decide(input: {
164
182
  method: InstallMethod;
165
183
  installBlocked?: boolean;
166
184
  force?: boolean;
185
+ followMovedTag?: boolean;
167
186
  }): UpdateAction;
168
187
  /** The command a human should run to upgrade, for the given install method. */
169
188
  export declare function manualUpgradeCommand(method: InstallMethod, version: string): string;
@@ -15,8 +15,13 @@
15
15
  * for entitled callers (internal cohort, staff, `cli_next` invite —
16
16
  * GET /v1/cli/channel-entitlement); otherwise it behaves as latest.
17
17
  *
18
- * Never downgrades automatically: switching next → latest while ahead of latest
19
- * keeps the current build until latest overtakes it (`mnemom update --force`).
18
+ * Never downgrades on its own, with one exception: a moved tag. Switching
19
+ * next → latest while ahead of latest keeps the current build until latest
20
+ * overtakes it (`mnemom update --force`). But when the channel pointed at the
21
+ * running build on the last check and now points lower, the tag was moved back
22
+ * (a bad release rolled back by hand: OIDC publishing can't move a tag, a
23
+ * person can), and the next check follows it down. A build the channel never
24
+ * pointed at (a dev build, a newer local install) is never downgraded.
20
25
  */
21
26
  import { compareVersions, isPrerelease, isValidVersion } from "./semver.js";
22
27
  export const UPDATE_CHANNELS = ["latest", "next"];
@@ -115,6 +120,24 @@ export function pickTarget(tags, channel) {
115
120
  return next;
116
121
  return compareVersions(next, stable) >= 0 ? next : stable;
117
122
  }
123
+ /**
124
+ * The channel's tag was moved back off the running build: on the last check it
125
+ * pointed at `current`, and now it points at an older version.
126
+ */
127
+ export function tagMovedBack(previous, channel, current, target) {
128
+ return (previous !== undefined &&
129
+ previous.channel === channel &&
130
+ previous.version === current &&
131
+ target !== undefined &&
132
+ compareVersions(target, current) < 0);
133
+ }
134
+ /** `state.available` is something to act on: newer, or the tag moved back to it. */
135
+ function actionable(state, current) {
136
+ if (!state.available || !isValidVersion(state.available))
137
+ return false;
138
+ const cmp = compareVersions(state.available, current);
139
+ return cmp > 0 || (state.movedBack === true && cmp < 0);
140
+ }
118
141
  /**
119
142
  * Longest a worker lives: it waits for its parent `mnemom` (a whole agent session)
120
143
  * up to this long. A recorded worker older than this is treated as gone, so a
@@ -128,9 +151,7 @@ export const WORKER_MAX_AGE_MS = 12 * HOUR;
128
151
  * scheduled check.
129
152
  */
130
153
  export function shouldResume(state, current, now, alive) {
131
- if (!state.available || !isValidVersion(state.available))
132
- return false;
133
- if (compareVersions(state.available, current) <= 0)
154
+ if (!actionable(state, current))
134
155
  return false;
135
156
  if (state.installBlocked || !canSelfUpdate(state.method ?? "unknown"))
136
157
  return false;
@@ -160,10 +181,11 @@ export function canSelfUpdate(method) {
160
181
  /**
161
182
  * The single decision: given where the registry is and how we're installed,
162
183
  * install / notify / do nothing. `force` (explicit `mnemom update --force`)
163
- * permits a downgrade to the channel's target.
184
+ * permits a downgrade to the channel's target; `followMovedTag` (see
185
+ * tagMovedBack) permits one to the version the channel's tag was moved back to.
164
186
  */
165
187
  export function decide(input) {
166
- const { current, target, mode, method, installBlocked, force } = input;
188
+ const { current, target, mode, method, installBlocked, force, followMovedTag } = input;
167
189
  if (mode === "off")
168
190
  return { kind: "noop", reason: "updates are off" };
169
191
  if (!target)
@@ -171,7 +193,7 @@ export function decide(input) {
171
193
  const cmp = compareVersions(target, current);
172
194
  if (cmp === 0)
173
195
  return { kind: "noop", reason: "already on the newest version" };
174
- if (cmp < 0 && !force)
196
+ if (cmp < 0 && !force && !followMovedTag)
175
197
  return { kind: "noop", reason: "current build is newer than the channel" };
176
198
  if (!canSelfUpdate(method)) {
177
199
  return { kind: "notify", version: target, reason: `installed via ${method}` };
@@ -215,9 +237,7 @@ export function startupNotice(input) {
215
237
  patch: { pending: undefined },
216
238
  };
217
239
  }
218
- if (mode === "off" || !state.available || !isValidVersion(state.available))
219
- return null;
220
- if (compareVersions(state.available, current) <= 0)
240
+ if (mode === "off" || !actionable(state, current))
221
241
  return null;
222
242
  // In auto mode with a working self-update, the worker installs it — stay quiet.
223
243
  if (mode === "auto" && canSelfUpdate(method) && !state.installBlocked)
@@ -229,8 +249,11 @@ export function startupNotice(input) {
229
249
  : state.installBlocked
230
250
  ? ` — the global npm prefix isn't writable; run: ${manualUpgradeCommand(method, state.available)} (see https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)`
231
251
  : " — run: mnemom update";
252
+ const what = state.movedBack
253
+ ? `mnemom ${current} was withdrawn; the channel is back on ${state.available}`
254
+ : `mnemom ${state.available} is available (you have ${current})`;
232
255
  return {
233
- message: `mnemom ${state.available} is available (you have ${current})${cmd}`,
256
+ message: `${what}${cmd}`,
234
257
  patch: { lastNotified: now },
235
258
  };
236
259
  }
@@ -46,7 +46,11 @@ export function updateDoctorRow(input) {
46
46
  hint: "it retries automatically with backoff; `mnemom update` retries now",
47
47
  };
48
48
  }
49
- const pending = state.available ? `, ${state.available} available` : "";
49
+ const pending = !state.available
50
+ ? ""
51
+ : state.movedBack
52
+ ? `, withdrawn: the channel is back on ${state.available}`
53
+ : `, ${state.available} available`;
50
54
  return {
51
55
  mark: "ok",
52
56
  text: `auto-update: ${settings.mode} on ${settings.channel} (v${current}${pending})`,
@@ -42,6 +42,8 @@ export interface RunResult {
42
42
  action?: UpdateAction;
43
43
  installed?: string;
44
44
  error?: string;
45
+ /** The channel's tag moved back below the running build (a withdrawn release). */
46
+ movedBack?: boolean;
45
47
  }
46
48
  /**
47
49
  * Resolve the `next` entitlement: a fresh cache wins; otherwise ask the API.
@@ -5,7 +5,7 @@
5
5
  * (maybe) install → persist state. Never throws; failures become state
6
6
  * (backoff) and a result the caller may print.
7
7
  */
8
- import { backoffDelay, decide, effectiveChannel, entitlementFresh, pickTarget, } from "./decide.js";
8
+ import { backoffDelay, decide, effectiveChannel, entitlementFresh, pickTarget, tagMovedBack, } from "./decide.js";
9
9
  import { compareVersions } from "./semver.js";
10
10
  /**
11
11
  * Resolve the `next` entitlement: a fresh cache wins; otherwise ask the API.
@@ -55,6 +55,7 @@ export async function runUpdate(deps, settings, opts = {}) {
55
55
  const channelDowngraded = channel !== settings.channel;
56
56
  const target = pickTarget(tags, channel);
57
57
  const method = await deps.installMethod();
58
+ const movedBack = tagMovedBack(state.channelTarget, channel, deps.current, target);
58
59
  const action = decide({
59
60
  current: deps.current,
60
61
  target,
@@ -63,17 +64,30 @@ export async function runUpdate(deps, settings, opts = {}) {
63
64
  // An explicit `mnemom update` retries even after an EACCES (the user may have fixed it).
64
65
  installBlocked: opts.foreground ? false : state.installBlocked,
65
66
  force: opts.force,
67
+ followMovedTag: movedBack,
66
68
  });
67
69
  const newer = target !== undefined && compareVersions(target, deps.current) > 0;
68
70
  const base = {
69
71
  lastCheck: now,
70
72
  method,
71
- available: newer ? target : undefined,
73
+ available: newer || movedBack ? target : undefined,
74
+ movedBack: movedBack || undefined,
75
+ // A moved-back tag keeps the old record until it is followed, so a deferred
76
+ // or notify-only pass doesn't forget the move on the next check; an empty
77
+ // answer from the registry keeps it too.
78
+ ...(movedBack || target === undefined ? {} : { channelTarget: { channel, version: target } }),
72
79
  failures: undefined,
73
80
  backoffUntil: undefined,
74
81
  lastError: undefined,
75
82
  };
76
- const result = { channel, channelDowngraded, method, target, action };
83
+ const result = {
84
+ channel,
85
+ channelDowngraded,
86
+ method,
87
+ target,
88
+ action,
89
+ ...(movedBack ? { movedBack: true } : {}),
90
+ };
77
91
  if (action.kind !== "install") {
78
92
  deps.saveState({ ...base, ...done });
79
93
  return result;
@@ -94,9 +108,11 @@ export async function runUpdate(deps, settings, opts = {}) {
94
108
  deps.saveState({
95
109
  ...base,
96
110
  available: undefined,
111
+ movedBack: undefined,
97
112
  installBlocked: undefined,
98
113
  pending: { from: deps.current, to: action.version, channel },
99
114
  followChannel: channel,
115
+ channelTarget: { channel, version: action.version },
100
116
  ...done,
101
117
  });
102
118
  return { ...result, installed: action.version };
@@ -106,11 +122,9 @@ export async function runUpdate(deps, settings, opts = {}) {
106
122
  deps.saveState({ ...base, installBlocked: true, lastError: outcome.message, ...done });
107
123
  }
108
124
  else {
109
- deps.saveState({
110
- ...failurePatch(state, now, outcome.message),
111
- available: base.available,
112
- ...done,
113
- });
125
+ // Keep the moved-tag record: startup reads `movedBack` to notify and resume,
126
+ // and a moved-back tag must stay remembered until it is followed.
127
+ deps.saveState({ ...base, ...failurePatch(state, now, outcome.message), ...done });
114
128
  }
115
129
  return { ...result, error: outcome.message };
116
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/mnemom",
3
- "version": "0.17.31",
3
+ "version": "0.17.32-next.1",
4
4
  "description": "Transparent AI agent tracing",
5
5
  "type": "module",
6
6
  "bin": {