@papi-ai/server 0.7.82 → 0.7.83

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.
@@ -8,6 +8,151 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // ../shared/dist/index.js
12
+ function isSelfHostedDeployment(value) {
13
+ return value === "1" || value?.toLowerCase() === "true";
14
+ }
15
+ function optionalBoolean(value, fallback) {
16
+ if (value == null || value.trim() === "") return fallback;
17
+ return isSelfHostedDeployment(value);
18
+ }
19
+ function cleanUrl(value) {
20
+ const cleaned = value?.trim().replace(/\/+$/, "");
21
+ return cleaned || void 0;
22
+ }
23
+ function resolveDeploymentProfile(env) {
24
+ const selfHosted = isSelfHostedDeployment(env.selfHosted);
25
+ const hostedSupabaseUrl = cleanUrl(env.hostedSupabaseUrl);
26
+ return {
27
+ kind: selfHosted ? "self-hosted" : "hosted",
28
+ appUrl: cleanUrl(env.appUrl) ?? (selfHosted ? void 0 : HOSTED_APP_URL),
29
+ mcpUrl: cleanUrl(env.mcpUrl) ?? (selfHosted ? void 0 : HOSTED_MCP_URL),
30
+ dataEndpoint: cleanUrl(env.dataEndpoint) ?? (hostedSupabaseUrl ? `${hostedSupabaseUrl}/functions/v1/data-proxy` : void 0) ?? (selfHosted ? void 0 : `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`),
31
+ providers: {
32
+ vercel: optionalBoolean(env.vercelEnabled, !selfHosted),
33
+ railway: optionalBoolean(env.railwayEnabled, !selfHosted),
34
+ managedSupabase: optionalBoolean(env.managedSupabaseEnabled, !selfHosted)
35
+ },
36
+ auth: {
37
+ emailPassword: optionalBoolean(env.emailPasswordEnabled, true),
38
+ emailSignup: optionalBoolean(env.emailSignupEnabled, false),
39
+ google: optionalBoolean(env.googleAuthEnabled, !selfHosted),
40
+ github: optionalBoolean(env.githubAuthEnabled, !selfHosted)
41
+ }
42
+ };
43
+ }
44
+ function effortOrdinal(effort) {
45
+ if (typeof effort !== "string") return void 0;
46
+ const normalized = effort.trim().toUpperCase();
47
+ return EFFORT_SCALE[normalized];
48
+ }
49
+ function isUnparsedEffort(effort) {
50
+ if (typeof effort !== "string" || effort.trim().length === 0) return false;
51
+ return effortOrdinal(effort) === void 0;
52
+ }
53
+ function calculateCycleMetrics(reports, currentCycle, window = 5) {
54
+ const recentReports = reports.filter(
55
+ (r) => r.cycle > currentCycle - window && r.cycle <= currentCycle
56
+ );
57
+ const unparsedEffortCount = recentReports.filter(
58
+ (r) => isUnparsedEffort(r.actualEffort) || isUnparsedEffort(r.estimatedEffort)
59
+ ).length;
60
+ const perCycle = /* @__PURE__ */ new Map();
61
+ for (const r of recentReports) {
62
+ const group = perCycle.get(r.cycle) ?? [];
63
+ group.push(r);
64
+ perCycle.set(r.cycle, group);
65
+ }
66
+ const accuracy = [];
67
+ const velocity = [];
68
+ const sortedCycles = [...perCycle.keys()].sort((a, b) => a - b);
69
+ for (const cycle of sortedCycles) {
70
+ const reps = perCycle.get(cycle);
71
+ const deltas = [];
72
+ for (const r of reps) {
73
+ const actual = effortOrdinal(r.actualEffort);
74
+ const estimated = effortOrdinal(r.estimatedEffort);
75
+ if (actual !== void 0 && estimated !== void 0) {
76
+ deltas.push(actual - estimated);
77
+ }
78
+ }
79
+ if (deltas.length > 0) {
80
+ accuracy.push({
81
+ cycle,
82
+ reports: deltas.length,
83
+ matchRate: Math.round(deltas.filter((d) => d === 0).length / deltas.length * 100),
84
+ mae: Math.round(deltas.reduce((s, d) => s + Math.abs(d), 0) / deltas.length * 10) / 10,
85
+ bias: Math.round(deltas.reduce((s, d) => s + d, 0) / deltas.length * 10) / 10
86
+ });
87
+ }
88
+ velocity.push({
89
+ cycle,
90
+ completed: reps.filter((r) => r.completed === "Yes").length,
91
+ partial: reps.filter((r) => r.completed === "Partial").length,
92
+ failed: reps.filter((r) => r.completed === "No").length,
93
+ effortPoints: reps.reduce((s, r) => s + (effortOrdinal(r.actualEffort) ?? 0), 0)
94
+ });
95
+ }
96
+ return { accuracy, velocity, unparsedEffortCount };
97
+ }
98
+ var CAPABILITY_REGISTRY, CAPABILITY_KEYS, HOSTED_APP_URL, HOSTED_MCP_URL, HOSTED_SUPABASE_URL, WAB_WINDOW_DAYS, WAB_WEEK_MS, EFFORT_SCALE;
99
+ var init_dist = __esm({
100
+ "../shared/dist/index.js"() {
101
+ "use strict";
102
+ CAPABILITY_REGISTRY = [
103
+ { key: "prReviewer", label: "Auto code review", description: "Runs a code review of the branch diff before accepting.", step: "review" },
104
+ { key: "securityScan", label: "Security scan", description: "Flags a security pass on risk-tier changes at review time.", step: "review" },
105
+ { key: "changelog", label: "Changelog & cycle update", description: "Curates a cycle-update post when a release ships.", step: "release" },
106
+ { key: "verifyHealthCheck", label: "Release health check", description: "Reminds you to verify cycle state before release.", step: "release" },
107
+ { key: "gestaltPreBuild", label: "Gestalt pre-build check", description: "Reads the whole cycle before building the first task.", step: "build" },
108
+ { key: "batchBuildRollup", label: "Batch build rollup", description: "Emits a cross-task summary after a batch build.", step: "build" },
109
+ { key: "modelRecommendation", label: "Model recommendation", description: "Suggests a model tier for each task.", step: "build" },
110
+ { key: "discoveredIssues", label: "Discovered issues surfacing", description: "Surfaces issues logged during builds at release.", step: "release" },
111
+ { key: "publishDirective", label: "Publish & registry updates", description: "Announces the release and updates MCP registry listings.", step: "release" },
112
+ // task-2482 (C319): the release quality gate. When on AND a gate command is
113
+ // configured (PAPI_GATE), release makes the host LLM run that command and
114
+ // BLOCKS the tag/merge on failure (fail-closed, AD-58 — PAPI never runs it).
115
+ { key: "releaseGate", label: "Release quality gate", description: "Runs your test/build command before release and blocks on failure.", step: "release" },
116
+ // task-2491 (C320): post-release deploy hook — the deploy half of the ship→verify
117
+ // loop. UNLIKE every other capability this one defaults OFF (defaultEnabled:false):
118
+ // running a deploy command is destructive, so it is strictly opt-in. When ON AND
119
+ // papi.deploy (PAPI_DEPLOY) is set, release emits a directive telling the HOST to
120
+ // run the deploy command AFTER the merge/tag (AD-58 — PAPI never runs it) and
121
+ // records a deploy_hook progress step so the reactive hub shows the deploy step.
122
+ { key: "deployHook", label: "Post-release deploy", description: "Runs your deploy command after a release merges.", step: "release", defaultEnabled: false },
123
+ // task-2833 (C341): enforce the handoff's acceptanceCriteria[] at build-complete.
124
+ // When ON, a completed:"yes" build whose handoff lists acceptance criteria must
125
+ // pass acceptance_confirmed:true or build_execute returns the checklist and does
126
+ // NOT mark the task Done (non-destructive — the report is not discarded). Defaults
127
+ // OFF: it changes the completion contract, so it is strictly opt-in until a project
128
+ // chooses to hold builds to their own acceptance criteria.
129
+ { key: "acceptanceGate", label: "Acceptance-criteria gate", description: "Requires confirming the handoff acceptance criteria before a build completes.", step: "build", defaultEnabled: false },
130
+ // task-2325 (C344): configurable workflow guardrails — let a user opt out of the
131
+ // git/branch/commit/PR ceremony and PAPI-meta framing so PAPI adapts to their
132
+ // workflow instead of imposing one. Every toggle DEFAULTS ON (no defaultEnabled)
133
+ // so current behaviour is byte-identical until the user flips it off. The no-git
134
+ // fallback that these ride on top of is a separate task (task-2353).
135
+ { key: "autoBranch", label: "Auto branch", description: "Creates a feature branch per task/cycle at build start.", step: "build" },
136
+ { key: "autoCommit", label: "Auto commit", description: "Commits your work automatically when a build completes.", step: "build" },
137
+ { key: "autoPush", label: "Auto push & PR", description: "Pushes the branch and opens a pull request on build complete.", step: "build" },
138
+ { key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" }
139
+ ];
140
+ CAPABILITY_KEYS = CAPABILITY_REGISTRY.map((c) => c.key);
141
+ HOSTED_APP_URL = "https://getpapi.ai";
142
+ HOSTED_MCP_URL = "https://mcp.getpapi.ai";
143
+ HOSTED_SUPABASE_URL = "https://guewgygcpcmrcoppihzx.supabase.co";
144
+ WAB_WINDOW_DAYS = 7;
145
+ WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
146
+ EFFORT_SCALE = {
147
+ XS: 1,
148
+ S: 2,
149
+ M: 3,
150
+ L: 4,
151
+ XL: 5
152
+ };
153
+ }
154
+ });
155
+
11
156
  // src/lib/git.ts
12
157
  var git_exports = {};
13
158
  __export(git_exports, {
@@ -1230,14 +1375,12 @@ function reportTelemetryEmitFailure(reason, ctx) {
1230
1375
  function noteTelemetryEmitSuccess() {
1231
1376
  consecutiveTelemetryFailures = 0;
1232
1377
  }
1233
- var HOSTED_SUPABASE_URL, DEFAULT_TELEMETRY_ENDPOINT, MD_PINGS_SUPABASE_URL, consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
1378
+ var consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
1234
1379
  var init_telemetry = __esm({
1235
1380
  "src/lib/telemetry.ts"() {
1236
1381
  "use strict";
1237
1382
  init_install_id();
1238
- HOSTED_SUPABASE_URL = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
1239
- DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
1240
- MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL;
1383
+ init_dist();
1241
1384
  consecutiveTelemetryFailures = 0;
1242
1385
  TELEMETRY_BLACKOUT_THRESHOLD = 3;
1243
1386
  }
@@ -1493,6 +1636,13 @@ var init_proxy_adapter = __esm({
1493
1636
  "commitReviewSubmit",
1494
1637
  "commitRelease",
1495
1638
  // (2) not-yet-wired hosted gaps — shrink as data-proxy handlers land (task-2390).
1639
+ // These Active Decision transaction helpers exist only on the direct-pg adapter.
1640
+ // The strategy service probes for them structurally and deliberately falls back to
1641
+ // the hosted-safe read/upsert paths when they are absent. Exposing them through the
1642
+ // generic trap sends an unwired method to data-proxy and turns that fallback into a
1643
+ // 403, blocking strategy_change and strategy_review on the canonical remote MCP.
1644
+ "allocateActiveDecision",
1645
+ "applyActiveDecisionUpdates",
1496
1646
  // getToolCallCount + updatePhaseStatus are ABSENT: they now have edge handlers and
1497
1647
  // are served through the forwarder (getToolCallCount = SUP-2026-026 handler #1).
1498
1648
  "getContributorRole",
@@ -2366,6 +2516,7 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
2366
2516
  import { pathToFileURL } from "url";
2367
2517
 
2368
2518
  // src/adapter-factory.ts
2519
+ init_dist();
2369
2520
  import path2 from "path";
2370
2521
  import { execSync } from "child_process";
2371
2522
 
@@ -2450,8 +2601,18 @@ function detectUserId() {
2450
2601
  }
2451
2602
  return void 0;
2452
2603
  }
2453
- var HOSTED_SUPABASE_URL2 = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
2454
- var HOSTED_PROXY_ENDPOINT = `${HOSTED_SUPABASE_URL2}/functions/v1/data-proxy`;
2604
+ function serverDeploymentProfile() {
2605
+ return resolveDeploymentProfile({
2606
+ selfHosted: process.env["PAPI_SELF_HOST"],
2607
+ appUrl: process.env["PAPI_DASHBOARD_URL"],
2608
+ mcpUrl: process.env["NEXT_PUBLIC_MCP_URL"],
2609
+ dataEndpoint: process.env["PAPI_DATA_ENDPOINT"],
2610
+ hostedSupabaseUrl: process.env["PAPI_HOSTED_SUPABASE_URL"],
2611
+ vercelEnabled: process.env["PAPI_ENABLE_VERCEL"],
2612
+ railwayEnabled: process.env["PAPI_ENABLE_RAILWAY"],
2613
+ managedSupabaseEnabled: process.env["PAPI_ENABLE_MANAGED_SUPABASE"]
2614
+ });
2615
+ }
2455
2616
  var PLACEHOLDER_PATTERNS = [
2456
2617
  "<YOUR_DATABASE_URL>",
2457
2618
  "your-database-url",
@@ -2687,7 +2848,8 @@ Fix the PAPI_PROJECT_ID in your .mcp.json (or unset it and let PAPI resolve the
2687
2848
  }
2688
2849
  case "proxy": {
2689
2850
  const { ProxyPapiAdapter: ProxyPapiAdapter2, wrapWithForwarding: wrapWithForwarding2 } = await Promise.resolve().then(() => (init_proxy_adapter(), proxy_adapter_exports));
2690
- const dashboardUrl = process.env["PAPI_DASHBOARD_URL"] || "https://getpapi.ai";
2851
+ const deployment = serverDeploymentProfile();
2852
+ const dashboardUrl = deployment.appUrl;
2691
2853
  const projectId = process.env["PAPI_PROJECT_ID"];
2692
2854
  const dataApiKey = process.env["PAPI_DATA_API_KEY"];
2693
2855
  if (!dataApiKey) {
@@ -2695,14 +2857,19 @@ Fix the PAPI_PROJECT_ID in your .mcp.json (or unset it and let PAPI resolve the
2695
2857
  `PAPI needs an account to store your project data.
2696
2858
 
2697
2859
  Get started in 3 steps:
2698
- 1. Sign up at ${dashboardUrl}/login
2860
+ 1. Sign up at ${dashboardUrl ? `${dashboardUrl}/login` : "your configured PAPI dashboard"}
2699
2861
  2. Complete the onboarding wizard \u2014 it generates your .mcp.json config
2700
2862
  3. Download the config, place it in your project root, and restart Claude Code
2701
2863
 
2702
2864
  Already have an account? Make sure PAPI_DATA_API_KEY is set in your .mcp.json env config.`
2703
2865
  );
2704
2866
  }
2705
- const dataEndpoint = process.env["PAPI_DATA_ENDPOINT"] || HOSTED_PROXY_ENDPOINT;
2867
+ const dataEndpoint = deployment.dataEndpoint;
2868
+ if (!dataEndpoint) {
2869
+ throw new Error(
2870
+ "PAPI_DATA_ENDPOINT is required for the proxy adapter in a self-hosted deployment. Set it to the deployment-owned data-proxy URL; hosted production defaults are intentionally disabled."
2871
+ );
2872
+ }
2706
2873
  const adapter = new ProxyPapiAdapter2({
2707
2874
  endpoint: dataEndpoint,
2708
2875
  apiKey: dataApiKey,
@@ -2800,111 +2967,8 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
2800
2967
  }
2801
2968
  }
2802
2969
 
2803
- // ../shared/dist/index.js
2804
- var CAPABILITY_REGISTRY = [
2805
- { key: "prReviewer", label: "Auto code review", description: "Runs a code review of the branch diff before accepting.", step: "review" },
2806
- { key: "securityScan", label: "Security scan", description: "Flags a security pass on risk-tier changes at review time.", step: "review" },
2807
- { key: "changelog", label: "Changelog & cycle update", description: "Curates a cycle-update post when a release ships.", step: "release" },
2808
- { key: "verifyHealthCheck", label: "Release health check", description: "Reminds you to verify cycle state before release.", step: "release" },
2809
- { key: "gestaltPreBuild", label: "Gestalt pre-build check", description: "Reads the whole cycle before building the first task.", step: "build" },
2810
- { key: "batchBuildRollup", label: "Batch build rollup", description: "Emits a cross-task summary after a batch build.", step: "build" },
2811
- { key: "modelRecommendation", label: "Model recommendation", description: "Suggests a model tier for each task.", step: "build" },
2812
- { key: "discoveredIssues", label: "Discovered issues surfacing", description: "Surfaces issues logged during builds at release.", step: "release" },
2813
- { key: "publishDirective", label: "Publish & registry updates", description: "Announces the release and updates MCP registry listings.", step: "release" },
2814
- // task-2482 (C319): the release quality gate. When on AND a gate command is
2815
- // configured (PAPI_GATE), release makes the host LLM run that command and
2816
- // BLOCKS the tag/merge on failure (fail-closed, AD-58 — PAPI never runs it).
2817
- { key: "releaseGate", label: "Release quality gate", description: "Runs your test/build command before release and blocks on failure.", step: "release" },
2818
- // task-2491 (C320): post-release deploy hook — the deploy half of the ship→verify
2819
- // loop. UNLIKE every other capability this one defaults OFF (defaultEnabled:false):
2820
- // running a deploy command is destructive, so it is strictly opt-in. When ON AND
2821
- // papi.deploy (PAPI_DEPLOY) is set, release emits a directive telling the HOST to
2822
- // run the deploy command AFTER the merge/tag (AD-58 — PAPI never runs it) and
2823
- // records a deploy_hook progress step so the reactive hub shows the deploy step.
2824
- { key: "deployHook", label: "Post-release deploy", description: "Runs your deploy command after a release merges.", step: "release", defaultEnabled: false },
2825
- // task-2833 (C341): enforce the handoff's acceptanceCriteria[] at build-complete.
2826
- // When ON, a completed:"yes" build whose handoff lists acceptance criteria must
2827
- // pass acceptance_confirmed:true or build_execute returns the checklist and does
2828
- // NOT mark the task Done (non-destructive — the report is not discarded). Defaults
2829
- // OFF: it changes the completion contract, so it is strictly opt-in until a project
2830
- // chooses to hold builds to their own acceptance criteria.
2831
- { key: "acceptanceGate", label: "Acceptance-criteria gate", description: "Requires confirming the handoff acceptance criteria before a build completes.", step: "build", defaultEnabled: false },
2832
- // task-2325 (C344): configurable workflow guardrails — let a user opt out of the
2833
- // git/branch/commit/PR ceremony and PAPI-meta framing so PAPI adapts to their
2834
- // workflow instead of imposing one. Every toggle DEFAULTS ON (no defaultEnabled)
2835
- // so current behaviour is byte-identical until the user flips it off. The no-git
2836
- // fallback that these ride on top of is a separate task (task-2353).
2837
- { key: "autoBranch", label: "Auto branch", description: "Creates a feature branch per task/cycle at build start.", step: "build" },
2838
- { key: "autoCommit", label: "Auto commit", description: "Commits your work automatically when a build completes.", step: "build" },
2839
- { key: "autoPush", label: "Auto push & PR", description: "Pushes the branch and opens a pull request on build complete.", step: "build" },
2840
- { key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" }
2841
- ];
2842
- var CAPABILITY_KEYS = CAPABILITY_REGISTRY.map((c) => c.key);
2843
- var WAB_WINDOW_DAYS = 7;
2844
- var WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
2845
- var EFFORT_SCALE = {
2846
- XS: 1,
2847
- S: 2,
2848
- M: 3,
2849
- L: 4,
2850
- XL: 5
2851
- };
2852
- function effortOrdinal(effort) {
2853
- if (typeof effort !== "string") return void 0;
2854
- const normalized = effort.trim().toUpperCase();
2855
- return EFFORT_SCALE[normalized];
2856
- }
2857
- function isUnparsedEffort(effort) {
2858
- if (typeof effort !== "string" || effort.trim().length === 0) return false;
2859
- return effortOrdinal(effort) === void 0;
2860
- }
2861
- function calculateCycleMetrics(reports, currentCycle, window = 5) {
2862
- const recentReports = reports.filter(
2863
- (r) => r.cycle > currentCycle - window && r.cycle <= currentCycle
2864
- );
2865
- const unparsedEffortCount = recentReports.filter(
2866
- (r) => isUnparsedEffort(r.actualEffort) || isUnparsedEffort(r.estimatedEffort)
2867
- ).length;
2868
- const perCycle = /* @__PURE__ */ new Map();
2869
- for (const r of recentReports) {
2870
- const group = perCycle.get(r.cycle) ?? [];
2871
- group.push(r);
2872
- perCycle.set(r.cycle, group);
2873
- }
2874
- const accuracy = [];
2875
- const velocity = [];
2876
- const sortedCycles = [...perCycle.keys()].sort((a, b) => a - b);
2877
- for (const cycle of sortedCycles) {
2878
- const reps = perCycle.get(cycle);
2879
- const deltas = [];
2880
- for (const r of reps) {
2881
- const actual = effortOrdinal(r.actualEffort);
2882
- const estimated = effortOrdinal(r.estimatedEffort);
2883
- if (actual !== void 0 && estimated !== void 0) {
2884
- deltas.push(actual - estimated);
2885
- }
2886
- }
2887
- if (deltas.length > 0) {
2888
- accuracy.push({
2889
- cycle,
2890
- reports: deltas.length,
2891
- matchRate: Math.round(deltas.filter((d) => d === 0).length / deltas.length * 100),
2892
- mae: Math.round(deltas.reduce((s, d) => s + Math.abs(d), 0) / deltas.length * 10) / 10,
2893
- bias: Math.round(deltas.reduce((s, d) => s + d, 0) / deltas.length * 10) / 10
2894
- });
2895
- }
2896
- velocity.push({
2897
- cycle,
2898
- completed: reps.filter((r) => r.completed === "Yes").length,
2899
- partial: reps.filter((r) => r.completed === "Partial").length,
2900
- failed: reps.filter((r) => r.completed === "No").length,
2901
- effortPoints: reps.reduce((s, r) => s + (effortOrdinal(r.actualEffort) ?? 0), 0)
2902
- });
2903
- }
2904
- return { accuracy, velocity, unparsedEffortCount };
2905
- }
2906
-
2907
2970
  // src/lib/formatters.ts
2971
+ init_dist();
2908
2972
  var PLAN_FULL_NOTES_BUDGET_BYTES = Number(process.env.PAPI_PLAN_NOTES_BUDGET) || 6e4;
2909
2973
  function effortWeight(size) {
2910
2974
  switch ((size || "").toUpperCase()) {