@inerrata-corporation/errata 2.0.0-dev.33 → 2.0.0-dev.35

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 (2) hide show
  1. package/errata.mjs +140 -60
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -20057,6 +20057,13 @@ function renderSnapshot(s) {
20057
20057
  lines.push(`- **Goals:** ${s.profile.goals.join("; ")}`);
20058
20058
  }
20059
20059
  lines.push("");
20060
+ if (s.pendingUpdate) {
20061
+ lines.push("### \u26A1 errata update available");
20062
+ lines.push(
20063
+ `\`${s.pendingUpdate.current}\` \u2192 \`${s.pendingUpdate.latest}\` on the \`${s.pendingUpdate.channel}\` channel. Tell your user \u2014 or run \`errata update\` yourself if they've asked you to keep errata current. It restarts the daemon; that's safe mid-session (hooks fail open, MCP reconnects).`
20064
+ );
20065
+ lines.push("");
20066
+ }
20060
20067
  if (s.needsRevisit.length > 0) {
20061
20068
  lines.push("### \u26A0\uFE0F Needs revisit \u2014 a fact these rested on changed");
20062
20069
  lines.push(
@@ -20227,6 +20234,7 @@ function assembleAgentContext(opts) {
20227
20234
  now: opts.now
20228
20235
  });
20229
20236
  if (opts.edgeElicitation) snapshot.edgeElicitation = opts.edgeElicitation;
20237
+ if (opts.pendingUpdate) snapshot.pendingUpdate = opts.pendingUpdate;
20230
20238
  if (opts.remote && opts.remote.length > 0) {
20231
20239
  const seen = /* @__PURE__ */ new Set();
20232
20240
  const remote = opts.remote.filter((n) => {
@@ -41878,7 +41886,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
41878
41886
  }
41879
41887
 
41880
41888
  // src/engine.ts
41881
- var DAEMON_VERSION = true ? "2.0.0-dev.33" : "2.0.0-alpha.0";
41889
+ var DAEMON_VERSION = true ? "2.0.0-dev.35" : "2.0.0-alpha.0";
41882
41890
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
41883
41891
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
41884
41892
  var GIT_OP_MUTE_MS = 4e3;
@@ -42191,12 +42199,14 @@ function createWorkspaceEngine(opts) {
42191
42199
  };
42192
42200
  const prebuilt = passWorker ? await passWorker.snapshot(snapOpts).catch(() => null) : null;
42193
42201
  const doneRender = prebuilt ? null : markPass(`context-render-inline:${profile.name}`);
42202
+ const pendingUpdate = opts.pendingUpdate?.() ?? null;
42194
42203
  const { body: body2, snapshot } = assembleAgentContext({
42195
42204
  store,
42196
42205
  ...snapOpts,
42197
42206
  remote: remotePriors,
42198
42207
  ...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {},
42199
- ...prebuilt ? { snapshot: prebuilt } : {}
42208
+ ...prebuilt ? { snapshot: prebuilt } : {},
42209
+ ...pendingUpdate ? { pendingUpdate } : {}
42200
42210
  });
42201
42211
  doneRender?.();
42202
42212
  const target = join19(opts.workspaceRoot, "AGENTS.md");
@@ -42864,6 +42874,10 @@ function createWorkspaceEngine(opts) {
42864
42874
  emitEvent(ev) {
42865
42875
  log.append(ev);
42866
42876
  },
42877
+ markContextDirty() {
42878
+ contextDirty = true;
42879
+ refreshContextNow();
42880
+ },
42867
42881
  async tick() {
42868
42882
  maybeRefreshRemotePriors();
42869
42883
  const report = {
@@ -43792,6 +43806,90 @@ function isDaemonAlive(lockPath) {
43792
43806
 
43793
43807
  // src/multi.ts
43794
43808
  init_config();
43809
+
43810
+ // src/update.ts
43811
+ import { spawn as spawn2 } from "node:child_process";
43812
+ var PACKAGE_NAME = "@inerrata-corporation/errata";
43813
+ async function latestPublished(channel, timeoutMs = 4e3) {
43814
+ const ctrl = new AbortController();
43815
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
43816
+ try {
43817
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`, {
43818
+ headers: { accept: "application/vnd.npm.install-v1+json" },
43819
+ signal: ctrl.signal
43820
+ });
43821
+ if (!res.ok) return null;
43822
+ const body2 = await res.json();
43823
+ return body2["dist-tags"]?.[channel] ?? null;
43824
+ } catch {
43825
+ return null;
43826
+ } finally {
43827
+ clearTimeout(timer);
43828
+ }
43829
+ }
43830
+ async function checkForUpdate(channel) {
43831
+ const latest = await latestPublished(channel);
43832
+ return {
43833
+ current: DAEMON_VERSION,
43834
+ latest,
43835
+ updateAvailable: latest !== null && latest !== DAEMON_VERSION
43836
+ };
43837
+ }
43838
+ var POLL_INTERVAL_MS = {
43839
+ dev: 6 * 60 * 6e4,
43840
+ latest: 24 * 60 * 6e4
43841
+ };
43842
+ function startUpdatePoller(opts) {
43843
+ const check2 = opts.check ?? (() => checkForUpdate(opts.channel));
43844
+ const base = opts.intervalMs ?? POLL_INTERVAL_MS[opts.channel];
43845
+ let pending = null;
43846
+ let inFlight = false;
43847
+ let timer = null;
43848
+ let stopped = false;
43849
+ const runOnce = async () => {
43850
+ if (inFlight) return;
43851
+ inFlight = true;
43852
+ try {
43853
+ const status = await check2();
43854
+ if (status.latest === null) return;
43855
+ const next = status.updateAvailable ? { current: status.current, latest: status.latest, channel: opts.channel } : null;
43856
+ const changed = (next?.latest ?? null) !== (pending?.latest ?? null);
43857
+ pending = next;
43858
+ if (changed) opts.onChange?.(pending);
43859
+ } catch {
43860
+ } finally {
43861
+ inFlight = false;
43862
+ }
43863
+ };
43864
+ const schedule = () => {
43865
+ if (stopped) return;
43866
+ const jitter = 1 + (Math.random() - 0.5) * 0.2;
43867
+ timer = setTimeout(() => {
43868
+ void runOnce().finally(schedule);
43869
+ }, base * jitter);
43870
+ timer.unref?.();
43871
+ };
43872
+ void runOnce().finally(schedule);
43873
+ return {
43874
+ current: () => pending,
43875
+ stop: () => {
43876
+ stopped = true;
43877
+ if (timer) clearTimeout(timer);
43878
+ }
43879
+ };
43880
+ }
43881
+ function runNpmInstall(channel) {
43882
+ return new Promise((resolve5) => {
43883
+ const child = spawn2("npm", ["install", "-g", `${PACKAGE_NAME}@${channel}`], {
43884
+ stdio: "inherit",
43885
+ shell: true
43886
+ });
43887
+ child.on("close", (code) => resolve5(code ?? 1));
43888
+ child.on("error", () => resolve5(1));
43889
+ });
43890
+ }
43891
+
43892
+ // src/multi.ts
43795
43893
  init_cloud_endpoint_policy();
43796
43894
  init_cloud_auth();
43797
43895
  function normPath(p) {
@@ -43858,6 +43956,15 @@ async function startMultiDaemon(opts = {}) {
43858
43956
  startLoopLagMonitor();
43859
43957
  let baseUrl = "";
43860
43958
  const records = [];
43959
+ const updatePoller = opts.updateCheck ? startUpdatePoller({
43960
+ channel: loadConfig().updateChannel,
43961
+ onChange: (pending) => {
43962
+ if (pending) {
43963
+ console.log(`\u26A1 errata update available: ${pending.current} \u2192 ${pending.latest} \u2014 run: errata update`);
43964
+ }
43965
+ for (const r of records) r.engine.markContextDirty();
43966
+ }
43967
+ }) : null;
43861
43968
  const boundaryListeners = [];
43862
43969
  let boundaryFlushing = false;
43863
43970
  const fireBoundary = () => {
@@ -43876,6 +43983,7 @@ async function startMultiDaemon(opts = {}) {
43876
43983
  apiKey: opts.apiKey,
43877
43984
  reviewUrl: () => `${baseUrl}/ws/${id}/review`,
43878
43985
  sharedStore,
43986
+ pendingUpdate: () => updatePoller?.current() ?? null,
43879
43987
  ...opts.skipWatchers ? { skipWatchers: true } : {}
43880
43988
  });
43881
43989
  const webApp = buildWebUi({
@@ -43965,6 +44073,7 @@ async function startMultiDaemon(opts = {}) {
43965
44073
  "/",
43966
44074
  (c) => c.json({
43967
44075
  daemonVersion: DAEMON_VERSION,
44076
+ pendingUpdate: updatePoller?.current() ?? null,
43968
44077
  projects: records.map((r) => ({
43969
44078
  id: r.id,
43970
44079
  name: r.entry.name,
@@ -44427,6 +44536,7 @@ async function startMultiDaemon(opts = {}) {
44427
44536
  await new Promise(
44428
44537
  (res) => server.close(res)
44429
44538
  );
44539
+ updatePoller?.stop();
44430
44540
  for (const r of records) await r.engine.stop();
44431
44541
  if (consolidateWorker) await consolidateWorker.stop();
44432
44542
  try {
@@ -44584,45 +44694,6 @@ async function loginOAuthLoopback(opts) {
44584
44694
  return { tokens, tokenEndpoint: endpoints.tokenEndpoint, clientId };
44585
44695
  }
44586
44696
 
44587
- // src/update.ts
44588
- import { spawn as spawn2 } from "node:child_process";
44589
- var PACKAGE_NAME = "@inerrata-corporation/errata";
44590
- async function latestPublished(channel, timeoutMs = 4e3) {
44591
- const ctrl = new AbortController();
44592
- const timer = setTimeout(() => ctrl.abort(), timeoutMs);
44593
- try {
44594
- const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`, {
44595
- headers: { accept: "application/vnd.npm.install-v1+json" },
44596
- signal: ctrl.signal
44597
- });
44598
- if (!res.ok) return null;
44599
- const body2 = await res.json();
44600
- return body2["dist-tags"]?.[channel] ?? null;
44601
- } catch {
44602
- return null;
44603
- } finally {
44604
- clearTimeout(timer);
44605
- }
44606
- }
44607
- async function checkForUpdate(channel) {
44608
- const latest = await latestPublished(channel);
44609
- return {
44610
- current: DAEMON_VERSION,
44611
- latest,
44612
- updateAvailable: latest !== null && latest !== DAEMON_VERSION
44613
- };
44614
- }
44615
- function runNpmInstall(channel) {
44616
- return new Promise((resolve5) => {
44617
- const child = spawn2("npm", ["install", "-g", `${PACKAGE_NAME}@${channel}`], {
44618
- stdio: "inherit",
44619
- shell: true
44620
- });
44621
- child.on("close", (code) => resolve5(code ?? 1));
44622
- child.on("error", () => resolve5(1));
44623
- });
44624
- }
44625
-
44626
44697
  // src/cli.ts
44627
44698
  init_paths();
44628
44699
 
@@ -44673,6 +44744,15 @@ function parseBurstArgs(args2) {
44673
44744
  };
44674
44745
  }
44675
44746
 
44747
+ // src/login-mode.ts
44748
+ var OAUTH_DEFAULT_ENV = "ERRATA_DAEMON_OAUTH_DEFAULT";
44749
+ function isDaemonOAuthDefaultEnabled(env2 = process.env) {
44750
+ return env2[OAUTH_DEFAULT_ENV] === "1";
44751
+ }
44752
+ function shouldUseOAuthLogin(flags2, env2 = process.env) {
44753
+ return Boolean(flags2.oauth || !flags2.device && isDaemonOAuthDefaultEnabled(env2));
44754
+ }
44755
+
44676
44756
  // src/consolidation-trigger.ts
44677
44757
  var DEFAULT_CONSOLIDATION_POLICY = {
44678
44758
  baseFloorMs: 6e4,
@@ -44849,7 +44929,7 @@ Commands:
44849
44929
  Flags: --port N (default 7891)
44850
44930
  mcp Run the MCP stdio server \u2014 the agent's full errata tool
44851
44931
  surface (navigation, problems, claims, burst, health)
44852
- login Sign in with the cloud (OAuth by default; --device for legacy)
44932
+ login Sign in with the cloud (device-code by default; --oauth for OAuth)
44853
44933
  logout Clear local cloud credentials
44854
44934
  sync now Flush outbox to the cloud once
44855
44935
  privacy Show what is collected/scrubbed + your consent state
@@ -44864,6 +44944,7 @@ Commands:
44864
44944
  Environment:
44865
44945
  ERRATA_CLOUD_URL Cloud base URL (default ${DEFAULT_CLOUD_URL})
44866
44946
  ERRATA_ALLOW_DIRECT_V1_CLOUD=1 Allow non-local legacy v1 cloud testing
44947
+ ${OAUTH_DEFAULT_ENV}=1 Internal dogfood: plain \`errata login\` uses OAuth
44867
44948
  `);
44868
44949
  }
44869
44950
  function resolveHookPort(explicit) {
@@ -45215,20 +45296,24 @@ async function cmdLoginOAuth(cfg) {
45215
45296
  process.exitCode = 1;
45216
45297
  return;
45217
45298
  }
45218
- cfg.accessToken = result.tokens.accessToken;
45219
- cfg.refreshToken = result.tokens.refreshToken ?? null;
45220
- cfg.tokenEndpoint = result.tokenEndpoint;
45221
- cfg.oauthClientId = result.clientId;
45222
- cfg.apiKey = null;
45223
- saveConfig(cfg);
45299
+ const nextCfg = {
45300
+ ...cfg,
45301
+ accessToken: result.tokens.accessToken,
45302
+ refreshToken: result.tokens.refreshToken ?? null,
45303
+ tokenEndpoint: result.tokenEndpoint,
45304
+ oauthClientId: result.clientId,
45305
+ apiKey: null
45306
+ };
45224
45307
  try {
45225
- const me = await authedCloudClient(cfg).me();
45226
- cfg.userId = me.agentId;
45227
- cfg.email = me.handle;
45228
- saveConfig(cfg);
45308
+ const me = await authedCloudClient(nextCfg).me();
45309
+ nextCfg.userId = me.agentId;
45310
+ nextCfg.email = me.handle;
45311
+ saveConfig(nextCfg);
45229
45312
  console.log(`logged in as ${me.handle} (${me.tier}) \u2014 tokens stored at ${globalConfigPath()}`);
45230
45313
  } catch (err2) {
45231
- console.warn(`signed in, but identity check failed: ${err2 instanceof Error ? err2.message : err2}`);
45314
+ console.error(`signed in, but identity check failed: ${err2 instanceof Error ? err2.message : err2}`);
45315
+ console.error(` credentials were not stored; try again or paste a key: errata login --token <key>`);
45316
+ process.exitCode = 1;
45232
45317
  }
45233
45318
  }
45234
45319
  async function cmdLogin() {
@@ -45239,7 +45324,7 @@ async function cmdLogin() {
45239
45324
  await applyToken(cfg, flags2.token);
45240
45325
  return;
45241
45326
  }
45242
- if (flags2.oauth || !flags2.device) {
45327
+ if (shouldUseOAuthLogin(flags2)) {
45243
45328
  await cmdLoginOAuth(cfg);
45244
45329
  return;
45245
45330
  }
@@ -46395,7 +46480,7 @@ async function cmdDash(args2) {
46395
46480
  const reindexOnStart = !args2.includes("--no-reindex");
46396
46481
  const skipEmbed = !args2.includes("--embed");
46397
46482
  const skipWatchers = args2.includes("--no-watch");
46398
- const handle2 = await startMultiDaemon({ webPort: port, reindexOnStart, skipEmbed, skipWatchers });
46483
+ const handle2 = await startMultiDaemon({ webPort: port, reindexOnStart, skipEmbed, skipWatchers, updateCheck: true });
46399
46484
  console.log(`errata multi-daemon running`);
46400
46485
  console.log(` endpoint: ${handle2.url} (JSON \u2014 the human view is \`errata report\`)`);
46401
46486
  console.log(` projects: ${handle2.records.length}`);
@@ -46408,11 +46493,6 @@ async function cmdDash(args2) {
46408
46493
  console.log(` indexing all projects in the background${skipEmbed ? " (--no-embed)" : ""}\u2026`);
46409
46494
  }
46410
46495
  console.log(`Press Ctrl-C to stop.`);
46411
- void checkForUpdate(loadConfig().updateChannel).then((u) => {
46412
- if (u.updateAvailable) {
46413
- console.log(`\u26A1 errata update available: ${u.current} \u2192 ${u.latest} \u2014 run: errata update`);
46414
- }
46415
- });
46416
46496
  const notifyEnabled = loadConfig().notifications && !process.env["ERRATA_NO_NOTIFY"];
46417
46497
  const QUIESCENCE_MS = 9e4;
46418
46498
  let quiesceTimer = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.33",
3
+ "version": "2.0.0-dev.35",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {