@primitive.ai/prim 0.1.0-alpha.91 → 0.1.0-alpha.93

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 (40) hide show
  1. package/README.md +34 -2
  2. package/dist/{chunk-67VNLLBN.js → chunk-2GP5UFV7.js} +1 -0
  3. package/dist/{chunk-VW3FDYRT.js → chunk-36TWVNBE.js} +6 -6
  4. package/dist/{chunk-HU4PW6I6.js → chunk-C3VAA5CG.js} +1 -1
  5. package/dist/{chunk-FOLKHXLJ.js → chunk-DCJN6LZH.js} +37 -5
  6. package/dist/{chunk-M6RIJVRE.js → chunk-DSWT6BCC.js} +78 -4
  7. package/dist/{chunk-FBAYCS3X.js → chunk-DXJQR4QI.js} +150 -38
  8. package/dist/{chunk-PCVGBE5B.js → chunk-G2VMA2QO.js} +2 -1
  9. package/dist/{chunk-NAHHFSLH.js → chunk-ICM2XCXS.js} +2 -2
  10. package/dist/{chunk-OAAP2MY6.js → chunk-K7D2EYBS.js} +2 -2
  11. package/dist/{chunk-5AFRMOPX.js → chunk-OH3EZXYX.js} +1 -1
  12. package/dist/{chunk-KFXF2V5G.js → chunk-OTHKG76J.js} +1 -1
  13. package/dist/chunk-OYHYCK4K.js +13 -0
  14. package/dist/{chunk-FKXFUUWJ.js → chunk-TYAHC25F.js} +1 -1
  15. package/dist/{chunk-5GYQ4J3D.js → chunk-UKUAAAXE.js} +2 -2
  16. package/dist/{chunk-EWZMDD44.js → chunk-X64HTHUF.js} +22 -3
  17. package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
  18. package/dist/daemon/server.js +382 -124
  19. package/dist/hook-runtime/daemon/server.js +382 -124
  20. package/dist/hook-runtime/hooks/post-commit.js +57 -39
  21. package/dist/hook-runtime/hooks/post-rewrite.js +58 -40
  22. package/dist/hook-runtime/hooks/post-tool-use.js +111 -78
  23. package/dist/hook-runtime/hooks/pre-tool-use.js +48 -26
  24. package/dist/hook-runtime/hooks/prim-hook.js +157 -117
  25. package/dist/hook-runtime/hooks/session-end.js +1 -1
  26. package/dist/hook-runtime/hooks/session-start.js +38 -4
  27. package/dist/hook-runtime/index.js +1293 -526
  28. package/dist/hook-runtime/statusline-main.js +23 -1
  29. package/dist/hooks/post-commit.js +11 -8
  30. package/dist/hooks/post-rewrite.js +11 -8
  31. package/dist/hooks/post-tool-use.js +13 -15
  32. package/dist/hooks/pre-commit.js +4 -4
  33. package/dist/hooks/pre-tool-use.js +10 -12
  34. package/dist/hooks/prim-hook.js +17 -14
  35. package/dist/hooks/session-end.js +4 -4
  36. package/dist/hooks/session-start.js +12 -36
  37. package/dist/index.js +867 -291
  38. package/dist/statusline-main.js +23 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-BTNNEH2W.js +0 -79
@@ -3,7 +3,7 @@ import {
3
3
  isTerminalSafeText,
4
4
  terminalSafeLine,
5
5
  terminalSafeText
6
- } from "./chunk-PCVGBE5B.js";
6
+ } from "./chunk-G2VMA2QO.js";
7
7
 
8
8
  // src/decisions/feedback.ts
9
9
  var FEEDBACK_PROTOCOL_VERSION = 2;
@@ -2,7 +2,7 @@ import {
2
2
  decodeJwtPayload,
3
3
  primConfigDirectory,
4
4
  resolveAuthCredential
5
- } from "./chunk-67VNLLBN.js";
5
+ } from "./chunk-2GP5UFV7.js";
6
6
 
7
7
  // src/daemon/client.ts
8
8
  import { createConnection } from "net";
@@ -0,0 +1,13 @@
1
+ // src/lib/unattended.ts
2
+ var UNATTENDED_ENV = "PRIM_UNATTENDED";
3
+ function unattendedEnv(env = process.env) {
4
+ return { ...env, [UNATTENDED_ENV]: "1" };
5
+ }
6
+ function isUnattended(env = process.env) {
7
+ return env[UNATTENDED_ENV] === "1";
8
+ }
9
+
10
+ export {
11
+ unattendedEnv,
12
+ isUnattended
13
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scheduleCollectScopeRefresh
3
- } from "./chunk-M6RIJVRE.js";
3
+ } from "./chunk-DSWT6BCC.js";
4
4
 
5
5
  // src/ingest-response.ts
6
6
  var IngestAcknowledgementError = class extends Error {
@@ -2,10 +2,10 @@ import {
2
2
  getClient,
3
3
  getSiteUrl,
4
4
  terminalSafeLine
5
- } from "./chunk-PCVGBE5B.js";
5
+ } from "./chunk-G2VMA2QO.js";
6
6
  import {
7
7
  daemonRequest
8
- } from "./chunk-KFXF2V5G.js";
8
+ } from "./chunk-OTHKG76J.js";
9
9
 
10
10
  // src/daemon/proxy.ts
11
11
  var DAEMON_HTTP_TIMEOUT_MS = 1e4;
@@ -7,7 +7,7 @@ import {
7
7
  syncDirectory,
8
8
  syncFile,
9
9
  withFileLockSync
10
- } from "./chunk-67VNLLBN.js";
10
+ } from "./chunk-2GP5UFV7.js";
11
11
 
12
12
  // src/lib/bin-path.ts
13
13
  import { existsSync, readFileSync } from "fs";
@@ -219,7 +219,7 @@ import {
219
219
  utimesSync,
220
220
  writeFileSync
221
221
  } from "fs";
222
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve } from "path";
222
+ import { delimiter, dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve } from "path";
223
223
  var HOOK_RUNTIME_SCHEMA_VERSION = 1;
224
224
  var DIRECTORY_MODE = 448;
225
225
  var DATA_FILE_MODE = 384;
@@ -569,11 +569,28 @@ function writeRelease(sourceDir, releaseDir, manifest, releasesDir) {
569
569
  rmSync(stagingDir, { recursive: true, force: true });
570
570
  }
571
571
  }
572
+ function stableNodePath(execPath = process.execPath, searchPath = process.env.PATH ?? "") {
573
+ let target;
574
+ try {
575
+ target = realpathSync(execPath);
576
+ } catch {
577
+ return execPath;
578
+ }
579
+ for (const directory of searchPath.split(delimiter)) {
580
+ if (!isAbsolute2(directory)) continue;
581
+ const candidate = join2(directory, "node");
582
+ try {
583
+ if (realpathSync(candidate) === target) return candidate;
584
+ } catch {
585
+ }
586
+ }
587
+ return execPath;
588
+ }
572
589
  function stageHookRuntime(options = {}) {
573
590
  const root = packageRoot();
574
591
  const sourceDir = options.sourceDir ?? (root ? join2(root, "dist", "hook-runtime") : null);
575
592
  const version = options.version ?? packageVersion();
576
- const nodePath = resolve(options.nodePath ?? process.execPath);
593
+ const nodePath = resolve(options.nodePath ?? stableNodePath());
577
594
  if (!sourceDir || !version || compareSemver(version, version) !== 0 || !isAbsolute2(nodePath) || /[\r\n]/u.test(nodePath)) {
578
595
  throw new Error("cannot stage hook runtime: package identity is unavailable");
579
596
  }
@@ -1443,6 +1460,7 @@ export {
1443
1460
  compareSemver,
1444
1461
  binFile,
1445
1462
  packageVersion,
1463
+ packageRoot,
1446
1464
  pinnedNpxArgs,
1447
1465
  pinnedNpxCommand,
1448
1466
  pinnedHookCommand,
@@ -1451,6 +1469,7 @@ export {
1451
1469
  hookCommandResolutions,
1452
1470
  hookRuntimePaths,
1453
1471
  inspectHookRuntime,
1472
+ stableNodePath,
1454
1473
  stageHookRuntime,
1455
1474
  assertOwnedHookRuntime,
1456
1475
  removeHookRuntime,
@@ -1,6 +1,57 @@
1
+ import {
2
+ boundedHealthError
3
+ } from "./chunk-DSWT6BCC.js";
1
4
  import {
2
5
  stripControlChars
3
- } from "./chunk-PCVGBE5B.js";
6
+ } from "./chunk-G2VMA2QO.js";
7
+
8
+ // src/lib/delivery-backlog.ts
9
+ var MS_PER_SECOND = 1e3;
10
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
11
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
12
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
13
+ function isNonNegativeInteger(value) {
14
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
15
+ }
16
+ function isPositiveInteger(value) {
17
+ return isNonNegativeInteger(value) && value > 0;
18
+ }
19
+ function deliveryBacklogState(ingestion) {
20
+ if (ingestion?.healthy !== false) return void 0;
21
+ const failures = ingestion.consecutiveFailures;
22
+ const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
23
+ if (failedWithoutProgress) return "failing";
24
+ const retained = ingestion.lastRetainedBucketCount;
25
+ if (!isNonNegativeInteger(retained)) return "failing";
26
+ return retained > 0 ? "retained" : "draining";
27
+ }
28
+ function formatBacklogAge(ageMs) {
29
+ if (ageMs >= MS_PER_DAY) return `${String(Math.floor(ageMs / MS_PER_DAY))}d`;
30
+ if (ageMs >= MS_PER_HOUR) return `${String(Math.floor(ageMs / MS_PER_HOUR))}h`;
31
+ if (ageMs >= MS_PER_MINUTE) return `${String(Math.floor(ageMs / MS_PER_MINUTE))}m`;
32
+ return `${String(Math.max(0, Math.floor(ageMs / MS_PER_SECOND)))}s`;
33
+ }
34
+ function formatPendingBacklog(backlog, now = Date.now()) {
35
+ const count = backlog.pendingCount;
36
+ const moves = typeof count === "number" && count > 0 ? `${backlog.pendingSampled ? "at least " : ""}${String(count)} pending move${count === 1 ? "" : "s"}` : "an unknown number of pending moves";
37
+ const age = backlog.oldestPendingAt === void 0 ? "" : ` (oldest ${backlog.pendingSampled ? "\u2265 " : ""}${formatBacklogAge(now - backlog.oldestPendingAt)})`;
38
+ return `${moves}${age}`;
39
+ }
40
+ function deliveryBacklogSummary(ingestion, now = Date.now(), backlog = ingestion) {
41
+ const state = deliveryBacklogState(ingestion);
42
+ if (!(state && ingestion && backlog)) return void 0;
43
+ const pending = formatPendingBacklog(backlog, now);
44
+ if (state === "draining") return `draining ${pending} in the background`;
45
+ if (state === "retained") {
46
+ const buckets = ingestion.lastRetainedBucketCount ?? 0;
47
+ const reasons = boundedHealthError(ingestion.lastRetainedReasons);
48
+ return `delivery held back: ${String(buckets)} journal bucket${buckets === 1 ? "" : "s"} retained${reasons ? ` (${reasons})` : ""} \xB7 holding ${pending} \u2014 run \`prim doctor\``;
49
+ }
50
+ const failures = ingestion.consecutiveFailures;
51
+ const count = isPositiveInteger(failures) ? ` (${String(failures)} consecutive failure${failures === 1 ? "" : "s"})` : "";
52
+ const error = boundedHealthError(ingestion.lastError);
53
+ return `delivery failing${count}${error ? `: ${error}` : ""} \xB7 retrying ${pending} in the background`;
54
+ }
4
55
 
5
56
  // src/lib/presence.ts
6
57
  var DECISION_ORIGIN = "https://app.getprimitive.ai";
@@ -76,7 +127,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
76
127
  if (snapshot.ingestion?.healthy === false) {
77
128
  const pending = snapshot.ingestion.pendingCount;
78
129
  const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
79
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
130
+ const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
131
+ return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
80
132
  }
81
133
  if (snapshot.heartbeat?.healthy === false) {
82
134
  return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
@@ -106,6 +158,10 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
106
158
  }
107
159
 
108
160
  export {
161
+ MS_PER_SECOND,
162
+ deliveryBacklogState,
163
+ formatPendingBacklog,
164
+ deliveryBacklogSummary,
109
165
  formatTeammates,
110
166
  repositoryBindingDiagnosticLabel,
111
167
  formatStatusline