getprismo 0.1.55 → 0.1.57

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.
@@ -575,17 +575,24 @@ module.exports = function createAgent(deps) {
575
575
  try {
576
576
  await sendHeartbeat(config, { mode, status: "online", lastPollAt: pollTime }, options);
577
577
  } catch (_) {}
578
- await sendLiveEvent(config, {
579
- eventId: `heartbeat-${repo.pathBasename}-${pollTime.slice(0, 16)}`,
580
- phase: "watching",
581
- eventType: "heartbeat",
582
- headline: "Connector is watching this repo",
583
- detail: `Mode: ${mode}. Polling for safe repairs and syncing telemetry.`,
584
- repo,
585
- }, options);
586
- await publishClaudeLoopStops(config, rootDir, repo, options);
587
- await publishClaudeContextBlocks(config, rootDir, repo, options);
588
- await publishAgentLoopSignals(config, rootDir, repo, options);
578
+
579
+ // Presence heartbeat above runs every poll (cheap upsert). The live-event
580
+ // feed and loop/context publishing only need the slower sync cadence, so
581
+ // gate them on syncTelemetry to avoid a burst of small writes every poll.
582
+ const publishLive = options.syncTelemetry !== false;
583
+ if (publishLive) {
584
+ await sendLiveEvent(config, {
585
+ eventId: `heartbeat-${repo.pathBasename}-${pollTime.slice(0, 16)}`,
586
+ phase: "watching",
587
+ eventType: "heartbeat",
588
+ headline: "Connector is watching this repo",
589
+ detail: `Mode: ${mode}. Polling for safe repairs and syncing telemetry.`,
590
+ repo,
591
+ }, options);
592
+ await publishClaudeLoopStops(config, rootDir, repo, options);
593
+ await publishClaudeContextBlocks(config, rootDir, repo, options);
594
+ await publishAgentLoopSignals(config, rootDir, repo, options);
595
+ }
589
596
 
590
597
  let autoDetectResult = null;
591
598
  if (options.autoDetect) {
@@ -905,9 +912,9 @@ module.exports = function createAgent(deps) {
905
912
  async function runAgent(rootDir = process.cwd(), options = {}) {
906
913
  if (!options.watch) return runAgentOnce(rootDir, options);
907
914
 
908
- const intervalMs = Math.max(5, Number(options.interval || 15)) * 1000;
909
- const syncIntervalMs = Math.max(30, Number(options.syncInterval || 60)) * 1000;
910
- const detectIntervalMs = Math.max(60, Number(options.detectInterval || 300)) * 1000;
915
+ const intervalMs = Math.max(15, Number(options.interval || 60)) * 1000;
916
+ const syncIntervalMs = Math.max(60, Number(options.syncInterval || 300)) * 1000;
917
+ const detectIntervalMs = Math.max(300, Number(options.detectInterval || 900)) * 1000;
911
918
  const plannerIntervalMs = Math.max(60, Number(options.plannerInterval || 600)) * 1000;
912
919
  let running = true;
913
920
  let sleepResolve = null;
@@ -357,8 +357,8 @@ function createCli(deps) {
357
357
  });
358
358
  if (result.connected && !rest.includes("--no-agent") && runConnectorInstall) {
359
359
  result.connector = runConnectorInstall(target, {
360
- interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 15),
361
- syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 60),
360
+ interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 60),
361
+ syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 300),
362
362
  mode: modeValue,
363
363
  dryRun: rest.includes("--dry-run"),
364
364
  });
@@ -389,8 +389,8 @@ function createCli(deps) {
389
389
  let result;
390
390
  if (action === "install") {
391
391
  result = runConnectorInstall(target, {
392
- interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 15),
393
- syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 60),
392
+ interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 60),
393
+ syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 300),
394
394
  mode: modeValue,
395
395
  dryRun: rest.includes("--dry-run"),
396
396
  });
@@ -462,9 +462,9 @@ function createCli(deps) {
462
462
  autoDetect: !rest.includes("--no-detect"),
463
463
  planRepairs: !rest.includes("--no-planner"),
464
464
  noSync: rest.includes("--no-sync"),
465
- interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 15),
466
- syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 60),
467
- detectInterval: parsePositiveInt(detectIntervalIndex >= 0 ? rest[detectIntervalIndex + 1] : null, 300),
465
+ interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 60),
466
+ syncInterval: parsePositiveInt(syncIntervalIndex >= 0 ? rest[syncIntervalIndex + 1] : null, 300),
467
+ detectInterval: parsePositiveInt(detectIntervalIndex >= 0 ? rest[detectIntervalIndex + 1] : null, 900),
468
468
  plannerInterval: parsePositiveInt(plannerIntervalIndex >= 0 ? rest[plannerIntervalIndex + 1] : null, 600),
469
469
  limit: parsePositiveInt(limitIndex >= 0 ? rest[limitIndex + 1] : null, 5),
470
470
  syncLimit: parsePositiveInt(limitIndex >= 0 ? rest[limitIndex + 1] : null, 20),
@@ -378,6 +378,13 @@ module.exports = function createCloudSync(deps) {
378
378
  };
379
379
  }
380
380
 
381
+ function sessionsFingerprint(sessions) {
382
+ const parts = (sessions || [])
383
+ .map((s) => `${s.sessionId || ""}:${s.updatedAt || ""}:${s.tokens?.display || 0}:${s.waste?.wastedTokens || 0}`)
384
+ .sort();
385
+ return `${parts.length}|${parts.join("|")}`;
386
+ }
387
+
381
388
  async function runSync(rootDir = process.cwd(), options = {}) {
382
389
  const config = loadConfig();
383
390
  const payload = buildSyncPayload(rootDir, {
@@ -408,16 +415,29 @@ module.exports = function createCloudSync(deps) {
408
415
  };
409
416
  }
410
417
  const endpoint = options.endpoint || `${String(config.apiUrl || DEFAULT_API_URL).replace(/\/$/, "")}/v1/dev/sessions/sync`;
418
+
419
+ // Skip the upload when nothing changed since the last sync. The connector
420
+ // re-runs on a short interval, but session data only changes when the user
421
+ // is actually coding, so most syncs are no-ops; sending them anyway is the
422
+ // main driver of needless backend load and network egress.
423
+ const fingerprint = sessionsFingerprint(payload.sessions);
424
+ if (!options.force) {
425
+ const previous = readJson(statePath());
426
+ if (previous && previous.fingerprint === fingerprint) {
427
+ return { schemaVersion: 1, command: "sync", synced: true, skipped: true, reason: "unchanged", statePath: statePath(), aggregate: payload.aggregate };
428
+ }
429
+ }
430
+
411
431
  const response = await requestJson("POST", endpoint, config.token, payload, options.timeoutMs || 8000);
412
- const state = {
432
+ writeJson(statePath(), {
413
433
  schemaVersion: 1,
414
434
  lastSyncAt: new Date().toISOString(),
415
435
  endpoint,
436
+ fingerprint,
416
437
  repo: payload.repo,
417
438
  aggregate: payload.aggregate,
418
439
  response: response.data,
419
- };
420
- writeJson(statePath(), state);
440
+ });
421
441
  return {
422
442
  schemaVersion: 1,
423
443
  command: "sync",
@@ -78,9 +78,12 @@ module.exports = function createConnector(deps) {
78
78
 
79
79
  function writeRunner(rootDir, options = {}) {
80
80
  const root = path.resolve(rootDir || process.cwd());
81
- const interval = Math.max(5, Number(options.interval || 15));
82
- const syncInterval = Math.max(30, Number(options.syncInterval || 60));
83
- const detectInterval = Math.max(60, Number(options.detectInterval || 300));
81
+ // Background poll cadence. Kept conservative so an always-on connector
82
+ // stays light on API/database traffic; agent sessions change on the order
83
+ // of minutes, not seconds, so fast polling only adds idle load.
84
+ const interval = Math.max(15, Number(options.interval || 60));
85
+ const syncInterval = Math.max(60, Number(options.syncInterval || 300));
86
+ const detectInterval = Math.max(300, Number(options.detectInterval || 900));
84
87
  const mode = options.mode || "autopilot";
85
88
  fs.mkdirSync(connectorDir(), { recursive: true });
86
89
  const command = `${BACKGROUND_COMMAND} agent --watch --interval ${interval} --sync-interval ${syncInterval} --detect-interval ${detectInterval} --mode ${shellEscape(mode)} ${shellEscape(root)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getprismo",
3
- "version": "0.1.55",
3
+ "version": "0.1.57",
4
4
  "description": "Local AI coding workflow scanner for Codex, Claude Code, Cursor, and token-waste diagnostics.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/shanirsh/prismodev#readme",